LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

641. 设计循环双端队列

设计实现双端队列。 你的实现需要支持以下操作:

MyCircularDeque(k):构造函数,双端队列的大小为k。
insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty():检查双端队列是否为空。
isFull():检查双端队列是否满了。
示例:

MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1);			        // 返回 true
circularDeque.insertLast(2);			        // 返回 true
circularDeque.insertFront(3);			        // 返回 true
circularDeque.insertFront(4);			        // 已经满了,返回 false
circularDeque.getRear();  				// 返回 2
circularDeque.isFull();				        // 返回 true
circularDeque.deleteLast();			        // 返回 true
circularDeque.insertFront(4);			        // 返回 true
circularDeque.getFront();				// 返回 4
 
 

提示:

所有值的范围为 [1, 1000] 操作次数的范围为 [1, 1000] 请不要使用内置的双端队列库。

PS: 按照题目的提示一点一点的来

class MyCircularDeque {
private int[] elements;
private int size;

public MyCircularDeque(int k) {
    elements = new int[k];
}

public boolean insertFront(int value) {
    if(isFull()){
        return false;
    }
    for(int i=size-1; i>=0; i--){
        elements[i+1] = elements[i];
    }
    elements[0] = value;
    size++;
    return true;
}

public boolean insertLast(int value) {
    if(isFull()){
        return false;
    }
    elements[size] = value;
    size++;
    return true;
}

public boolean deleteFront() {
    if(isEmpty()){
        return false;
    }
    elements[0] = 0;
    for(int i=0; i<size-1; i++){
        elements[i]=elements[i+1];
    }
    size--;
    return true;
}

public boolean deleteLast() {
    if(isEmpty()){
        return false;
    }
    elements[size-1] = 0;
    size--;
    return true;
}

public int getFront() {
    return size == 0 ? -1 : elements[0];
}

public int getRear() {
    return size == 0 ? -1 : elements[size-1];
}

public boolean isEmpty() {
    return size == 0;
}

public boolean isFull() {
    return size == elements.length;
}
}

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque obj = new MyCircularDeque(k);
 * boolean param_1 = obj.insertFront(value);
 * boolean param_2 = obj.insertLast(value);
 * boolean param_3 = obj.deleteFront();
 * boolean param_4 = obj.deleteLast();
 * int param_5 = obj.getFront();
 * int param_6 = obj.getRear();
 * boolean param_7 = obj.isEmpty();
 * boolean param_8 = obj.isFull();
 */

643. 子数组最大平均数 I

给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数。

示例 1:

输入: [1,12,-5,-6,50,3], k = 4 输出: 12.75 解释: 最大平均数 (12-5-6+50)/4 = 51/4 = 12.75

注意:

1 <= k <= n <= 30,000。 所给数据范围 [-10,000,10,000]。

class Solution {
    public double findMaxAverage(int[] nums, int k) {
        if(nums.length == 1) return nums[0];

        int[] results = nums.clone();
        for(int i = 1; i < nums.length; i++){
            results[i] += results[i - 1];
        }
        int max = results[k - 1];
        for(int i = 1; i <= nums.length - k; i++){
            int value = results[i + k - 1] - results[i - 1];
            max = Integer.max(max, value);
        }
        return (double)max / k;
    }
}

646. 最长数对链

给出 n 个数对。 在每一个数对中,第一个数字总是比第二个数字小。

现在,我们定义一种跟随关系,当且仅当 b < c 时,数对(c, d) 才可以跟在 (a, b) 后面。我们用这种形式来构造一个数对链。

给定一个对数集合,找出能够形成的最长数对链的长度。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。

示例 :

输入: [[1,2], [2,3], [3,4]] 输出: 2 解释: 最长的数对链是 [1,2] -> [3,4] 注意:

给出数对的个数在 [1, 1000] 范围内。

class Solution {
     public int findLongestChain(int[][] pairs) {
        Arrays.sort(pairs,(a,b)-> a[1]-b[1]);
        int res = 1,tmp = pairs[0][1];
        for(int i = 1;i < pairs.length;i++){
            if(pairs[i][0] > tmp){
                 res++;
                 tmp = pairs[i][1];
            }
        }
        return res;
    }
}

647. 回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。

示例 1:

输入: "abc" 输出: 3 解释: 三个回文子串: "a", "b", "c". 示例 2:

输入: "aaa" 输出: 6 说明: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa". 注意:

输入的字符串长度不会超过1000。

class Solution {
     public int countSubstrings(String s) {
        char[] arr = s.toCharArray();
        int sum = 0;
        for (int c = 0; c < arr.length; c++) {
            sum += expend(arr, c, c) + expend(arr, c, c + 1);
        }
        return sum;
    }

    private int expend(char[] arr, int l, int r) {
        int cnt = 0;
        while (l >= 0 && r < arr.length && arr[l] == arr[r]) {
            l--;
            r++;
            cnt++;
        }
        return cnt;
    }
}

648. 单词替换

在英语中,我们有一个叫做 词根(root)的概念,它可以跟着其他一些词组成另一个较长的单词——我们称这个词为 继承词(successor)。例如,词根an,跟随着单词 other(其他),可以形成新的单词 another(另一个)。

现在,给定一个由许多词根组成的词典和一个句子。你需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。

你需要输出替换之后的句子。

示例 1:

输入:

dict(词典) = ["cat", "bat", "rat"]
sentence(句子) = "the cattle was rattled by the battery"
输出: "the cat was rat by the bat"

注:

输入只包含小写字母。 1 <= 字典单词数 <=1000 1 <= 句中词语数 <= 1000 1 <= 词根长度 <= 100 1 <= 句中词语长度 <= 1000

