2018leetcode算法面试题汇总部分解答

3 篇文章 0 订阅
2 篇文章 0 订阅

开始之前

只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

解法:使用^异或位运算符!!!

class Solution {
    public int singleNumber(int[] nums) {
        int temp = 0;
        for(int i = 0;i < nums.length;i++){
            temp = temp ^ nums[i];
        }
        return temp;
    }
}

求众数

给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在众数。

示例 1:

输入: [3,2,3]
输出: 3

示例 2:

输入: [2,2,1,1,1,2,2]
输出: 2

解法:有分治的思想,充分利用众数出现次数大于n/2的条件。

class Solution {
    public int majorityElement(int[] nums) {
        int count = 1;
        int majorityNum = nums[0];
        for(int i = 1;i < nums.length;i++){
            if(nums[i] == majorityNum)
                count++;
            else{
                count--;
                if(count == 0)
                    majorityNum = nums[i + 1];
            }
        }
  return majorityNum;
    }
}

搜索二维矩阵2

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]

给定 target = 5,返回 true。

给定 target = 20,返回 false。

解法:利用矩阵特性,从矩阵的右上角开始比较,每一次比较消去一行或一列。

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        if(matrix == null || matrix.length == 0)
            return false;
        int m = matrix.length;
        int n = matrix[0].length;
        int row = 0, cow = n - 1;
        while(cow >= 0 && row < m) {
            if(matrix[row][cow] == target)
                return true;
            if(target < matrix[row][cow])
                cow--;
            else if(target > matrix[row][cow])
                row++;
        }
        return false;
    }
}

合并两个有序数组

给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。

说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3

输出: [1,2,2,3,5,6]

解法:思路和搜索二维矩阵相似,利用两个数组都已经排好序的特点。有一点要注意,从最大的开始比较,且从后往前依次填充数组,这样就避免了在原数组中与原数组元素产生冲突。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int index1 = m - 1;
        int index2 = n - 1;
        while(index1>= 0 && index2 >= 0){
            if(nums1[index1] > nums2[index2]){
                nums1[index1 + index2 + 1] = nums1[index1];
                index1--;
            }
            else{
                nums1[index1 + index2 + 1] = nums2[index2];
                index2--;
            }
        } 
        if(index1 < 0){
            for(int i = 0;i <= index2;i++){
                nums1[i] = nums2[i];
            }
        }
    }
}

鸡蛋掉落

你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。

每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。

你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。

每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。

你的目标是确切地知道 F 的值是多少。

无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?

示例 1:

输入:K = 1, N = 2
输出:2
解释:
鸡蛋从 1 楼掉落。如果它碎了,我们肯定知道 F = 0 。
否则,鸡蛋从 2 楼掉落。如果它碎了,我们肯定知道 F = 1 。
如果它没碎,那么我们肯定知道 F = 2 。
因此,在最坏的情况下我们需要移动 2 次以确定 F 是多少。

示例 2:

输入:K = 2, N = 6
输出:3

示例 3:

输入:K = 3, N = 14
输出:4

提示:
1 <= K <= 100
1 <= N <= 10000

解法:分析可知(1)dp[n][k]=min(max(dp[i − 1][k − 1],dp[n − i][k])+1)(1 <= i <= n),dp[n][1] = n,dp[n][0] = 0。

class Solution {
    public int superEggDrop(int K, int N) {
        if(K == 0)
            return 0;
        if(K == 1)
            return N;
        int dp[][] = new int[N + 1][K + 1];
        for(int i = 1;i <= N;i++)
            dp[i][1] = i;
        for(int i = 1;i <= N;i++){
            for(int j = 2;j <= K;j++){
                int temp = 0;
                //超时,这部分需要优化
                for(int m = 1;m <= i;m++){
                    int max = 0;
                    if(dp[m - 1][j - 1] > dp[i - m][j])
                        max = dp[m - 1][j - 1] + 1;
                    else
                        max = dp[i - m][j] + 1;
                    if(temp == 0)
                        temp = max;
                    else
                        temp = (max < temp) ? max : temp;
                }
                dp[i][j] = temp;
            }
        }
        return dp[N][K];
    }
}

(2)m表示移动次数,dp[m][k]表示k个鸡蛋,移动m步可以确定的层数,dp[m][k]=1 + dp[m - 1][k - 1] + dp[m][k - 1];

    public int superEggDrop(int K, int N) {
        int[] dp = new int[K+1];
        int step = 0;
        for (; dp[K] < N; step++) {
            for (int i = K; i > 0; i--)
                dp[i] += (1+ dp[i-1]);
        }
        return step;
    }
}

字符串

验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: “A man, a plan, a canal: Panama”
输出: true

示例 2:

输入: “race a car”
输出: false
解法:先将字符串中的大写字母转化成小写字母(或者相反),再从两头开始向中间搜索,遇到不是目标字符就跳过,一次匹配,两头各向中间推进一格,如果有不匹配的就说明不是回文字符串。

