代码随想录五刷day1

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言


数组中的五个经典操作 一、二分法 二、双指针法 三、滑动窗口 四、模拟行为 五、前缀和

一、力扣704. 二分查找

·二分查找就是在一个无重复元素的有序的序列上面,根据最大值和最小值,来寻找定位目标值的方法,每次根据最大值和最小值,计算出中间值,判断目标值落在中间值的左边还是右边,从而缩小搜索范围,最后通过边界条件的等或者不等,进行定位·

class Solution {
    public int search(int[] nums, int target) {
        Arrays.sort(nums);
        int left = 0, right = nums.length-1, mid = 0;
        while(left <= right){
            mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return -1;
    }
}

左闭右开式写法

class Solution {
    public int search(int[] nums, int target) {
        Arrays.sort(nums);
        int left = 0, right = nums.length, mid = 0;
        while(left < right){
            mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                right = mid;
            }
        }
        return -1;
    }
}

二、力扣35. 搜索插入位置

使用二分查找算法定位目标值的位置,当队列为有序的就可以,至于元素唯一不唯一,都能找到合适的位置

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length - 1, mid = 0;
        while(left <= right){
            mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return right + 1;
    }
}

三、力扣34. 在排序数组中查找元素的第一个和最后一个位置

对于在有序队列里面寻找位置,直接识别为二分算法变形,在这里面,左右边界分开寻找,一边找一个,找到第一个非目标元素就算成功 第二种方法就是,一个二分查找直接找目标值,若是找不到,就是默认值边界,要是找到了,直接写两个while循环,向左,向右找边界

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[]{-1, -1};
        int left = 0, right = nums.length -1, mid = 0;
        //寻找左边界
        while(left <= right){
            mid = left + (right - left) / 2;
            if(nums[mid] == target){
                if(mid > 0 && nums[mid-1] == target){
                    right = mid - 1;
                }else{
                    res[0] = mid;
                    break;
                }
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        //寻找右边界
        left = 0;
        right = nums.length -1;
        while(left <= right){
            mid = left + (right - left) / 2;
            if(nums[mid] == target){
                if(mid < nums.length-1 && nums[mid+1] == target){
                    left = mid + 1;
                }else{
                    res[1] = mid;
                    break;
                }
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        //返回结果
        return res;
    }
}

四、力扣69. x 的平方根

寻找一个数的平方根,可以直接使用二分查找,从1到这个数本身就是一个不重复的有序队列,为了防止整数溢出计算的时候,注意一下就行

class Solution {
    public int mySqrt(int x) {
        int left = 1, right = x, mid = 0;
        while(left <= right){
            mid = left + (right - left) / 2;
            if(mid == x / mid){
                return mid;
            }else if(mid < x / mid){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return right;
    }
}

五、力扣367. 有效的完全平方数

对于这种数值的计算,大部分都可以归为二分查找的范畴

class Solution {
    public boolean isPerfectSquare(int num) {
        int left = 1, right = num, mid = 0;
        while(left <= right){
            mid = left + (right - left) / 2;
            if(mid == num / mid && mid * mid == num){
                return true;
            }else if(mid < num / mid){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return false;
    }
}

六、力扣27. 移除元素

对于移除元素来说,应该尽量避免元素的大规模移动,所以应该使用双指针算法,两个指针一个用于重定位赋值,另外一个用于正向遍历扫描,遇到目标值不做处理跳过,非目标值,进行重赋值操作,直到右指针扫描完毕

class Solution {
    public int removeElement(int[] nums, int val) {
        int count = 0;
        for(int i = 0, j = 0; j < nums.length; ){
            if(nums[j] == val){
                count ++;
                j ++;
            }else{
                nums[i] = nums[j];
                i ++;
                j ++;
            }
        }
        return count;
    }
}

七、力扣26. 删除有序数组中的重复项

删除有序数组中的重复项,推荐使用双指针中的快慢指针法,不需要大规模移动元素,只需要进行重赋值操作

class Solution {
    public int removeDuplicates(int[] nums) {
        int res = 1;
        if(nums.length == 1){
            return res;
        }
        for(int i = 0, j = 1; j < nums.length;){
            if(nums[j] == nums[i]){
                j ++;
            }else{
                nums[++i] = nums[j++];
                res = i+1;
            }
        }
        return res;
    }
}

八、力扣283. 移动零

只对特定元素进行移动,其他保持相对位置不变,使用快慢时针,双指针的特点就是可以进行重赋值操作,这个才是解决问题的特性

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

九、力扣844. 比较含退格的字符串

class Solution {
    public boolean backspaceCompare(String s, String t) {
        StringBuilder sb1 = new StringBuilder();
        StringBuilder sb2 = new StringBuilder();
        for(char c : s.toCharArray()){
            if(c == '#'){
                if(sb1.length() > 0){
                    sb1.deleteCharAt(sb1.length()-1);
                }
            }else{
                sb1.append(c);
            }
        }
        for(char c : t.toCharArray()){
            if(c == '#'){
                if(sb2.length() > 0){
                    sb2.deleteCharAt(sb2.length()-1);
                }
            }else{
                sb2.append(c);
            }
        }
        int i = 0, j = 0;
        while(i < sb1.length() && j < sb2.length()){
            if(sb1.charAt(i) != sb2.charAt(j)){
                return false;
            }
            i ++;
            j ++;
        }
        return sb1.length() == sb2.length();
    }
}

十、力扣977. 有序数组的平方

class Solution {
    public int[] sortedSquares(int[] nums) {
        int[] res = new int[nums.length];
        for(int i = 0, j = nums.length-1, k = j; i <= j;){
            if(nums[i]*nums[i] >= nums[j]*nums[j]){
                res[k --] = nums[i]*nums[i];
                i ++;
            }else{
                res[k --] = nums[j]*nums[j];
                j --;
            }
        }
        return res;
    }
}

十一、力扣209. 长度最小的子数组

经典的滑动窗口,设置一个全局最小子数组长度,窗口滑动一次,就可以获得结果 滑动的具体过程就是,右指针每次向右移动一下,之后判读窗口内的值是否达到目标值,若是达到,就开始while循环收缩左指针,直到窗口内的值不满足目标值,此时继续右指针的动作

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int res = Integer.MAX_VALUE;
        int left = 0, right = 0, sum = 0;
        while(right < nums.length){
            sum += nums[right];
            right ++;
            while(left < right && sum >= target){
                res = Math.min(res, right - left);
                sum -= nums[left ++];
            }
        }
        return res == Integer.MAX_VALUE ? 0 : res;
    }
}

十二、力扣904. 水果成篮

class Solution {
    public int totalFruit(int[] fruits) {
        int res = 0;
        Map<Integer,Integer> map = new HashMap<>();
        int left = 0, right = 0;
        while(right < fruits.length){
            map.put(fruits[right],map.getOrDefault(fruits[right],0) + 1);
            right ++;
            if(map.size() <= 2){
                res = Math.max(res,right-left);
            }
            while(left < right && map.size() > 2){
                map.put(fruits[left], map.getOrDefault(fruits[left],0) - 1);
                if(map.get(fruits[left]) == 0){
                    map.remove(fruits[left]);
                }
                left ++;
            }
        }
        return res;
    }
}

十三、力扣76. 最小覆盖子串

滑动窗口,指的的就是左右指针,一次遍历,不走回头路,在中间的窗口中收集需要的结果,大循环每循环一次,窗口就向右扩大一下,扩大后就要检测,窗口是否满足缩小的特征,若是满足就开始从左缩小窗口,在缩小的过程中进行收集

class Solution {
    public String minWindow(String s, String t) {
        Map<Character,Integer> map = new HashMap<>();
        Set<Character> set = new HashSet<>();
        String res = s + "-";
        for(char c : t.toCharArray()){
            set.add(c);
            map.put(c, map.getOrDefault(c,0) + 1);
        }
        int left = 0, right = 0, count = map.size();
        char[] sh = s.toCharArray();
        while(right < s.length()){
            if(set.contains(sh[right])){
                map.put(sh[right], map.getOrDefault(sh[right], 0) - 1);
                if(map.get(sh[right]) == 0){
                    count --;
                }
            }
            right ++;
            while(left < right && count == 0){
                if(set.contains(sh[left])){
                    if(map.get(sh[left]) == 0){
                        res = (right - left) < res.length() ? s.substring(left, right) : res;
                        count ++;
                    }
                    map.put(sh[left], map.getOrDefault(sh[left], 0) + 1);
                }
                left ++;
            }
        }
        return res.equals(s + "-") ? "" : res;
    }
}

十四、力扣59. 螺旋矩阵 II

这种类型的题目属于模拟,看清楚题目,处理好边界条件就行

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] res = new int[n][n];
        int left = 0, right = n-1, low = n-1, under = 0;
        for(int a = 1; a <= n*n; ){
            for(int i = left; i <= right && a <= n*n; i ++){
                res[under][i] = a ++;
            }
            under ++;
            for(int i = under; i <= low && a <= n*n; i ++){
                res[i][right] = a ++;
            }
            right --;
            for(int i = right; i >= left && a <= n*n; i --){
                res[low][i] = a ++;
            }
            low --;
            for(int i = low; i >= under && a <= n*n; i --){
                res[i][left] = a ++;
            }
            left ++;
        }
        return res;
    }
}

十五、力扣54. 螺旋矩阵

经典模拟,先左后右,从上到下,再从右向左,再从下到上

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> res = new ArrayList<>();
        int m = matrix.length, n = matrix[0].length;
        int left = 0, right = n-1, up = 0, down = m-1;
        for(int a = 1; a <= m*n; ){
            for(int i = left; i <= right && a <= m*n; i ++){
                res.add(matrix[up][i]);
                a ++;
            }
            up ++;
            for(int i = up; i <= down && a <= m*n; i ++){
                res.add(matrix[i][right]);
                a ++;
            }
            right --;
            for(int i = right; i >= left && a <= m*n; i --){
                res.add(matrix[down][i]);
                a ++;
            }
            down --;
            for(int i = down; i >= up && a <= m*n; i --){
                res.add(matrix[i][left]);
                a ++;
            }
            left ++;
        }
        return res;
    }
}

