【算法通关村第三关——双指针的妙用】

双指针思想

双指针在之前的文章中的链表也有用到过,可以说是一种简单又好用的方式。同样在处理数组中也非常常见。

删除元素

1.1 原地移除所有数值等于val的元素

例子1:
输入:nums = [3,2,2,3] val = 3
输出:2,nums = [2,2]
例子2:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, [0,1,4,0,3]

方法一:快慢双指针

在这里插入图片描述

/**
     * 方法1:使用快慢型双指针
     *
     * @param nums
     * @param val
     * @return
     */
    public static int removeElement(int[] nums, int val) {
        //定义快慢指针下标
        int slow = 0;
        //因为快指针每次随着循环都要+1所以直接在循环中定义
        for (int fast = 0; fast < nums.length; fast++) {
            if (nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
        }
        //最后剩余元素的数量
        return slow;
    }

方法二:对撞指针

在这里插入图片描述

/**
     * 方法2:使用对撞型双指针
     *
     * @param nums
     * @param val
     * @return
     */
    public static int removeElement2(int[] nums, int val) {
        //定义左右指针
        int right = nums.length - 1;
        int left;
        for (left = 0; left <= right; ) {
            if ((nums[left] == val) && (nums[right] != val)) {
                int tmp = nums[left];
                nums[left] = nums[right];
                nums[right] = tmp;
            }
            if (nums[left] != val) {
                left++;
            }
            if (nums[right] == val) {
                right--;
            }

        }
        return left;
    }

方法三:方法一+方法二

/**
     * 方法三:优化:对撞型双指针+快慢指针
     *
     * @param nums
     * @param val
     * @return
     */
    public static int removeElement3(int[] nums, int val) {
        int right = nums.length - 1;
        for (int left = 0; left < right; ) {
            if (nums[left] == val) {
                nums[left] = nums[right];
                right--;
            } else {
                left++;
            }
        }
        return right;
    }

1.2 删除有序数组中的重复项

LeetCode26 给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组 并在使用 O(1) 额外空间的条件下完成。

注意:这里必须是有序数组,否则就会出现下图所示的问题,我哭,作者花了几分钟画的图,发现数组无序,怎么都对不上。
在这里插入图片描述
这里附上代码:

  public static int removeDuplicates(int[] nums) {
        //slow表示可以放入新元素的位置,索引为0的元素不用管
        int slow = 1;
        //循环起到了快指针的作用
        for (int fast = 0; fast < nums.length; fast++) {
            if (nums[fast] != nums[slow - 1]) {
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }

元素奇偶移动专题

根据某些条件移动元素也是一类常见的题目,例如排序本身就是在移动元素,这里看一个奇偶移动的问题。
LeetCode905,按奇偶排序数组。给定一个非负整数数组 A,返回一个数组,在该数组中,A的所有偶数元素之后跟着所有奇数元素。你可以返回满足此条件的任何数组作为答案。

例如:
输入:[3,1,2,4]
输出:[2,4,3,1] (这里顺序不对也算正确)

这里直接用空间负责度为O(1)的方法,另外需要额外创建一个临时数组,然后第一遍查找所有偶数复制,然后第二遍查找所有基数,在接着复制的方法比较简单。
第二种方法就是对撞快慢指针
在这里插入图片描述
维护两个指针 left= 0和right=arr.length-1,left从0开始逐个检查每个位置是否为偶数,如果是则跳过,如果是奇数则停下来。然后right从右向左检查,如果是奇数则跳过偶数则停下来。然后交换array[left]和array[right]。之后再继续循环,直到left> =right。
附上代码:

  public static int[] sortArrayByParity(int[] A) {
        int left = 0, right = A.length - 1;
        while (left < right) {
            if (A[left] % 2 > A[right] % 2) {
                int tmp = A[left];
                A[left] = A[right];
                A[right] = tmp;
            }

            if (A[left] % 2 == 0) left++;
            if (A[right] % 2 == 1) right--;
        }

        return A;
    }

数组轮转问题

LeetCode189.给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

输入:nums = [1,2,3,4,5,6,7],k=3
输出:[5,6,7,1,2,3,4]

这里使用两轮反转的方式实践。
思路如下:
1.首先对整个数组实行翻转,例如[1,2,3,4,5,6,7] 我们先将其整体翻转成[7,6,5,4,3,2,1].
2.从k 处分隔成左右两个部分,这里就是根据k将其分成两组 [7,6,5] 和[4,3,2,1]
3.最后将两个再次翻转就得到[5,6,7] 和[1,2,3,4],最终结果就是[5,6,7,1,2,3,4]
结合代码看:

public static 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);
    }

    public static void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start += 1;
            end -= 1;
        }
    }

数组的区间专题

LeetCode228.给定一个无重复元素的有序整数数组nums。返回恰好覆盖数组中所有数字的最小有序区间范围列表。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字x。列表中的每个区间范围a,b] 应该按如下格式输出:"a->b”。

其实可以直接忽略上面的话,,额,,反正我读起来挺困难的,直接看示例:

输入:nums = [0,1,2,4,5,7]
输出:[“0->2”,“4->5”,“7”]

看到这种问题,直接先把快慢指针的模板套上。直接上代码

 public static List<String> summaryRanges(int[] nums) {
        List<String> res = new ArrayList<>();
        // slow 初始指向第 1 个区间的起始位置
        int slow = 0;
        for (int fast = 0; fast < nums.length; fast++) {
            // fast 向后遍历,直到不满足连续递增(即 nums[fast] + 1 != nums[fast + 1])
            // 或者 fast 达到数组边界,则当前连续递增区间 [slow, fast] 遍历完毕,将其写入结果列表。
            if (fast + 1 == nums.length || nums[fast] + 1 != nums[fast + 1]) {
                // 将当前区间 [slow, fast] 写入结果列表
                StringBuilder sb = new StringBuilder();
                sb.append(nums[slow]);
                if (slow != fast) {
                    sb.append("->").append(nums[fast]);
                }
                res.add(sb.toString());
                // 将 slow 指向更新为 fast + 1,作为下一个区间的起始位置
                slow = fast + 1;
            }
        }
        return res;
    }

字符串替换空格问题

这是剑指offer中的题目,出现频率也很高: 请实现一个函数,将一个字符串中的每个空格替换成"%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
代码如下

 /**
     * 方法1:创建新的字符串
     * 最简单粗暴
     *
     * @param str
     * @return
     */
    public static String replaceSpace1(StringBuffer str) {
        String res = "";
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c == ' ')
                res += "%20";
            else
                res += c;
        }
        return res;
    }
      /**
     * 方法2:在原数组基础上改
     * 个人感觉此方法用双指针属实有点强求了
     * 大家选择性观看
     * @param str
     * @return
     */
    public static String replaceSpace2(StringBuffer str) {
        if (str == null)
            return null;
        int numOfblank = 0;//空格数量
        int len = str.length();
        for (int i = 0; i < len; i++) {  //计算空格数量
            if (str.charAt(i) == ' ')
                numOfblank++;
        }
        str.setLength(len + 2 * numOfblank); //设置长度
        int fast = len - 1;  //两个指针
        int slow = (len + 2 * numOfblank) - 1;

        while (fast >= 0 && slow > fast) {
            char c = str.charAt(fast);
            if (c == ' ') {
                fast--;
                str.setCharAt(slow--, '0');
                str.setCharAt(slow--, '2');
                str.setCharAt(slow--, '%');
            } else {
                str.setCharAt(slow, c);
                fast--;
                slow--;
            }
        }
        return str.toString();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值