class Solution {
    public boolean isLetterOrNum(char a) {
  if(a - '0' < 0 || a - '9'> 0 && a  - 'A' < 0 || a - 'Z' > 0 && a - 'a' < 0 || a - 'z' > 0)
   return false;
  return true;
 }
 
 public boolean isPalindrome(String s) {
  char[] charS = s.toUpperCase().toCharArray();
        int i = 0;
        int j = charS.length - 1;
        while(i < j){
            while(!isLetterOrNum(charS[i])) {
             i++;
             if(i > j)
                    return true;
            }
                
            while(!isLetterOrNum(charS[j])) {
             j--;
            }
                
            if(!(charS[i] == charS[j])) {
             return false;
            }
            i++;
            j--;
        }
        return true;
    }
    
}

有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。

示例 1:

输入: s = “anagram”, t = “nagaram”
输出: true

示例 2:

输入: s = “rat”, t = “car”
输出: false

说明:

你可以假设字符串只包含小写字母。

进阶:

如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
解法:用一个数组存储26个字母出现次数,第一个字符串中出现的字母在数组对应位置计数加1,第二个字符串则相反,最后若数组元素全为零,则输出True,否则False。

class Solution {
    public boolean isAnagram(String s, String t) {
        char[] sc = s.toCharArray();
        char[] tc = t.toCharArray();
        int[] array = new int[26];
        for(char i : sc){
            array[i - 'a']++;
        }
        for(char i : tc){
            array[i - 'a']--;
        }
        for(int i = 0;i < 26;i++){
            if(array[i] != 0)
                return false;
        }
        return true;
    }
}

字符串中的第一个唯一字符

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

案例:

s = “leetcode”
返回 0.

s = “loveleetcode”,
返回 2.

注意事项:您可以假定该字符串只包含小写字母。

解法:使用一个lastIndexOf()函数,从字符串头开始遍历,再和该函数获得值比较,相同则输出下标。或者两次遍历,一次从头开始,一次从尾开始,找到目标字符就记录下标并跳出,若两次下标相同就输出下标。

class Solution {
 public int firstUniqChar(String s) {
        int res = -1;
        for (char ch = 'a'; ch <= 'z'; ch++) {
            int index = s.indexOf(ch);
            if (index != -1 && index == s.lastIndexOf(ch)) {
                res = (res == -1 || res > index) ? index : res;
            }
        }
        return res;
    }
}

反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
解法:遍历字符串,把前面的字符和后面的字符交换即可。

class Solution {
    public void reverseString(char[] s) {
        for(int i = 0;i < s.length / 2;i++){
            char temp = s[i];
            s[i] = s[s.length - i - 1];
            s[s.length - i - 1] = temp;
        }
    }
}

数组

乘积最大子序列

给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

解法:1.从数组中所有为0的元素将数组分开;2.将分开的数组依次连乘,若结果为正,就比较得到最大的连乘结果,若结果为负,得到第一个及最后一个负数出现的位置,比较从第一个负数后面开始的连乘结果和从开始到最后一个负数前面截止的连成结果;3.所有结果取最大值。

class Solution {
    public int maxProductMethod(int[] nums, int start, int end){
        if(start == end)
            return nums[start];
        if(start > end || start >= nums.length)
            return 0;
        int result = 1;
        for(int i = start;i <= end;i++){
            result *= nums[i];
        }
        if(result < 0){
            int first = start;
            int last = end;
            while(nums[first++] > 0){
            }
            while(nums[last--] > 0){
            }
            int result1 = maxProductMethod(nums,first,end);
            int result2 = maxProductMethod(nums,start,last);
            result = result1 > result2? result1: result2;
        }
        return result;
    }                                                                                    
    
    public int maxProduct(int[] nums) {                                                           
        int start = 0;
        int result = nums[0];                      
        int newResult;
        for(int i = 0;i < nums.length;i++){
            if(nums[i] == 0){
                if(result < 0){
                    result = 0;
                }
                newResult = maxProductMethod(nums,start,i - 1);
                result = newResult > result? newResult: result;
                start = i + 1;
            }
        }
        newResult = maxProductMethod(nums,start,nums.length - 1);
        result = newResult > result? newResult: result;
        return result;
    }
}

旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
• 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
• 要求使用空间复杂度为 O(1) 的原地算法。

解法:(1)写旋转一次的函数,该函数执行k次即可。

class Solution {
    public void rotateForOnce(int[] nums){
        int temp = nums[nums.length - 1];
        for(int i = nums.length - 2;i >= 0;i--){
            nums[i + 1] = nums[i];
        }
        nums[0] = temp;
    }
    
    
    public void rotate(int[] nums, int k) {
        while(k-->0){
            rotateForOnce(nums);
        }
    }
}