class Solution {
    public String replaceWords(List<String> dict, String sentence) {
        Trie trie = new Trie();
        for(String word : dict){
            trie.insert(word);
        }
        String[] words = sentence.split(" ");
        StringBuilder sb = new StringBuilder();
        for(String word : words){
            sb.append(trie.change(word)).append(" ");
        }
        return sb.toString().substring(0,sb.toString().length()-1);
    }
}

class Trie{
    TrieNode root;
    public Trie(){
        root = new TrieNode();
    }
    public void insert(String word){
        TrieNode node = root;
        for(char c : word.toCharArray()){
            if(node.link[c - 'a'] == null){
                node.link[c - 'a'] = new TrieNode();
            }
            node =  node.link[c - 'a'];
        }
        node.isEnd = true;
    }

       public String change(String word){
        StringBuilder sb = new StringBuilder();
        TrieNode node = root;
        boolean found = false;
        for(char c :word.toCharArray()){
            if(node.link[c - 'a'] == null){
                break;
            }
            else{
                sb.append(c);
                node = node.link[c - 'a'];
                if(node.isEnd){
                    return sb.toString();
                }
            }
        }
        return word;
    }
}

class TrieNode{
    TrieNode[] link;
    boolean isEnd;

    public TrieNode() {
        link = new TrieNode[26];
        isEnd = false;
    }
}

649. Dota2 参议院

Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇)

Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过程的投票进行。在每一轮中,每一位参议员都可以行使两项权利中的一项:

禁止一名参议员的权利:

参议员可以让另一位参议员在这一轮和随后的几轮中丧失所有的权利。

宣布胜利: 如果参议员发现有权利投票的参议员都是同一个阵营的,他可以宣布胜利并决定在游戏中的有关变化。

给定一个字符串代表每个参议员的阵营。字母 “R” 和 “D” 分别代表了 Radiant(天辉)和 Dire(夜魇)。然后,如果有 n 个参议员,给定字符串的大小将是 n。

以轮为基础的过程从给定顺序的第一个参议员开始到最后一个参议员结束。这一过程将持续到投票结束。所有失去权利的参议员将在过程中被跳过。

假设每一位参议员都足够聪明,会为自己的政党做出最好的策略,你需要预测哪一方最终会宣布胜利并在 Dota2 游戏中决定改变。输出应该是 Radiant 或 Dire。

示例 1:

输入: "RD" 输出: "Radiant" 解释: 第一个参议员来自 Radiant 阵营并且他可以使用第一项权利让第二个参议员失去权力,因此第二个参议员将被跳过因为他没有任何权利。然后在第二轮的时候,第一个参议员可以宣布胜利,因为他是唯一一个有投票权的人 示例 2:

输入: "RDD" 输出: "Dire" 解释: 第一轮中,第一个来自 Radiant 阵营的参议员可以使用第一项权利禁止第二个参议员的权利 第二个来自 Dire 阵营的参议员会被跳过因为他的权利被禁止 第三个来自 Dire 阵营的参议员可以使用他的第一项权利禁止第一个参议员的权利 因此在第二轮只剩下第三个参议员拥有投票的权利,于是他可以宣布胜利

注意:

给定字符串的长度在 [1, 10,000] 之间.

PS: 记录一下R得人数,然后,在进行删除操作(把原来的改成*)

class Solution {
         public String predictPartyVictory(String senate) {
		boolean scanR = true;
		boolean scanD = true;
		char[] cs = senate.toCharArray();
		int person = 0;
		while (scanR && scanD) {
			scanR = false;
			scanD = false;
			for (int j = 0; j < cs.length; j++) {
				if (cs[j] == 'R') {
					scanR = true;
					if (person < 0) {
						cs[j] = '*';
					}
					person += 1;
				}
				if (cs[j] == 'D') {
					scanD = true;
					if (person > 0) {
						cs[j] = '*';
					}
					person -= 1;
				}

			}
		}

		return person > 0 ? "Radiant" : "Dire";
	}
}

650. 只有两个键的键盘

最初在一个记事本上只有一个字符 'A'。你每次可以对这个记事本进行两种操作:

Copy All (复制全部) : 你可以复制这个记事本中的所有字符(部分的复制是不允许的)。 Paste (粘贴) : 你可以粘贴你上一次复制的字符。 给定一个数字 n 。你需要使用最少的操作次数,在记事本中打印出恰好 n 个 'A'。输出能够打印出 n 个 'A' 的最少操作次数。

示例 1:

输入: 3 输出: 3 解释: 最初, 我们只有一个字符 'A'。 第 1 步, 我们使用 Copy All 操作。 第 2 步, 我们使用 Paste 操作来获得 'AA'。 第 3 步, 我们使用 Paste 操作来获得 'AAA'。 说明:

n 的取值范围是 [1, 1000] 。

class Solution {
      public int minSteps(int n) {
        if(n == 1){
            return 0;
        }
        //加一个需要一步
        return process(n, 1, 1) + 1;
    }
    
    // 保持粘贴板有东西
    private int process(final int target, int nowLen, int copyLen){ 
        // 先粘贴
        nowLen += copyLen;
        if(target == nowLen){
            return 1;
        } 
        //如果能整除当前得,就是复制,因为先黏贴得一步,然后复制得一步
        return (target - nowLen) % nowLen == 0 ?  process(target, nowLen, nowLen) + 2 : process(target, nowLen, copyLen) + 1;//不行得话只有黏贴一步 
    }



        //加n>1时 其实就是将n分解为m个数字的乘积 且m个数字的和最小
        // 即把一个数分解为n个质数的和 从小到大的去试探
    // public int minSteps(int n) {
    //     int res = 0;
    //     for (int i = 2; i <= n; i++) { 
    //         while (n % i == 0) {
    //             res += i;
    //             n /= i;
    //         }
    //     }
    //     return res;
    // }
     
}