LintCode双指针/滑动窗口/Two Sum类型题总结

双指针题算是数组类型题目的一个子模块了。

373. Partition Array by Odd and Even

把一个数组划分为奇数在前偶数在后的状态,要求in place。很简单,就用双指针法,让两个指针从两头往中间扫描,当左边是偶数右边是奇数时就交换,直到左右指针相遇为止。

    public void partitionArray(int[] nums) {
        int left = 0, right = nums.length - 1;
        while (left < right) {
            // 左边是奇数的话就自增,直到找到前面的偶数
            while (nums[left] % 2 == 1) {
                left++;
            }
            // 右边是偶数的话就自减,直到找到后面的奇数
            while (nums[right] % 2 == 0) {
                right--;
            }
            if (left < right && nums[left] % 2 == 0 && nums[right] % 2 == 1) {
                int tmp = nums[left];
                nums[left] = nums[right];
                nums[right] = tmp;
            }
        }
        return;
    }

539. Move Zeroes

把一个数组的所有0移动到末尾,且不改变原数组非零元素之间的相对位置。要求in place进行。

第一种方法是双指针移动法,left和right指针一开始都置为起始元素,然后让right去遍历寻找非0元素。找到了非0元素后,就把两个指针的元素互换,使得后面的right指向的非0元素可以把left指向的0给替换掉。

交换完后,此时的left应该指向下一个0元素,所以left要自增。同时外层循环让right自增,继续去寻找下一个非0元素:

    public void moveZeroes(int[] nums) {
        int left = 0, right = 0;
        while (right < nums.length) {
            // right指针找到一个非0的数,就与left指针交换
            if (nums[right] != 0) {
                int tmp = nums[left];
                nums[left] = nums[right];
                nums[right] = tmp;
                // 交换后,left自增
                left++;
            }
            right++;
        }
        return;
    }

第二种方法是双指着压缩法:实际上就是将所有的非0数向前尽可能的压缩,最后把没压缩的那部分全置0就行了。比如103040,先压缩成134,剩余的3为全置为0。过程中需要一个指针记录压缩到的位置。

从前往后遍历数组,遇到一个非0数组,就往pos那个位置给填上,填完后pos就自增。然后外层循环的index也要自增。最后把pos之后的所有非0元素置为0:

    public void moveZeroes(int[] nums) {
        int pos = 0, index = 0;
        
        while (index < nums.length) {
            if (nums[index] != 0) {
                nums[pos++] = nums[index];
            }
            index++;
        }
        
        while (pos < nums.length) {
            nums[pos] = 0;
            pos++;
        }
    }

172. Remove Element

给定一个数组和一个整数elem,要求吧数组中所有等于elem的元素都删除,并返回新数组的长度。比如:Given an array [0,4,4,0,0,2,4,4], value=4 return 4 and front four elements of the array is [0,0,0,2]

这道题的解法和上道题的方法二是一模一样的。双指针压缩法,把所有不等于elem的元素往前压缩:

    public int removeElement(int[] A, int elem) {
        int pos = 0;
        for (int i = 0; i < A.length; i++) {
            if (A[i] != elem) {
                A[pos++] = A[i];
            }
        }
        return pos;
    }


100. Remove Duplicates from Sorted Array

给定一个有序数组,要求去除其中重复的元素。与其说是删除重复元素,倒不如说是把unique的元素全部放到前面。

这道题跟上面两道题有异曲同工之妙,基本思路是一致的。双指针压缩法。我把所有unique的元素尽量往前面放。

左指针pos用于放置元素,右指针i用于从前往后扫描。当扫到一个跟pos不同的元素时,我就可以把它放到pos后面。直到扫描完最后一个元素,这个时候数组的前pos+1个元素就是不包含重复元素的原数组的压缩版了:

    public int removeDuplicates(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int pos = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != nums[pos]) {
                pos++;
                nums[pos] = nums[i];
            }
        }
        return pos + 1;
    }


101. Remove Duplicates from Sorted Array II

跟上道题不同之处在于允许2次重复的出现。但是重复超过2次就不行了。基本思路还是一样的。

pre指针用于记录位置值以及放置unique元素,cur指针用于遍历,搜索与pre不同的元素。由于允许出现2次重复,所以需要一个变量count来统计重复次数。

因为数组是有序的,所以可以从前往后遍历的同时记录count。

如果cur和pre不同,则结束计数器(即count置为1),同时把后面那个跟pre不同的元素(即cur)移动到pre的后面。然后把pre指向下一个元素。

如果cur和pre相同,并且此时还可以继续计数,则统计重复次数(即count++)。并且把cur移动到pre的后面。然后把pre指向下一个元素。

外层循环则继续移动cur指针来遍历

    public int removeDuplicates(int[] nums) {
        i
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是一个快慢指针的 Java 代码示例,用于寻找链表的中间节点: ```java class ListNode { int val; ListNode next; ListNode(int val) { this.val = val; } } public ListNode findMiddle(ListNode head) { ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null) { slow = slow.next; // 慢指针每次移动一步 fast = fast.next.next; // 快指针每次移动两步 } return slow; // 返回中间节点 } ``` 在该代码中,我们使用了快慢两个指针 `slow` 和 `fast`,它们都从链表的头部开始移动。慢指针 `slow` 每次移动一步,而快指针 `fast` 每次移动两步。通过这种方式,当快指针 `fast` 到达链表末尾时,慢指针 `slow` 正好到达链表的中间位置。 下面是一个滑动窗口的 Java 代码示例,用于在数组中找到和大于等于给定目标值的最短子数组长度: ```java public int minSubArrayLen(int target, int[] nums) { int left = 0; int right = 0; int sum = 0; int minLen = Integer.MAX_VALUE; while (right < nums.length) { sum += nums[right]; // 右指针向右移动,并累加元素值 while (sum >= target) { minLen = Math.min(minLen, right - left + 1); // 更新最小长度 sum -= nums[left]; // 左指针向右移动,并减去元素值 left++; // 缩小窗口大小 } right++; // 扩大窗口大小 } return minLen == Integer.MAX_VALUE ? 0 : minLen; // 返回最短子数组长度 } ``` 在该代码中,我们使用了两个指针 `left` 和 `right` 来构成一个窗口,窗口的右边界由 `right` 控制。通过调整窗口的大小和位置,我们不断计算窗口内元素的和 `sum`,并与目标值进行比较。如果 `sum` 大于等于目标值,我们将左指针 `left` 向右移动,并减去窗口左侧的元素值,直到 `sum` 小于目标值。在此过程中,我们记录并更新窗口大小的最小值 `minLen`。最后返回最小值作为结果。 需要注意的是,滑动窗口算法适用于解决一些需要寻找满足特定条件的子数组或子串的问,可以通过调整窗口大小和位置来逐步逼近目标值。具体问中可能会有一些边界条件和特殊情况需要处理。以上代码示例仅供参考,实际应用中需要根据具体情况进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值