(2)递归,逐渐降低问题规模。如数组长L,向右移动K次,首先K为L的倍数时,相当于没有旋转,故取K = K % L,然后从A[L - K - 1]开始,与其后K位的元素交换,最后得到数组位前K位可能没在相应位置,但K位以后的元素一定已经到达相应位置。数组前K位的位置也是有规律可循的,如果L%K == 0,那么前K位也是在相应位置的,就已经完成了。如果L%K != 0,观察其变化规律,则需要把它们向右移动K - L%K 次,这样相当于把问题化简了。

class Solution {
    public void rotateMethod(int[] nums,int len,int k){
        if(k == 0)
            return;
        for(int i = len - k - 1;i >= 0;i--){
            int temp = nums[i + k];
            nums[i + k] = nums[i];
            nums[i] = temp;
        }
        if(len % k == 0)
        	rotateMethod(nums,k,len % k);
        else
        	rotateMethod(nums,k,k - len % k);
    }
    
    public void rotate(int[] nums, int k) {
        rotateMethod(nums,nums.length,k % nums.length);
    }
}

存在重复元素

给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true

解法:排序,比较相邻的元素是否相等。

class Solution {
    public boolean containsDuplicate(int[] nums) {
        if(nums.length < 2){
            return false;
        }
        Arrays.sort(nums);
        for(int i = 0; i < nums.length - 1; i++) {
            if(nums[i] == nums[i + 1]){
                return true;
            }
        }
        return false;
    }
}

移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

解法:(1)遍历一遍数组,记录0出现次数,每一次遇到非0的元素,就把它赋给前0出现次数个数的元素,并将该元素置为0(若0出现次数为0,则不重置)。

class Solution {
	public void moveZeroes(int[] nums) {
        int zeroNums = 0;
        for(int i = 0;i < nums.length;i++){
           if(nums[i] == 0){
                zeroNums++;
            }
            else{
                nums[i - zeroNums] = nums[i];
                if(zeroNums > 0){
                    nums[i] = 0;
                }
            }
        }
    }    
}

(2)遍历一遍,由题目要求,如果这个数是第i 个不为0的数,那就直接放在第i 位上。把所有不为0的数排完,剩下的都是0。

class Solution {
    public void moveZeroes(int[] nums) {
        int index = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                nums[index++] = nums[i];
            }
        }
        for (int i = index; i < nums.length; i++) {
                nums[i] = 0;
        }
    }    
}

两个数组的交集2

给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
说明:
• 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
• 我们可以不考虑输出结果的顺序。
进阶:
• 如果给定的数组已经排好序呢?你将如何优化你的算法?
• 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
• 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

解法:先排序,从两个数组的最小值开始比较。

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        ArrayList<Integer> arrayList = new ArrayList();
        int i = 0;
        int j = 0;
        while(i < nums1.length && j < nums2.length){
            if(nums1[i] == nums2[j]){
                arrayList.add(nums1[i]);
                i++;
                j++;
            }
            else if(nums1[i] < nums2[j]){
                i++;
            }
            else if(nums2[j] < nums1[i]){
                j++;
            }
        }
        
        int[] array = new int[arrayList.size()];
        for(int k = 0;k < array.length;k++){
            array[k] = arrayList.get(k);
        }
        
        return array;
    }
}

递增的三元子序列

给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。
数学表达式如下:
如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。

解法:首先找到一个长度为2的递增子序列,然后继续向后查找,若找到比min小的就把min换成较小的,若找到比min大但比medium小的就把medium换成这个,若找到比medium还大的就返回false。

class Solution {
    public boolean increasingTriplet(int[] nums) {
        int first = Integer.MAX_VALUE,
int second = Integer.MAX_VALUE;
        if(nums.length<3){
            return false;
        }
        for(int num: nums){
            if(first>num){
                first = num;
            }else if(first<num && second>num){
                second = num;
            }else if(num>second){
                return true;
            }
        }
        return false;
    }

}

除自身以外数组的乘积

给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
示例:
输入: [1,2,3,4]
输出: [24,12,8,6]
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

解法:output中第k位的值可以看作前k – 1位的乘积,再乘上k + 1位后的乘积。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int[] output = new int[nums.length];
        int p = 1;
        for(int i = 0;i < nums.length;i++){
            output[i] = p;
            p *= nums[i];
        }
        p = 1;
        for(int i = nums.length - 1;i >= 0;i--){
            output[i] *= p;
            p *= nums[i];
        }
        return output;
    }
}

堆、栈与队列

数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

解法:(1)先排序,再找对应下标,Arrays.sort()默认升序排序。

class Solution {
    public int findKthLargest(int[] nums, int k) {
        Arrays.sort(nums);
        return nums[nums.length - k];
    }
}

(2)使用大根堆,堆的大小为k。

有序矩阵中第K小的元素

