移动零

最佳解法:

public class Solution {

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

盛最多水的容器


思路:

  • 设置左右两个指针
  • 每次高度小的指针向中间靠拢
  • 如果面积大于了maxSize就更新最大面积
  • 直到两个指针相遇
  • 复杂度O(n)

最佳解法:

public class Solution {

    public int maxArea(int[] height) {

        int left = 0;
        int right = height.length - 1;
        int maxSize = 0;

        while (left < right) {
            int minheight = height[left] < height[right] ? height[left++] : height[right--];
            maxSize = Math.max(maxSize, minheight * (right - left + 1));
        }
        return maxSize;
    }
}

爬楼梯


思路:

  • 斐波拉契数列的变形式
  • 只要认出是斐波拉契数组就没什么问题了

解答一:带缓存的递归

public class Solution {

    static int[] cache;

    public int climbStairs(int n) {

        cache = new int[n + 1];
        return fib(n);

    }

    public int fib(int n) {

        if (n < 3) {
            if (cache[n] == 0) {
                cache[n] = n;
            }
            return cache[n];
        }
        if (cache[n] == 0) {
            cache[n] = fib(n - 1) + fib(n - 2);
        }
        return cache[n];
    }
    
}

解答二:迭代法

class Solution {
   public int climbStairs(int n) {
        if (n <= 2) {
            return n;
        }
        int f1 = 1, f2 = 2, f3 = 3;
        for (int i = 3; i <= n; i++) {
            f3 = f1 + f2;
            f1 = f2;
            f2 = f3;
        }
        return f3;
    }
}

两数之和


解答一:暴力

public class Solution {

    public int[] twoSum(int[] nums, int target) {
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[j] + nums[i] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }
}

解答二:两次哈希

public class Solution {

    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
        }

        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement) && map.get(complement) != i) {
                return new int[]{i, map.get(complement)};
            }
        }
        return new int[0];
    }
}

解答三:一次哈希

public class Solution {

    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (!map.containsKey(complement)) {
                map.put(nums[i], i);
            } else {
                return new int[]{map.get(complement), i};
            }

        }
        return new int[0];
    }
}

三数之和


解法一:双指针夹逼

public class Solution {

    public List<List<Integer>> threeSum(int[] nums) {

        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length < 3) return res;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) break;//后面的都没可能实现了
            if (i > 0 && nums[i] == nums[i - 1]) continue;//去重
            int left = i + 1;
            int right = nums.length - 1;
            while (left < right) {
                int count = nums[i] + nums[left] + nums[right];
                if (count == 0) {
                    res.add(Arrays.asList(nums[left], nums[i], nums[right]));
                    while (left < right && nums[left] == nums[left + 1]) left++;//去重
                    while (left < right && nums[right] == nums[right - 1]) right--;//去重
                    left++;
                    right--;
                } else if (count > 0) {
                    right--;
                } else {
                    left++;
                }
            }
        }
        return res;
    }
}

解法二:hash法

public class Solution {

    public List<List<Integer>> threeSum(int[] nums) {

        Set<List<Integer>> res = new LinkedHashSet<>();
        if (nums == null || nums.length < 3) return new ArrayList<>(res);
        Arrays.sort(nums);
        //for循环内部相当于执行n-1次的两数之和
        for (int i = 0; i < nums.length - 2; i++) {
            if (nums[i] > 0) break;//后面的都没有用了
            if (i > 0 && nums[i] == nums[i - 1]) continue;//去重
            int target = -nums[i];
            Set<Integer> set = new HashSet<>(nums.length - 1);
            for (int j = i + 1; j < nums.length; j++) {
                int complement = target - nums[j];

                if (!set.contains(complement)) {
                    set.add(nums[j]);
                } else {
                    List<Integer> list = Arrays.asList(nums[i], complement, nums[j]);
                    list.sort(Comparator.naturalOrder());//排序
                    res.add(list);
                }
            }
        }
        return new ArrayList<>(res);
    }
}

删除排序数组中的重复项


解法:双指针

public class Solution {
    public int removeDuplicates(int[] nums) {

        int index = 1;//待填入非重复数值下标
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] != nums[i - 1]) {//如果和前面一个不相等,就覆盖到待填入非重复数值下标
                nums[index++] = nums[i];
            }
        }
        return index;
    }
}

旋转数组

解法一:三次反转
如示例所示数据

原始数组                  : 1 2 3 4 5 6 7
反转所有数字后             : 7 6 5 4 3 2 1
反转前 k 个数字后          : 5 6 7 4 3 2 1
反转后 n-k 个数字后        : 5 6 7 1 2 3 4 --> 结果
class Solution {
    public void rotate(int[] nums, int k) {
        //取个模,避免移动范围超过数组长度
        k = k % nums.length;

        reverse(nums, 0, nums.length - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.length - 1);
        
    }

    private void reverse(int[] nums, int i, int j) {
        while (i < j) {
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
            j--;
            i++;
        }
    }
}

