01数组算法/代码随想录

server/2024/10/17 17:28:56/

一、数组

好久没写算法题,之前喜欢按着习惯选择刷题,很早以前就听说代码随想录,今天跟着代码随想录再过一遍算法

1.1二分查找
  1. 常见疑问

    • middle一定是在[left, right]这个范围内
    • 标准代码不会越界,因为在else if中出现越界后,下一次循环就不会通过
  2. 左闭右闭区间

    • 代码示例

      java">public class BinarySearch<T> {public int BinarySearch(int[] arr, int target) {int left = 0, right = arr.length - 1, middle = 0;while (left <= right) {middle = (left + right) / 2;if (arr[middle] == target) {return middle;}else if(arr[middle] > target) {right = middle - 1;}else if(arr[middle] < target) {left = middle + 1;}}return -1;}public static void main(String[] args) {BinarySearch<Integer> binarySearch = new BinarySearch<>();int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};System.out.println(binarySearch.BinarySearch(arr, 5));}
      }
      
    1. 为什么是left <= right,举一个极端例子[2, 2],这个时候如果是没有=,则无法返回值

    2. 为什么是middle - 1 middle + 1,因为在arr[middle] > targetarr[middle] < target的时候,已经判断过arr

      [middle]已不在范围内了,直接排除即可

  3. 左闭右开区间

    • 代码示例

      java">public class BinarySearch<T> {public int BinarySearch(int[] arr, int target) {int left = 0, right = arr.length - 1, middle = 0;while (left < right) {middle = (left + right) / 2;if (arr[middle] == target) {return middle;}else if(arr[middle] > target) {right = middle;}else if(arr[middle] < target) {left = middle + 1;}}return -1;}public static void main(String[] args) {BinarySearch<Integer> binarySearch = new BinarySearch<>();int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};System.out.println(binarySearch.BinarySearch(arr, 5));}
      }
      
    1. 为什么是left < right,因为在极端情况下,比如不断缩减或一开始数组就是这样[2, 2),这明显错误,所以不存在相等
    2. 为什么是middle,因为是开区间,开区间本身就不包括在内
    3. 左开右闭同理,不赘诉
1.2双指针
1.2.1 快慢指针:拥有快慢指针,快指针先行,慢指针跟随条件移动
  1. 常用来数组元素的删除

    • 代码示例

      java">public int removeElement(int[] nums, int val) {int fast = 0, slow = 0;for (; fast < nums.length; ++fast) {if (nums[fast] != val) {nums[slow] = nums[fast];slow++;}}return slow;
      }
      
    • 实战

      1. 力扣27

        在这里插入图片描述

        暴力

        java">class Solution {public int removeElement(int[] nums, int val) {int n = nums.length;for(int i = 0; i < n; ++i) {if (nums[i] == val) {for (int j = i; j < n - 1; ++j) {nums[j] = nums[j + 1];}n -= 1;i -= 1;}}return n;}
        }
        

        快慢指针

        java">class Solution {public int removeElement(int[] nums, int val) {int n = nums.length;int fast = 0; int slow = 0;while (fast < n) {if (nums[fast] != val) {nums[slow] = nums[fast];slow++;}fast++;}return slow;}
        }
        
      • 在只有一个要移除的元素时和暴力的区别不大,只有在多个需要去除的元素才能体现出优势,但这里算是初步体现了快慢双指针的思想,让一个快指针去遍历每一个元素并判断每一个元素是否等于要删除的元素,慢指针代表索引号,这样避免了找到一次元素就要遍历一次的尴尬处境。这里体现了双指针思想,拥有两个自由的判断点,通过两种指针的交替配合从而避免暴力双层循环(暴力往往只有一个自由的判断点,外层循环的遍历),下面几题变种也体现了这种思想。