给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,

返回 13。
说明:
你可以假设 k 的值永远是有效的, 1 ≤ k ≤ n2 。

解法:(1)将可能的值置入数组中,将数组排序。

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        ArrayList<Integer> arrayList = new ArrayList();
        for(int i = 0;i < matrix.length && i < k;i++){
            for(int j = 0;j < matrix[0].length && j < k;j++){
                if(k < i * j + i + j){
                    break;
                }
                arrayList.add(matrix[i][j]);
            }
        }
        Collections.sort(arrayList);
        return arrayList.get(k - 1);
    }
}

(2)二分法,依次缩小范围查找。

class Solution {
    public static int guess(int[][] matrix, int g, int n, int k){
        int sum = 0;//记录所有小于g的个数
        for(int i = 0; i < n; i++){
            int L = 0;
            int R = n - 1;
            int ans = 0;//记录一行中小于g的个数
            while (L <= R){
                int mid = L + (R - L)/2;
                //若某一行值小于g,则应该是最后一个元素的下标 + 1.
                if(matrix[i][mid] < g){
                    ans = mid + 1;
                    L = mid + 1;
                }else {
                    R = mid - 1;
                }
            }
            sum += ans;
        }
        return k - sum;
    }

    public static int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length;
        int L = matrix[0][0];
        int R = matrix[n - 1][n - 1];
        int ans = 0;
        while (L <= R){
            int mid = L + (R - L )/2;
            if(guess(matrix, mid, n, k) > 0){
                ans = mid;
                L = mid + 1;
            }
            else if(guess(matrix, mid, n, k) <= 0){
                R = mid - 1;
            }
        }
        return  ans;
    }
}

前K个高频元素

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
说明:
• 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
• 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。

解法:用一个map存储数组中每个数字出现的次数,再创建一个数组,根据记录次数存储数字,再从次数大的方向开始遍历,存入ArrayList中。

class Solution {
    public List<Integer> topKFrequent(int[] nums, int k) {      
	//定义统计数组里每个数字出现的次数HashMap;
	HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
    for(int i = 0; i< nums.length; i++){
        Integer count = map.get(nums[i]);
    	if(count == null){
    		count = 0;
    	}
    	map.put(nums[i],count + 1);
    }
    //构造一个数组来放map中的key;
    List<Integer>[] keyList = new List[nums.length + 1];
    for(int key: map.keySet()){  
        //map中数出现的次数;
        int a = map.get(key);
        //将map中的key放在arrayList的里
        if(keyList[a] == null){
            ArrayList<Integer> temp = new ArrayList<Integer>();  
            temp.add(key);  
            keyList[a] = temp;  
        }
        else{  
            keyList[a].add(key);  
        } 
    }
    ArrayList<Integer> res = new ArrayList<Integer>();  
    for(int i = keyList.length - 1;i >= 0 && res.size() < k;i--){  
        if(keyList[i] != null){  
            res.addAll(keyList[i]);  
        }  
    }
    return res;  
    }
}

滑动窗口最大值

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。
返回滑动窗口最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
注意:
你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。
进阶:
你能在线性时间复杂度内解决此题吗?

解法:记录窗口中最大值及其下标,以及第二大值及其下标。遍历数组并按照如下规律更新:
1.移出的不是最大值,比较新移入的值,若比最大值大,更新最大值,并将第二大值重置(保证第二大值永远在最大值后面),若不比最大值大,比第二大值大,更新第二大值,其余不变。
2.移出的是最大值,将第二大值及其下标赋给最大值及其下标,重置第二大值,并从现在最大值下标开始至现在遍历下标,确定第二大值及其下标。

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0)
            return nums;
        int[] result = new int[nums.length - k + 1];
        int maxIndex = nums.length;
        int secondMaxIndex = nums.length;
        int max = Integer.MIN_VALUE;
        int secondMax = Integer.MIN_VALUE;
        for(int i = 0;i < nums.length;i++){
            if(maxIndex == i - k){
                max = secondMax;
                maxIndex = secondMaxIndex;
                secondMax = Integer.MIN_VALUE;
                for(int j = maxIndex + 1;j < i;j++){
                    if(nums[j] > secondMax){
                    secondMax = nums[j];
                    secondMaxIndex = j;
                    }
                }
            }
            if(nums[i] > max){
                    max = nums[i];
                    maxIndex = i;
                    secondMax = Integer.MIN_VALUE;
            }
            else if(nums[i] > secondMax){
                secondMax = nums[i];
                secondMaxIndex = i;
            }
            if(i + 1 >= k){
                result[i - k + 1] = max;
            }
        }
        return result;
    }
}

基本计算器2

实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,,/ 四种运算符和空格 。 整数除法仅保留整数部分。
示例 1:
输入: "3+2
2"
输出: 7
示例 2:
输入: " 3/2 "
输出: 1
示例 3:
输入: " 3+5 / 2 "
输出: 5
说明:
• 你可以假设所给定的表达式都是有效的。
• 请不要使用内置的库函数 eval。

