LeetCode刷题:数组


引申:也包含部分String的题目。String可以转化为Character[]数组进行处理。

二分法:有序数组

引导

知识点:

  1. 时间复杂度:O(logn)。空间复杂度:O(1)
  2. 只要看到给出的数组是有序数组,都可以想一想是否可以使用二分法。

写法注意:
边界条件处理:到底是 while(left < right) 还是 while(left <= right),到底是right = middle呢,还是要right = middle - 1呢?要在二分查找的过程中,保持不变量。

左闭右闭写法,target 在左区间,[left, middle - 1];在右区间,[middle + 1, right]

	int left = 0;
    int right = n - 1; // 定义target在左闭右闭的区间里,[left, right] 
    while (left <= right) {
    
     // 当left==right,区间[left, right]依然有效
        int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
        if (nums[middle] > target) {
    
    
             right = middle - 1; // target 在左区间,所以[left, middle - 1]
        } else if (nums[middle] < target) {
    
    
             left = middle + 1; // target 在右区间,所以[middle + 1, right]
        } else {
    
     // nums[middle] == target
             return middle;
        }
    }

左闭右开[)写法(推荐)

	while (left < right) {
    
     // 因为left == right的时候,在[left, right)是无效的空间
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
    
    
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
    
    
                left = middle + 1; // target 在右区间,在 [middle+1, right)中
            } else {
    
     // nums[middle] == target
                return middle; // 数组中找到目标值的情况,直接返回下标
            }
     }

例题1

https://leetcode-cn.com/problems/search-insert-position/solution/sou-suo-cha-ru-wei-zhi-by-leetcode-solution/

class Solution {
    
    
    public int searchInsert(int[] nums, int target) {
    
    
        int n = nums.length;
        int left = 0, right = n - 1, ans = n;
        while (left <= right) {
    
    
            // 优化写法,位运算。数学意义:右移一位相当于除2,右移n位相当于除以2的n次方。
            // 同时避免(left + right)/2,防止溢出
            int mid = ((right - left) >> 1) + left; 
            if (target <= nums[mid]) {
    
    
                ans = mid;
                right = mid - 1;
            } else {
    
    // > nums[mid],取mid+1
                left = mid + 1;
            }
        }
        return ans;
    }
}

双指针

引导

知识点:

  1. 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。在数组和链表的操作中是非常常见的。
    双指针的例题,都是通过快慢指针进行操作,然后命中。最多就是命中条件不同,而有了不同的题型。

例题1

https://leetcode-cn.com/problems/remove-element/solution/yi-chu-yuan-su-by-leetcode/

扫描二维码关注公众号,回复: 12909151 查看本文章
public int removeElement(int[] nums, int val) {
    
    
    int i = 0;
    for (int j = 0; j < nums.length; j++) {
    
    
        if (nums[j] != val) {
    
    
            nums[i] = nums[j];
            i++;
        }
    }
    return i;
}

例题2

https://leetcode-cn.com/problems/3sum/solution/san-shu-zhi-he-by-leetcode-solution/

排序加双指针:

class Solution {
    
    
    public List<List<Integer>> threeSum(int[] nums) {
    
    
        int n = nums.length;
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        // 枚举 a
        for (int first = 0; first < n; ++first) {
    
    
            // 需要和上一次枚举的数不相同
            // 在-1,-1,-1,0……的场景下。第一个-1时没有限制
            // first可以为-1,second与third都可以为-1。此时认为已经将-1开头的所有工况都遍历了
            // 今后就不允许再以-1开头
            if (first > 0 && nums[first] == nums[first - 1]) {
    
    
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
    
    
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
    
    
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
    
    
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
    
    
                    break;
                }
                if (nums[second] + nums[third] == target) {
    
    
                    List<Integer> list = new ArrayList<Integer>();
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    ans.add(list);
                }
            }
        }
        return ans;
    }
}

例题3

在这里插入图片描述https://leetcode-cn.com/problems/container-with-most-water/solution/sheng-zui-duo-shui-de-rong-qi-by-leetcode-solution/

两个指针指向的数字中较小值∗指针之间的距离。移动数字较小的那个指针。

public class Solution {
    
    
    public int maxArea(int[] height) {
    
    
        int l = 0, r = height.length - 1;
        int ans = 0;
        while (l < r) {
    
    
            int area = Math.min(height[l], height[r]) * (r - l);
            ans = Math.max(ans, area);
            if (height[l] <= height[r]) {
    
    
                ++l;
            }
            else {
    
    
                --r;
            }
        }
        return ans;
    }
}

滑动窗口

引导

知识点:

  1. 可伸缩的滑动窗口,不断的调节窗口的起始index和终止index,判断能否当前窗口能否命中条件。
  2. 与双指针的思路类似,也是通过2个指针在一次遍历中完成需要两次遍历的工作。滑动窗口也可以理解为双指针法的一种。

主要确定三点:

  1. 窗口内是什么?命中条件是什么?
  2. 如何移动窗口的起始位置start?
  3. 如何移动窗口的结束位置end?一般是以end<n来判断的,滑动窗口最后一位到达终点即为结束。

例题1

题目链接

在这里插入图片描述

class Solution {
    
    
    public int minSubArrayLen(int s, int[] nums) {
    
    
        int n = nums.length;
        if (n == 0) {
    
    
            return 0;
        }
        int ans = Integer.MAX_VALUE;
        int start = 0, end = 0;
        int sum = 0;
        while (end < n) {
    
     // 滑动窗口end坐标到达终点即为结束
            sum += nums[end];
            while (sum >= s) {
    
    
                ans = Math.min(ans, end - start + 1);
                sum -= nums[start];
                start++;
            }
            end++;
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}

模拟

知识点:

  1. 模拟上下左右移动:int[][] directions = { {0, 1}, {1, 0}, {0, -1}, {-1, 0}};
  2. 控制方向:directionIndex = (directionIndex + 1) % 4;
  3. 查询坐标:假如m行n列。获取行:curNum/n;获取列:curNum%n

例题1

例题

class Solution {
    
    
    public int[][] generateMatrix(int n) {
    
    
        int maxNum = n * n;
        int curNum = 1;
        int[][] matrix = new int[n][n];
        int row = 0, column = 0;
        int[][] directions = {
    
    {
    
    0, 1}, {
    
    1, 0}, {
    
    0, -1}, {
    
    -1, 0}}; // 右下左上
        int directionIndex = 0;
        while (curNum <= maxNum) {
    
    
            matrix[row][column] = curNum;
            curNum++;
            int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
            if (nextRow < 0 || nextRow >= n || nextColumn < 0 || nextColumn >= n || matrix[nextRow][nextColumn] != 0) {
    
    
                directionIndex = (directionIndex + 1) % 4; // 顺时针旋转至下一个方向
            }
            row = row + directions[directionIndex][0];
            column = column + directions[directionIndex][1];
        }
        return matrix;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_38370441/article/details/115089108
今日推荐