十六、力扣LCR 146. 螺旋遍历二维数组

class Solution {
    public int[] spiralArray(int[][] array) {
        if(array.length == 0 || array[0].length == 0){
            return new int[0];
        }
        int m = array.length, n = array[0].length;
        int[] res = new int[m*n];
        int left = 0, right = n-1, up = 0, down = m-1;
        for(int a = 0; a < m*n; ){
            for(int i = left; i <= right && a < m*n; i ++){
                res[a ++] = array[up][i];
            }
            up ++;
            for(int i = up; i <= down && a < m*n; i ++){
                res[a ++] = array[i][right];
            }
            right --;
            for(int i = right; i >= left && a < m*n; i --){
                res[a ++] = array[down][i];
            }
            down --;
            for(int i = down; i >= up && a < m*n; i --){
                res[a ++] = array[i][left];
            }
            left ++;
        }
        return res;
    }
}

十七、卡码网 58. 区间和(第九期模拟笔试)

使用前缀和来计算区间和是最好的方式,两个前缀和之间的差,就是他们下标区间左开右闭的区间和

import java.util.*;
public class Main{
    
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] sum = new int[n+1];
        for(int i = 1; i < sum.length; i ++){
            sum[i] = sum[i-1] + scanner.nextInt();
        }
        int a = 0, b = 0;
        while(scanner.hasNextInt()){
            a = scanner.nextInt();
            b = scanner.nextInt();
            System.out.println(sum[b+1] - sum[a]);
        }
        
    }
    
}