解法:(1)使用栈。

class Solution {
    public int calculate(String s) {
        int res = 0, num = 0, n = s.length();
        char op = '+';
        Stack<Integer> st = new Stack<Integer>();
        for (int i = 0; i < n; ++i) {
            char c = s.charAt(i);
            if (c >= '0') {
                num = num * 10 + c - '0';
            }
            if ((c < '0' && c != ' ') || i == n - 1) {
                if (op == '+') st.push(num);
                if (op == '-') st.push(-num);
                if (op == '*' || op == '/') {
                    int tmp = (op == '*') ? st.pop() * num : st.pop() / num;
                    st.push(tmp);
                }
                op = c;
                num = 0;
            } 
        }
        while (!st.empty()) {
            res += st.pop();
        }
        return res;
    }
}

(2)不使用栈。

class Solution {
    public int calculate(String s) {
        int res = 0, curRes = 0, num = 0, n = s.length();
        char op = '+';
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (c >= '0' && c <= '9') {
                num = num * 10 + c - '0';
            }
			// 执行上一次操作符运算,curRes保存临时结果。
            if (c == '+' || c == '-' || c == '*' || c == '/' || i == n - 1) {
                switch (op) {
                    case '+': curRes += num; break;
                    case '-': curRes -= num; break;
                    case '*': curRes *= num; break;
                    case '/': curRes /= num; break;
                }
                if (c == '+' || c == '-' || i == n - 1) {
                    res += curRes;
                    curRes = 0;
                }
                op = c;
                num = 0;
            } 
        }
        return res;
    }
}

逆波兰表达式求值

逆波兰表达式求值
根据逆波兰表示法,求表达式的值。
有效的运算符包括 +, -, , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
• 整数除法只保留整数部分。
• 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入: [“2”, “1”, “+”, “3”, "
"]
输出: 9
解释: ((2 + 1) * 3) = 9
示例 2:
输入: [“4”, “13”, “5”, “/”, “+”]
输出: 6
解释: (4 + (13 / 5)) = 6
示例 3:
输入: [“10”, “6”, “9”, “3”, “+”, “-11”, “", “/”, "”, “17”, “+”, “5”, “+”]
输出: 22
解释:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

解法:逆波兰表达式就不需要考虑优先级了,使用栈来存储。

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<String> stack = new Stack();
        int a,b;
        for(int i = 0;i < tokens.length;i++){
            switch(tokens[i]){
                case "+" :
                    a = Integer.parseInt(stack.pop());
                    b = Integer.parseInt(stack.pop());
                    stack.push(Integer.toString(b + a));
                    break;
                case "-" :
                    a = Integer.parseInt(stack.pop());
                    b = Integer.parseInt(stack.pop());
                    stack.push(Integer.toString(b - a));
                    break;
                case "*" :
                    a = Integer.parseInt(stack.pop());
                    b = Integer.parseInt(stack.pop());
                    stack.push(Integer.toString(b * a));
                    break;
                case "/" :
                    a = Integer.parseInt(stack.pop());
                    b = Integer.parseInt(stack.pop());
                    stack.push(Integer.toString(b / a));
                    break;
                default:
                    stack.push(tokens[i]);
            }
        }
        return Integer.parseInt(stack.pop());
    }
}

链表

复制带随机指针的链表

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的深拷贝。

示例:

输入:
{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …":"1","next":{"id”:“2”,“next”:null,“random”:{“KaTeX parse error: Expected 'EOF', got '}' at position 9: ref":"2"}̲,"val":2},"rand…ref”:“2”},“val”:1}

解释:
节点 1 的值是 1,它的下一个指针和随机指针都指向节点 2 。
节点 2 的值是 2,它的下一个指针指向 null,随机指针指向它自己。

提示:
你必须返回给定头的拷贝作为对克隆列表的引用。
解法:按步进行复制。1.在每个结点后面复制一个结点,并将其插入到现有结点中间(每个原来结点的后面都跟一个新的结点);2.把原来结点的random指针复制到其后的新结点中;3.把链表拆分。

/**
 * Definition for a Node.
 * class Node {
 *     public int val;
 *     public Node next;
 *     public Node random;
 *     public Node() {}
 *     public Node(int _val,Node _next,Node _random){
 *                 val = _val;
 *     		next = _next;
 * 		random = _random;
 *     }
 * };
 */
