双指针

EmiyaCC 于 2021-07-15 发布

快慢指针

主要解决链表中的一些问题,比如判断链表中是否右环,判断两个链表的相较节点

判断链表中是否有环

// https://leetcode-cn.com/problems/linked-list-cycle/
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) return false;
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) return true;
        }
        return false;
    }
}

返回环入口位置

// https://leetcode-cn.com/problems/linked-list-cycle-ii/
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null) return null;
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                fast = head;
                while (fast != slow) {
                    fast = fast.next;
                    slow = slow.next;
                }
                return fast;
            }
        }
        return null;
    }
}

简单证明:

设从head到环入口距离为a,环入口到fastslow相交点距离为bb到环入口为c ,则: \(\begin{align} fast &= a + n * (b + c) + b \\ slow &= a + m * (b + c) + b \\ \\ fast - slow &= (n - m) * (b + c) \\ &= slow = a + m * (b + c) + b \\ \\ a &= (n - 2m) * (b + c) - b \\ &= (n - 2m - 1) * (b + c) + c \end{align}\) 可以看出,a的距离等于多圈环距离加上c的距离,故可以分别让两个节点从头节点和fastslow相交点开始走,相交处就是环入口

寻找链表的中点

ListNode middleNode(ListNode head) {
    ListNode fast, slow;
    fast = slow = head;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    // slow 就在中间位置
    return slow;
}

需要注意的是,当链表的长度为奇数,slow恰好停在中间位置;而当链表的长度为偶数,slow的位置会在中间偏右

删除链表的倒数第 N 个结点

// https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode slow = dummy;
        ListNode fast = head;
        for (int i = 0; i < n; i ++) {
            fast = fast.next;
        }
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return dummy.next;
    }
}

左右指针

主要解决数组(字符串)中的问题,比如二分查找

两数之和 II - 输入有序数组

// https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int left = 0; 
        int right = numbers.length - 1;
        while (left < right) {
            if (numbers[left] + numbers[right] == target) {
                return new int[] {left + 1, right + 1};
            } else if (numbers[left] + numbers[right] < target) {
                left += 1;
            } else if (numbers[left] + numbers[right] > target) {
                right -= 1;
            }
        }
        return new int[] {};
    }
}

三数之和

// https://leetcode-cn.com/problems/3sum/
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int len = nums.length;
        List<List<Integer>> list = new ArrayList<>();
        if (len < 3) return list;
        Arrays.sort(nums);
        for (int i = 0; i < len - 2; i ++) {
            if (nums[i] > 0) break;
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int target = -nums[i];
            int start = i + 1;
            int end = len - 1;
            while (start < end) {
                if (nums[start] + nums[end] == target) {
                    list.add(new ArrayList<>(Arrays.asList(nums[i], nums[start], nums[end])));
                    start ++;
                    end --;
                    while (start < end && nums[start] == nums[start - 1]) start ++;
                    while (start < end && nums[end] == nums[end + 1]) end --;
                } else if (nums[start] + nums[end] > target) {
                    end --;
                } else {
                    start ++;
                }
            }
        }
        return list;
    }
}

四数之和

// https://leetcode-cn.com/problems/4sum/
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if (nums.length < 4 || nums == null) return res;
        Arrays.sort(nums);
        for (int i = 0; i < nums.length - 3; i ++) {
            if (nums[i] > target && nums[i] > 0) break;
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            for (int j = i + 1; j < nums.length - 2; j ++) {
                // 此处注意后面的值
                if (nums[j] > target - nums[i] && nums[i] > 0) break;
                // 此处 j 需要大于 i + 1
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                int tmpTarget = target - nums[i] - nums[j];
                int left = j + 1;
                int right = nums.length - 1;
                while (left < right) {
                    if (nums[left] + nums[right] == tmpTarget) {
                        res.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[left], nums[right])));
                        left ++;
                        right --;
                        while (left < right && nums[left] == nums[left - 1]) left ++;
                        while (left < right && nums[right] == nums[right + 1]) right --;
                    } else if (nums[left] + nums[right] > tmpTarget) {
                        right --;
                    } else {
                        left ++;
                    }
                }
            }
        }
        return res;
    }
}

反转字符串

// https://leetcode-cn.com/problems/reverse-string/)
class Solution {
    public void reverseString(char[] s) {
        int n = s.length;
        for (int i = 0; i < n / 2; i ++) {
            char tmp = s[i];
            s[i] = s[n - 1 - i];
            s[n - 1 - i] = tmp;
        }
    }
}