解法二:额外空间暂存法

public class Solution {
    public void rotate(int[] nums, int k) {
        //取个模,避免移动范围超过数组长度
        k = k % nums.length;

        int[] tmp = new int[k];
        int j = -1;
        //暂存后移超出的数
        for (int i = nums.length - k; i < nums.length; i++) {
            tmp[++j] = nums[i];
        }
        int m = nums.length - 1;
        //数据右移
        for (int i = nums.length - k - 1; i >= 0; i--) {
            nums[m--] = nums[i];
        }
        //暂存数据存入
        for (int i = 0; i < k; i++) {
            nums[i] = tmp[i];
        }
    }
}

合并两个有序数组


思路:从末尾开始填

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {

        int k = m-- + n-- - 1;
        while (m >= 0 && n >= 0) {
            nums1[k--] = nums1[m] > nums2[n] ? nums1[m--] : nums2[n--];
        }
        //nums1先完了
        while (n >= 0) {
            nums1[k--] = nums2[n--];
        }

    }
}

加一

重点是考虑进位问题,所以就让它如果是9就一次每位加一,如果不是9了没有进位就返回

注意所有位都进位的特殊情况

class Solution {
    public int[] plusOne(int[] digits) {

        for (int i = digits.length - 1; i >= 0; i--) {
            if (digits[i] != 9) {
                digits[i]++;
                return digits;
            }
            digits[i] = 0;
        }
        //如果能跳出循环,说明数字全部都是9了
        int tmp[] = new int[digits.length + 1];
        tmp[0] = 1;
        return tmp;
    }
}

重塑矩阵


要点:index / n 表示行号, index% n 表示列号

class Solution {
    public int[][] matrixReshape(int[][] nums, int r, int c) {
        int[][] res = new int[r][c];

        int m = nums.length;
        int n = nums[0].length;
        if(m*n != r*c){
            return nums;
        }
        int index = 0;
        for(int i = 0;i < r;i++){
            for(int j = 0; j< c;j++){
                res[i][j] = nums[index/n][index %n];
                index++;
            }
        }

        return res;
    }
}

最大连续一的个数

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int max = 0;
        int cur = 0;
        for(int num:nums){
            if(num == 1){
            	cur++;
            }else{
            	max = Math.max(max,cur);
            	cur = 0;
            }
        }
        return Math.max(max,cur);
    }
}

搜索二维矩阵II


超级经典一个题,记住从右上角开始搜索或者左下角开始搜索就好了

public class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {

        if (matrix.length == 0 || matrix[0].length == 0 || matrix == null) {
            return false;
        }
        int row = 0;
        int col = matrix[0].length - 1;
        while (row < matrix.length && col >= 0) {
            if (target == matrix[row][col]) return true;
            else if (target > matrix[row][col]) row++;
            else col--;
        }
        return false;
    }
}

有序矩阵中第K小的元素


解答一:
看到最大最小的几个或者第几个,一下子就想到了优先队列,Java的优先队列底层实现默认是小顶堆,正好直接使用

public class Solution {
    public int kthSmallest(int[][] matrix, int k) {

        if (matrix == null||matrix.length == 0||matrix[0].length == 0){
            return -1;
        }

        //构建一个小顶堆
        PriorityQueue<Integer> dump = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });

        int rowSize = matrix.length < k ? matrix.length : k;
        for (int i = 0; i < rowSize; i++) {
            for (int j = 0; j < rowSize; j++) {
                dump.offer(matrix[i][j]);
            }
        }

        while (k > 1) {
            dump.poll();
            k--;
        }
        return dump.poll();

    }
}

针对这个题小顶堆效率不是很高

解答二:针对值的二叉搜索

public class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        //[left,right)左闭右开的值范围型二分查找
        int left = matrix[0][0];
        int right = matrix[matrix.length - 1][matrix[0].length - 1] ;
        while (left < right) {
            int mid = left + (right - left) / 2;
            //记录有多少个小于mid
            int count = 0;
            int j = matrix[0].length - 1;
            for (int i = 0; i < matrix.length; i++) {
                //梯形遍历,找到每行最大的小于mid就知道每行个数了
                while (j >= 0 && mid < matrix[i][j]) j--;
                count += (j + 1);
            }
            if (count<k) left = mid+1; //说明mid小了,往上移动
            else right = mid;//包含了等于,所以不-1
        }
        return left;//退出条件就是相等
    }
}

寻找重复数

采用二分查找,将范围进行二分
如果小于mid的值的个数大于mid,说明在左边,否则在右边

 public class Solution {
    public int findDuplicate(int[] nums) {
        int len = nums.length;
        int left = 1;
        int right = len - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            int count = 0;
            for (int num : nums) {
                if (num <= mid) {
                    count++;
                }
            }

            //根据抽屉原理,小于mid的个数大于min,那么重复值一定在[left,mid]
            if (count > mid) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}