public class Solution {
    public Node copyRandomList(Node head) {   
        if (head == null) {
            return null;
        }
        Node p = head;
        //第一步,将原节点的next指向对应的新节点
        //新节点的next指向原节点的原next
        while (p != null) {
            Node newP = new Node();
            newP.val = p.val;
            newP.next = p.next;
            p.next = newP;
            p = newP.next;
        }
        //第二步,参照原节点的random指向,改变新节点的random指向
        p = head;
        while (p != null) {
            //p.next 就是当前节点对应的新节点
            p.next.random = p.random == null ? null : p.random.next;
            p = p.next.next;
        }
        //第三步,拆分链表
        Node newHead = head.next;
        p = head;
        Node q = newHead;
        while (p != null) {
            p.next = q.next;
            if (q.next != null) {
                q.next = q.next.next;
            }
            p = p.next;
            q = q.next;
        }
        return newHead;
    }
}

环形链表

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?
解法:用两个指针,一个一次走一步,一个一次走两步,若有环,则这两指针一定会相遇。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null)
            return false;
        ListNode fast = head;
        ListNode slow = head;
        while(true){
            if(fast.next != null && fast.next.next != null){
                fast = fast.next.next;
                slow = slow.next;
                if(fast.val == slow.val)
                return true;
            }
            else{
                break;
            }
        }
        return false;
    }
}

排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4

示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5
解法:利用并归排序。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public static ListNode sortList(ListNode head) {
        if(head == null) {
            return null;
        }
       return mergeSort(head);
    }

    private static ListNode mergeSort(ListNode head) {
        if (head.next == null) {
            return head;
        }
        //快慢指针
        ListNode slow = head, quick = head, pre = null;
        //快2步,慢一步
        while (quick != null && quick.next != null) {
            pre = slow;
            slow = slow.next;
            quick = quick.next.next;
        }
        pre.next = null;
        ListNode l = mergeSort(head);
        ListNode r = mergeSort(slow);
        return merge(l, r);
    }

    private static ListNode merge(ListNode l, ListNode r) {
        ListNode dummyHead = new ListNode(0);
        ListNode cur = dummyHead;
        while (l!=null && r!=null) {
            if (l.val < r.val) {
                cur.next = l;
                cur = cur.next;
                l = l.next;
            } else {
                cur.next = r;
                cur = cur.next;
                r = r.next;
            }
        }
        if (l != null) {
            cur.next = l;
        }
        if (r != null) {
            cur.next = r;
        }
        return dummyHead.next;
    }
}

反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:

你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
解法:从头到尾一一反转。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null)
            return head;
        if(head.next.next == null){
            ListNode newHead = head.next;
            head.next.next = head;
            head.next = null;
            return newHead;
        }
        ListNode currentNode = head;
        ListNode nextNode = currentNode.next;
        ListNode nnextNode;
        currentNode.next = null;
        while(nextNode != null){
            nnextNode = nextNode.next;
            nextNode.next = currentNode;
            currentNode = nextNode;
            nextNode = nnextNode;
        }
        return currentNode;
    }
}

哈希与映射

Excel表列序号

给定一个Excel表格中的列名称,返回其相应的列序号。
例如,
A -> 1
B -> 2
C -> 3

Z -> 26
AA -> 27
AB -> 28

示例 1:
输入: “A”
输出: 1
示例 2:
输入: “AB”
输出: 28
示例 3:
输入: “ZY”
输出: 701
致谢:
特别感谢 @ts 添加此问题并创建所有测试用例。

解法:就像是26进制一样。

class Solution {
    public int titleToNumber(String s) {
        int count = 0;
        int start = 64;
        int k = s.length();
        char[] chars = s.toCharArray();
        for(int i = 0;i < k;i++){
            count = count * 26;
            count = count + chars[i]- start;
        }
        return count;
    }
}

四数相加2

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。
例如:
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]

输出:
2

解释:
两个元组如下:

  1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

解法:暴力求解时间复杂度太高,就想办法降维,两两组合,再找之和为0的组合数。使用map存储,记录key为和的值,value为出现次数。

class Solution {
    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
        HashMap<Integer,Integer> map = new HashMap<>();
            int count = 0;
            for(int i=0;i<A.length;i++){
                for(int j=0;j<B.length;j++){
                    map.put(A[i]+B[j],map.getOrDefault(A[i]+B[j],0)+1);
                }
            }
            for(int i=0;i<C.length;i++){
                for(int j=0;j<D.length;j++){
					//相同的多次相加相当于相乘
                    count+= map.getOrDefault(-C[i]-D[j],0);
                }
            }
            return count;
    }
}

二叉搜索树中第K小的元素

给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明:
你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/
1 4

2
输出: 1
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/
3 6
/
2 4
/
1
输出: 3
进阶:
如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化 kthSmallest 函数?