1.2.2 左右指针:两个指针相互操作,知道相撞
  1. 常用来比较大小并排序,实现O(n)时间复杂度

    • 代码示例

      java">public int[] sortedSquares(int[] nums) {int left = 0;int n = nums.length;int right = n - 1;int[] res = new int[n];while (right >= left) {if (nums[left] * nums[left] > nums[right] * nums[right]) {res[n - 1] = nums[left] * nums[left++];} else {res[n - 1] = nums[right] * nums[right--];}n -= 1;}return res;
      }
      
    • 这里为什么是right >= left和二分查找类似,因为都是左闭右闭的场景

    • 实战

      1. 力扣977

        在这里插入图片描述

        java">public int[] sortedSquares(int[] nums) {int left = 0;int n = nums.length;int right = n - 1;int[] res = new int[n];while (right >= left) {if (nums[left] * nums[left] > nums[right] * nums[right]) {res[n - 1] = nums[left] * nums[left++];} else {res[n - 1] = nums[right] * nums[right--];}n -= 1;}return res;
        }
        
      • 左指针和右指针都往中间靠,如果右指针比左指针大,则右指针–,如果左指针比右指针大,则左指针++,知道右指针小于左指针
1.2.3 滑动窗口
  1. 用来计算哪一段区间最符合要求

    • 实战

      1. 力扣209

        在这里插入图片描述

        暴力(失败,超出时间限制)

        java">class Solution {public int minSubArrayLen(int target, int[] nums) {int res = Integer.MAX_VALUE;int n = nums.length - 1;for (int i = 0; i <= n; ++i) {int add = 0;for (int j = i; j <= n; ++j) {add += nums[j];if (add >= target) {res = Math.min(j - i + 1, res);break;}}}return res == Integer.MAX_VALUE ? 0 : res;}
        }
        

        滑动窗口

        java">public int minSubArrayLen(int target, int[] nums) {
        int left = 0, right = 0;
        int n = nums.length;
        int result = Integer.MAX_VALUE;
        int subLength = 0;
        int sum = 0;
        for (; right < n; ++right) {sum += nums[right];while (sum >= target) {subLength = right - left + 1;result = Math.min(subLength, result);sum -= nums[left++];}
        }
        return result == Integer.MAX_VALUE ? 0 : result;
        }
        
    • 若使用头指针,则和暴力没什么区别,所以选择尾部。滑动窗口一层循环则可直接计算,尾指针先移动,满足条件时,尾指针不动,头指针+1,再判断,如果通过,则更新result。否则继续移动尾指针,直到尾指针到数组尾部结束。

1.3模拟
  1. 对于难以应用算法的问题,可以通过模拟问题中的思路进行代码编写

    • 实战

    • 力扣54

      在这里插入图片描述

      java">class Solution {public List<Integer> spiralOrder(int[][] matrix) {List<Integer> res = new ArrayList<Integer>();int u = 0; int d = matrix.length - 1; int l = 0;int r = matrix[0].length - 1;while(true) {for (int i = l; i <= r; ++i) {res.add(matrix[u][i]);}if (++u > d) break;for (int i = u; i <= d; ++i) {res.add(matrix[i][r]);}if (--r < l) break;for (int i = r; i >= l; --i) {res.add(matrix[d][i]);}if (--d < u) break;for (int i = d; i >= u; --i) {res.add(matrix[i][l]);}if (++l > r) break;}return res;}
      }
      
    • 这是很常见的模拟之一,与力扣59互补,拿到这种题有常见的思路

      1. 大多数这种题目,都是给予条件,根据条件进行不断的计算。所以我们需要确定 1.循环中的计算 2.循环条件 3.终止条件

        • 上述写法是比较快的,有些地方是较优解的

          • 我比较喜欢使用while循环,因为while循环往往可以表示确定次数的循环和不确定次数的循环,for循环是确定次数的。但for循环也有一些优势,简洁明了,很容易判断循环条件

          • 拿与力扣59举例,那题是一个正方形,所以只会出现一种特殊情况就是层数为奇数的时候,需要手动添加垂直水平中间数组中的值,而这里行列是不一样的,所以终止的情况有多种,上下左右都有可能,所以直接在while循环中添加判断是比较复杂的

          • 这里,我再说个结论,中间的循环写法很大程度依靠终止条件的写法。力扣59强调每一个方向使用相同开闭区间的数组。这里是每一个变都是闭区间,是因为59题是依靠着一个实时的变量来控制数字在数组中的走向,这里判断条件不好写绕圈次数,因为上面说过终止条件判断较多,所以不太好靠一个实时变量来控制走向。由于终止条件很多,所以这里引用上下左右边界,就很好的解决了循环内写法的问题

          • 这种模拟的方法效率更高,大家在写模拟的时候,可以先将思路列举好,起始条件、终止条件、遍历写法,然后再一步一步优化

  • 总结

    1. 双指针技术的魅力就在于它通过一个主循环来控制遍历,同时在循环内进行值的计算和条件判断,从而避免了不必要的嵌套循环。这种方式不仅提高了效率,还使得代码更加简洁和易于理解。所以写双指针的时候需要在一层循环就进行计算,

      • 以下的写法就是为了写双指针而写,实际上就是暴力,没有做到第一层循环就计算,力扣209

        java">public int minSubArrayLen(int target, int[] nums) {int start = 0, end = 0;int n = nums.length;int res = Integer.MAX_VALUE;while (end < n) {int add = 0;int i = start;// 计算窗口内的和for (; i <= end; i++) {add += nums[i];if (add >= target) {res = Math.min(end - start + 1, res);break;  // 找到满足条件的和后,退出循环}}// 如果找到满足条件的和,移动 startif (add >= target) {start++;} else {end++;  // 否则,移动 end}}return res == Integer.MAX_VALUE ? 0 : res;  // 如果没有找到,返回 0
        }
        

