• 双指针系列-数组篇(LeetCode 27,26,283,844,977)


    E 27.移除元素

    给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。

    不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

    元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

    解题思路1是利用双指针,首尾指针,从首指针开始遍历,若首指针是val,则尾指针位置的值赋值,尾指针动而首指针不动,是为了下一次要比较前一次尾指针的赋值是否为val,不是val则首指针向右移动。这里尾指针赋值为len,时间复杂度为O(n),空间复杂度为O(1)

    思路2是双指针均从首处开始,但是这里会出现非val值的重复赋值

    1. class Solution {
    2. public int removeElement(int[] nums, int val) {//双指针思路,首尾指针,重合时跳出
    3. //这里注意处理尾处指针是否为val值,这里未保留移除后原数组的顺序,若需保留则可以首首双指针
    4. //单元素或空元素处理
    5. int len = nums.length;
    6. int left = 0;
    7. int right = len;//这里赋值len则无需比较len=1的特殊情况
    8. //len == 1 && nums[len - 1] == val,且right-1==left处的值也比较了,例如[3,3]示例
    9. while(left < right){
    10. if(nums[left] == val){
    11. nums[left] = nums[right - 1];
    12. right--;//不对首指针进行处理,下一次就可以判断尾指针的赋值是否为val
    13. }
    14. else
    15. left++;
    16. }
    17. return left;
    18. }//这个只遍历一遍
    19. }

    E 26.删除排序数组中的重复项

    给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

    由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

    将最终结果插入 nums 的前 k 个位置后返回 k 。

    不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

    解题思路是双指针left和right在首首处开始,找到第一个不与left指针相同数值的right指针位置的数赋值,注意先left+1再赋值,因为还需每个元素出现1次,时间复杂度为O(n),空间复杂度为O(1)

    1. class Solution {
    2. public int removeDuplicates(int[] nums) {
    3. if(nums.length == 0 || nums == null)
    4. return 0;
    5. int left = 0;
    6. for(int right = 1; right < nums.length; right++){
    7. if(nums[left] != nums[right]){
    8. left++;
    9. nums[left] = nums[right];
    10. }
    11. }
    12. return left + 1;
    13. }
    14. }

    E 283.移动零

    给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

    请注意 ,必须在不复制数组的情况下原地对数组进行操作。

    解题思路是利用双指针left,right首首开始,当right指针不为0时,则赋值给left指针,right指针处附0,其实也是交换的意思,无需对left指针进行判断,因为均从首首开始,所以right指针就包含了left指针的判断。时间复杂度为O(n),空间复杂度为O(1)

    1. class Solution {
    2. public void moveZeroes(int[] nums) {
    3. int left = 0;
    4. int right = 0;
    5. while(right < nums.length){
    6. if(nums[right] != 0){
    7. if(right > left){
    8. nums[left] = nums[right];
    9. nums[right] = 0; //也是做交换的意思
    10. }
    11. left++;
    12. }
    13. right++;
    14. }
    15. //这个题我是赋值处理方法,我的误区就是一直在right位置是否为0处怎么好移动left和right,
    16. //后面自己附0的方法其实跟交换思想有异曲同工之处,但是我一直在纠结left=0和right=0,
    17. //其实只要考虑right就行,因为right是有left那里过去的,既然left没动,说明left处就为0,动了才前进+1
    18. }

    E 844.比较含退格的字符串

    给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

    注意:如果对空文本输入退格字符,文本继续为空。

    该题使用双指针的思路解决,时间复杂度为O(n + m),空间复杂度是O(1),此外还可以借助栈来实线,时间复杂度为O(n + m),空间复杂度是O(n + m),总体思路是逐步消除'#'的影响后再来比较字符,此题中只有'#'左边的字符会消除,所以考虑借助指针尾处开始,使用另一个指针skip来记录删除字符的情况,遇到'#'需增加删除字符个数,skip+1,遇到其他字符skip-1代表删除该字符,然后均左移,直到遇到无'#'影响的字符才跳出比较

    1. class Solution {
    2. public boolean backspaceCompare(String s, String t) {
    3. //空间复杂度需为O(1),栈的方法空间复杂度为O(n)
    4. //'#'只对左边字符有影响所以从逆序开始遍历,使用Skip来处理'#'的影响
    5. //遇到'#'说明需要删除一个字符skip+1,遇到非'#'且skip不为0,则代表删除该字符,skip-1,
    6. //处理好'#'的影响后,我们再来比较字符串
    7. int i = s.length() - 1;
    8. int j = t.length() - 1;
    9. int skipS = 0, skipT = 0;
    10. /*
    11. char[] s = S.toCharArray();转换为字符数组的方式
    12. char[] t = T.toCharArray();
    13. */
    14. while(i >= 0 || j >= 0){//一方出界才结束循环
    15. //处理'#'对s的影响,即寻找第一个可以比较的字符位置
    16. while(i >= 0){
    17. if(s.charAt(i) == '#'){//需增加删除字符,skipS增加,继续左移
    18. skipS++;
    19. i--;
    20. }
    21. else if(skipS > 0){//该位置字符仍需删除,删除后skipS减少,继续左移
    22. skipS--;
    23. i--;
    24. }
    25. else//既不等于'#'又skipS为0,则此时该位置字符无需删除可以作比较,跳出循环
    26. break;
    27. }
    28. //与String s同理
    29. while(j >= 0){
    30. if(t.charAt(j) == '#'){
    31. skipT++;
    32. j--;
    33. }
    34. else if(skipT > 0){
    35. skipT--;
    36. j--;
    37. }
    38. else
    39. break;
    40. }
    41. //下面开始比较,考虑false的几种情况
    42. //一方出界i >= 0, j < 0; j >= 0, i < 0
    43. //均未出界i >= 0, j >= 0,但当前字符不同s[i] != t[j];
    44. if(i >= 0 && j >= 0){
    45. if(s.charAt(i) != t.charAt(j))
    46. return false;
    47. }
    48. else if(i >= 0 || j >= 0)
    49. return false;
    50. //注意这里不在之后使用else,会超出时间限制
    51. //因为如果进入到前面的if里则到不了这里的--操作,实际上未return还需继续遍历
    52. i--;
    53. j--;
    54. }
    55. return true;
    56. }
    57. }

    E 977.有序数组的平方

    给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

    1.双指针+归并排序思想 

    利用原数组有序的条件,找到负数与非负数的分界点,然后利用双指针在分界点处两边进行比较,选择较小的值填充入新结果数组,若有一指针到达分界点,进另一指针依次向边界点移动填充结果,时间复杂度是O(n),不算上新数组开辟的空间则空间复杂度是O(1)

    1. class Solution {
    2. public int[] sortedSquares(int[] nums) {
    3. //官方双指针解法1,解题思路是找到负数与非负数的分界点,利用其单调性进行解题
    4. //首先找出这个分界点
    5. int neg = -1;
    6. for(int i = 0; i < nums.length; i++){
    7. if(nums[i] < 0){
    8. neg = i;
    9. }
    10. else
    11. break;//找到第一个非负数的位置则跳出,neg位置为负数,neg + 1为非负数
    12. }
    13. //分界点左边平方后单调递减,右边单调递增,移动两指针顺序放入结果数组中
    14. int[] sqr = new int[nums.length];
    15. int left = neg;
    16. int right = neg + 1;
    17. int index = 0;
    18. while(left >= 0 || right < nums.length){
    19. if(left < 0){
    20. sqr[index] = nums[right] * nums[right];
    21. right++;
    22. }//说明全是非负数,直接顺序放入即可
    23. else if(right == nums.length){
    24. sqr[index] = nums[left] * nums[left];
    25. left--;
    26. }//说明全是负数,left指针逆序顺序放入即可
    27. else if(nums[left] * nums[left] > nums[right] * nums[right]){
    28. sqr[index] = nums[right] * nums[right];
    29. right++;
    30. }//比较放入小的
    31. else{
    32. sqr[index] = nums[left] * nums[left];
    33. left--;
    34. }
    35. index++;
    36. }
    37. return sqr;
    38. }
    39. }

    2.首尾双指针比较

    首尾指针比较,若值较大者则放入新数组中,逆序填充,时间复杂度是O(n),不算上新数组开辟的空间则空间复杂度是O(1)

    1. class Solution {
    2. public int[] sortedSquares(int[] nums) {
    3. //官方双指针解法2,解题思路是首尾指针比较,较大的值逆序填充结果数组
    4. int[] sqr = new int[nums.length];
    5. int left = 0;
    6. int right = nums.length - 1;
    7. int index = nums.length - 1;//逆序填充
    8. while(left <= right){//注意left=right处也要比较
    9. if(nums[left] * nums[left] > nums[right] * nums[right]){
    10. sqr[index] = nums[left] * nums[left];
    11. left++;
    12. }
    13. else{
    14. sqr[index] = nums[right] * nums[right];
    15. right--;
    16. }
    17. index--;
    18. }
    19. return sqr;
    20. }
    21. }

  • 相关阅读:
    tomcat里部署多个war,导致配置文件错乱。
    WebRTC研究:audio 丢包判断
    微信小程序之后台首页交互
    一文精通C++ -- 继承
    二十、SpringBoot + Jwt + Vue 权限管理系统(1)
    【1++的C++进阶】之智能指针
    MongoDB——文档增删改查命令使用
    神经网络和AI的关系
    【JavaEE基础与高级 第56章】Java中的打印流、属性集、IO流异常的处理详细使用介绍
    大数据必学Java基础(十):标识符和关键字
  • 原文地址:https://blog.csdn.net/weixin_42107106/article/details/125461524