解法:用分治的思想,先确定root是第i小的数,然后比较i与k,若i<k则从右子树找第k-i小的值;若i>k则从左子数找第k小的值。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int kthSmallest(TreeNode root, int k) {
        int a = 1;
        if(root.left != null){
            a += childrenNodeNum(root.left);
        }
        if(a == k)
            return root.val;
        else if(a < k){
            return kthSmallest(root.right,k - a);
        }
        else{
            return kthSmallest(root.left,k);
        }  
    }
    
    public int childrenNodeNum(TreeNode root){
        if(root.left == null && root.right == null)
            return 1;
        else if(root.left == null)
            return childrenNodeNum(root.right) + 1;
        else if(root.right == null)
            return childrenNodeNum(root.left) + 1;
        else
            return childrenNodeNum(root.left) + childrenNodeNum(root.right) + 1;
    }
}

排序与检索

最大数

给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。

示例 1:

输入: [10,2]
输出: 210

示例 2:

输入: [3,30,34,5,9]
输出: 9534330

说明: 输出结果可能非常大,所以你需要返回一个字符串而不是整数。
解法:将数组转化为字符串数组,然后按照降序排序,再将字符串数组构建在一起就可得到最大的整数。

class Solution{
    public String largestNumber(int[] nums) {        
        String[] strArr = new String[nums.length];        
        for (int i = 0; i < nums.length; i++) {            
            strArr[i] = String.valueOf(nums[i]);        
        }
        //降序排序
        Arrays.sort(strArr, new Comparator<String>() {                        
            public int compare(String o1, String o2) {                
                return (o2 + o1).compareTo(o1 + o2);            
            }        
        });
        if (strArr[0].equals("0")) {            
            return "0";        
        } 
        String str = "";              
       
        for (String temp : strArr) {            
            str += temp;     
        }        
        return str;   
    }
}

动态规划

打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例 1:

输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
解法:dp[n] = max{dp[n - 2] + a[n],dp[n - 1]},dp[0]= 0,dp[1] = a[1],dp[2] = max{a[1],a[2]}。


class Solution {
    public int rob(int[] nums) {
        int[] dp = new int[nums.length + 1];
        
        if(nums.length == 0)
            return 0;
        if(nums.length == 1)
            return nums[0];
        if(nums.length == 2)
            return Math.max(nums[1],nums[0]);
        
        dp[0] = 0;
        dp[1] = nums[0];
        dp[2] = Math.max(nums[1],nums[0]);
        for(int i = 3;i < dp.length;i++){
            dp[i] = Math.max(nums[i - 1] + dp[i - 2],dp[i - 1]);
        }
        return dp[nums.length];
        
    }
}

完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例 1:

输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.

示例 2:

输入: n = 13
输出: 2
解释: 13 = 4 + 9.

解法:动态规划,dp[n] = min{dp[n],dp[n - i * i] + 1},dp[0]
= 0,dp[1] = 1。

class Solution {
    
    public int min(int a,int b){
        return a > b? b: a;
    }
    
    public int numSquares(int n) {
        int[] nums = new int[n + 1];
        nums[1] = 1;
        for(int i = 2;i <= n ;i++){
            nums[i] = Integer.MAX_VALUE;
            for(int j = 1;j < i && j * j <= i;j++){
                nums[i] = min(nums[i], nums[i - j * j] + 1);
            }
        }
        return nums[n];
    }
}

零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1

示例 2:

输入: coins = [2], amount = 3
输出: -1

说明:

