文章目录
- 双指针
- [leetcode167 两数之和](https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/)
- 分析
- 题解
- [leetcode88 合并两个有序数组](https://leetcode.cn/problems/merge-sorted-array/description/)
- 分析
- 题解
- [leetcode142 环形链表](https://leetcode.cn/problems/linked-list-cycle-ii/description/)
- 分析
- 非公式分析
- 题解
- [leetcode76 最小覆盖子串](https://leetcode.cn/problems/minimum-window-substring/)
- 分析
- 题解
双指针
双指针用于处理数组和链表等线性结构。同时用2个指针遍历有序的数据结构,减少时间复杂度。
leetcode167 两数之和
分析
由于数组是有序的,因此两个指针,一个从前向后遍历,一个从后往前遍历,可以在O(n)时间复杂度完成查询。
假设结果为[i, j]。那么左指针遍历到i时,如果右指针大于j,那么和偏大,右指针左移,不会出现左指针右移的情况。直到右指针走到j,返回结果。右指针遍历到j也是同理,因此不会出现左指针大于i,右指针小于j的情况。
题解
class Solution {public int[] twoSum(int[] numbers, int target) {int first = 0; // 第一个指针int last = numbers.length - 1; // 第二个指针while (first < last) {int sum = numbers[first] + numbers[last];if (sum == target) {return new int[]{first + 1, last + 1};} else if (sum < target) {first++;} else {last--;}}return new int[]{-1, -1};}
}
leetcode88 合并两个有序数组
分析
2个有序数组,每个数组用一个指针遍历。因为合并结果存在nums1
数组,而nums1
数组的前半部分有值,后半部分是空的。所以与正向双指针相比,逆向双指针更快。
题解
class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {int tail = m + n - 1;int i = m - 1; // 第一个指针int j = n - 1; // 第二个指针int cur;while (i >= 0 || j >= 0) {if (i < 0) {cur = nums2[j];j--;} else if (j < 0) {cur = nums1[i];i--;} else if (nums1[i] > nums2[j]) {cur = nums1[i];i--;} else {cur = nums2[j];j--;}nums1[tail] = cur;tail--;}}
}
leetcode142 环形链表
分析
假设slow指针和fast指针均从head节点出发,每次slow移动1个节点,fast移动2个节点。它们相遇时的位置如图。a表示头节点到入环点的距离,b表示入环点到相遇点的距离,c表示相遇点到入环点的距离。
slow = a + b, fast = a + n(b + c) + b
其中n
表示fast节点在环中多走的次数。
fast = 2 * slow
,则
a + n(b + c) + b = 2(a + b)
,变换得到
(n - 1)(b + c) + c = a
,这个等式意味着,分别从相遇点出发和从头结点出发的两个节点终会在入环点相遇。
非公式分析
现在fast和slow在相遇点相遇,slow
走过的距离是x
。此时再来一个节点ptr
以slow
的速度从头结点走到相遇点,走过的距离也是x
。那么slow
在这期间走过的距离是多少?是2x
,恰好是之前fast
走过的距离。ptr
与slow
相遇的位置恰好是slow
与fast
相遇的位置。
由于slow
与ptr
在环里相遇,它们速度又相同,因此它们在环里的每个位置都相遇,自然包括入环点。
题解
public class Solution {public ListNode detectCycle(ListNode head) {ListNode slow = head;ListNode fast = head;while (fast != null && fast.next != null) {fast = fast.next.next; // 快指针slow = slow.next; // 慢指针if (slow == fast) {fast = head; // 不浪费指针,fast表示ptrwhile (fast != slow) {fast = fast.next;slow = slow.next;}return fast;}}return null;}
}
leetcode76 最小覆盖子串
分析
左右指针在同一个字符数组上,分别表示子串的左右边界。由于要求最小子串,因此先用右指针保证覆盖子串内容,再停止右指针并用左指针压缩子串范围。
题解
public static String minWindow(String s, String t) {char[] sArray = s.toCharArray();char[] tArray = t.toCharArray();int[] cnt = new int[128];int count = 0;for (char c : tArray) {cnt[c]++;count++;}int start = -1;int end = sArray.length;int l = 0;for (int r = 0; r < sArray.length; r++) {if (--cnt[sArray[r]] >= 0) {count--;}while (count == 0) {if (++cnt[sArray[l]] > 0) {if (r - l < end - start) {end = r;start = l;}count++;}l++;}}return start == -1 ? "" : s.substring(start, end + 1);}
}