http://www.ppmy.cn/server/132541.html

相关文章

自由学习记录(6)

单例为何存在 静态类不能继承其他类&#xff0c;也不能被继承&#xff0c;这是静态类的一个限制。静态类的设计初衷是为了提供一组与对象无关的工具或功能&#xff0c;因此它没有对象实例的概念&#xff0c;这也意味着不能利用面向对象中的继承和多态特性。 为什么不能继承静…

2024-10-16 问AI: [AI面试题] 描述遗传算法的概念

文心一言 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;是一种模拟生物进化过程的全局优化搜索算法&#xff0c;其概念可以从以下几个方面进行描述&#xff1a; 一、基本原理 遗传算法借鉴了达尔文的进化论和孟德尔的遗传学说&#xff0c;其本质是一种并…

使用Hugging Face中的BERT进行标题分类

使用Hugging Face中的BERT进行标题分类 前言相关介绍出处基本原理优点缺点 前提条件实验环境BERT进行标题分类准备数据集读取数据集划分数据集设置相关参数创建自己DataSet对象计算准确率定义预训练模型定义优化器训练模型保存模型测试模型 参考文献 前言 由于本人水平有限&…

springboot系列--web相关知识探索五

一、前言 web相关知识探索四中研究了请求中所带的参数是如何映射到接口参数中的&#xff0c;也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、以及自定义对象参数。web相关知识探索四中主要研究了复杂参数底层绑定原理。本次主要是研…

PCB厚铜板究竟厚在哪,猎板PCB告诉您

在PCB厚铜板领域&#xff0c;厚铜板通常指的是在PCB制造过程中&#xff0c;使用比标准PCB厚度更大的铜箔。标准PCB铜箔厚度一般在35微米&#xff08;1盎司/平方英尺&#xff09;左右&#xff0c;而厚铜PCB的铜厚度通常在105微米&#xff08;3盎司/平方英尺&#xff09;及以上&a…

在uniapp中实现长按聊天对话框可以弹出对话框然后可以删除该条对话,单击可以进入该条对话框的对话页面

效果展示 效果描述 长按【大于1s】某一条对话框会弹出一个对话框&#xff0c;点击确定按钮就可以将当前对话框从列表中进行删除&#xff0c;如果点击取消则不做额外操作。 如果只是点击了一下&#xff0c;时间【小于1s】的情况下会直接引入到与该用户的对话框详情页面。 代码…

【Docker】Elasticsearch Docker 容器数据迁移

背景 之前的 es 数据是容器化部署的&#xff0c;由于最近云服务器到期&#xff0c;需要进行更换&#xff0c;于是需要进行es 容器和es 数据的迁移。这里记录一下。 版本信息&#xff1a; es&#xff1a;7.10.0kibana:7.10.0 操作步骤 1. 新环境下载docker 镜像 版本与旧环…

GNU/Linux - Savannah项目

* 我们托管在自由操作系统上运行的自由项目&#xff0c;不依赖任何专有软件。 * 我们的服务使用 100% 的自由软件运行&#xff0c;包括服务本身。 * We host free projects that run on free operating systems and without any proprietary software dependencies. * Our se…