你可以认为每种硬币的数量是无限的。
解法:dp[n] = min{dp[n],[n - coins[i] + 1},dp[0] = 0,dp[coin[i]] = 1。

class Solution {  
    public int coinChange(int[] coins, int amount) {
        Arrays.sort(coins);
        int[] arrays = new int[amount + 1];
        arrays[0] = 0;
        for(int i = 1;i <= amount;i++){
            arrays[i] = -1;
        }
        for(int i = 0;i < coins.length && coins[i] <= amount;i++){
            arrays[coins[i]] = 1;
        }
        for(int i = coins[0];i <= amount;i++){
            for(int j = 0;j < coins.length && coins[j] <= i;j++){
                int temp = arrays[i - coins[j]];
                if(temp > 0){
                    if(arrays[i] == -1)
                        arrays[i] = temp + 1;
                    else
                        arrays[i] = Math.min(arrays[i],temp + 1);
                    if(arrays[i] <= 0)
                        arrays[i] = -1;
                }
            }
        }
        return arrays[amount];
    }
}

更好的代码。

class Solution {
    public int coinChange(int[] coins, int amount) {
        if(amount == 0) return 0;
        int[] dp = new int[amount+1];
        //用amount + 1填充dp中的元素
        Arrays.fill(dp, amount+1);
        dp[0] = 0;
        for(int i = 1; i<=amount; i++)
            for(int j=0; j<coins.length; j++) {
                if(i - coins[j] >= 0) {
                    dp[i] = Math.min(dp[i], dp[i-coins[j]] + 1);
                }
            }
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

未完待续…

数学与位运算

直线上最多的点数

给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
示例 1:
输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
| o
| o
| o
±------------>
0 1 2 3 4
示例 2:
输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
| o
| o o
| o
| o o
±------------------>
0 1 2 3 4 5 6

解答:两次for循环,计算斜率,注意斜率不存在,用map存储,注意0与-0的比较。

/**
 * Definition for a point.
 * class Point {
 *     int x;
 *     int y;
 *     Point() { x = 0; y = 0; }
 *     Point(int a, int b) { x = a; y = b; }
 * }
 */
class Solution {
    public int maxPoints(Point[] points) {
        if(points.length <= 2)
            return points.length;
        int max = 2;
        double temp = 0;
        int same;
        for(int i = 0;i < points.length;i++){
            Map<Double,Integer> map = new HashMap<Double,Integer>();
            same = 1;
            for(int j = i + 1;j < points.length;j++){
                double a = points[i].x - points[j].x;
                double b = points[i].y - points[j].y;
                if(a == 0 && b == 0){
                    same++;
                }
                else{
                    if(b == 0){
                        temp = Double.MAX_VALUE;
                    }  
                    else if(a == 0){
                        temp = 0;
                    }
                    else{
                        temp = a / b;
                    }
                    map.put(temp,map.getOrDefault(temp,0) + 1);
                } 
            }
            int newMax = 0;
            for(int times : map.values()){
                if(times > newMax)
                    newMax = times;
            }
            newMax += same; 
            max = max >= newMax? max: newMax;
        }
        return max;
    }
}

阶乘后的零

给定一个整数 n,返回 n! 结果尾数中零的数量。
示例 1:
输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。
示例 2:
输入: 5
输出: 1
解释: 5! = 120, 尾数中有 1 个零.
说明: 你算法的时间复杂度应为 O(log n) 。

解法:找规律。。。

class Solution {
    public int trailingZeroes(int n) {
        if(n < 5)
            return 0;
        int k = n / 5;
        return k + trailingZeroes(k);
    }
}

计数质数

统计所有小于非负整数 n 的质数的数量。
示例:
输入: 10
输出: 4
解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。

解法:从第一个质数2开始遍历,直到n。用一个boolean数组标记数i是否为质数,若是,则将i到n之间的所有i的倍数都标记为非质数,这样标记只要到n平方根就可以停止,剩下部分继续统计质数的个数。

class Solution {
    public int countPrimes(int n) {
        boolean[] notprimes = new boolean[n + 1];
        int count = 0;
        for(int i = 2;i < n;i++){
            if(!notprimes[i]){
                if(i < Math.sqrt(n)){
                    for(int j = i + i;j < n;j += i){
                        notprimes[j] = true;
                    }
                }
                count++;
            }
        }
        return count;
    }
}

缺失数字

给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
示例 1:
输入: [3,0,1]
输出: 2
示例 2:
输入: [9,6,4,2,3,5,7,0,1]
输出: 8
说明:
你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现?

解法:用包括缺失的元素的和减去数组中的元素之和,得到的就是缺失元素。

class Solution {
    public int missingNumber(int[] nums) {
        int currentSum = 0;
        for(int i : nums){
            currentSum += i;
        }
        return nums.length * (nums.length + 1) / 2 - currentSum;
    }
}

3的幂

给定一个整数,写一个函数来判断它是否是 3 的幂次方。
示例 1:
输入: 27
输出: true
示例 2:
输入: 0
输出: false
示例 3:
输入: 9
输出: true
示例 4:
输入: 45
输出: false
进阶:
你能不使用循环或者递归来完成本题吗?

解法:简单,n一定是正整数且能被最大的3幂数整除。

class Solution {
    public boolean isPowerOfThree(int n) {
        return n > 0 && Math.pow(3,19) % n == 0;
    }
}

摆动排序2

给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]… 的顺序。

示例 1:

输入: nums = [1, 5, 1, 1, 6, 4]
输出: 一个可能的答案是 [1, 4, 1, 5, 1, 6]

示例 2:

输入: nums = [1, 3, 2, 2, 3, 1]
输出: 一个可能的答案是 [2, 3, 1, 3, 1, 2]

说明:

你可以假设所有输入都会得到有效的结果。

进阶:

你能用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现吗?
解法:先排序,再根据排序结果将数组分为两大小部分,将较小部分从小到大(或相反,那下面也要对应相反)插入到0,2,4…等位置,将较大部分从小到大插入到1,3,5…等位置(这样做是为了保证nums[2 * i] < nums[2 * i + 1]一定成立)。

class Solution {
    public void wiggleSort(int[] nums) {
        int n = nums.length;
        if(n == 0) return ;
        int[] a = Arrays.copyOfRange(nums,0,n);
        Arrays.sort(a);
        int k = 0 , p = (n-1)/2, q = n-1;
        while(k < n){
            if(k % 2 == 0) nums[k++]=a[p--];
            else nums[k++]=a[q--]

        }
    }
}
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值