十八、卡码网 44. 开发商购买土地(第五期模拟笔试)

import java.util.*;
public class Main{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int m = scanner.nextInt(), n = scanner.nextInt();
        scanner.nextLine();
        int sum = 0;
        int[][] nums = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                nums[i][j] = scanner.nextInt();
                sum += nums[i][j];
            }
        }
        scanner.close();
        int res = Integer.MAX_VALUE;
        int temp = 0;
        for (int i = 0; i < m - 1; i++) {
            for (int j = 0; j < n; j++) {
                temp += nums[i][j];
            }
            res = Math.min(res, Math.abs(temp - (sum - temp)));
        }
        temp = 0;
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < m; j++) {
                temp += nums[j][i];
            }
            res = Math.min(res, Math.abs(temp - (sum - temp)));
        }
        System.out.println(res);
    }
}
### 关于代码随想录 Day04 的学习资料与解析 #### 一、Day04 主要内容概述 代码随想录 Day04 的主要内容围绕 **二叉树的遍历** 展开,包括前序、中序和后序三种遍历方式。这些遍历可以通过递归实现,也可以通过栈的方式进行迭代实现[^1]。 #### 二、二叉树的遍历方法详解 ##### 1. 前序遍历(Pre-order Traversal) 前序遍历遵循访问顺序:根节点 -> 左子树 -> 右子树。以下是基于递归的实现: ```python def preorderTraversal(root): result = [] def traversal(node): if not node: return result.append(node.val) # 访问根节点 traversal(node.left) # 遍历左子树 traversal(node.right) # 遍历右子树 traversal(root) return result ``` 对于迭代版本,则可以利用显式的栈来模拟递归过程: ```python def preorderTraversal_iterative(root): stack, result = [], [] current = root while stack or current: while current: result.append(current.val) # 访问当前节点 stack.append(current) # 将当前节点压入栈 current = current.left # 转向左子树 current = stack.pop() # 弹出栈顶元素 current = current.right # 转向右子树 return result ``` ##### 2. 中序遍历(In-order Traversal) 中序遍历遵循访问顺序:左子树 -> 根节点 -> 右子树。递归实现如下: ```python def inorderTraversal(root): result = [] def traversal(node): if not node: return traversal(node.left) # 遍历左子树 result.append(node.val) # 访问根节点 traversal(node.right) # 遍历右子树 traversal(root) return result ``` 迭代版本同样依赖栈结构: ```python def inorderTraversal_iterative(root): stack, result = [], [] current = root while stack or current: while current: stack.append(current) # 当前节点压入栈 current = current.left # 转向左子树 current = stack.pop() # 弹出栈顶元素 result.append(current.val) # 访问当前节点 current = current.right # 转向右子树 return result ``` ##### 3. 后序遍历(Post-order Traversal) 后序遍历遵循访问顺序:左子树 -> 右子树 -> 根节点。递归实现较为直观: ```python def postorderTraversal(root): result = [] def traversal(node): if not node: return traversal(node.left) # 遍历左子树 traversal(node.right) # 遍历右子树 result.append(node.val) # 访问根节点 traversal(root) return result ``` 而迭代版本则稍复杂一些,通常采用双栈法或标记法完成: ```python def postorderTraversal_iterative(root): if not root: return [] stack, result = [root], [] while stack: current = stack.pop() result.insert(0, current.val) # 插入到结果列表头部 if current.left: stack.append(current.left) # 先压左子树 if current.right: stack.append(current.right) # 再压右子树 return result ``` #### 三、补充知识点 除了上述基本的二叉树遍历外,Day04 还可能涉及其他相关内容,例如卡特兰数的应用场景以及组合问题的基础模板[^2][^4]。如果遇到具体题目,可以根据实际需求调用相应算法工具。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乱世在摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值