双指针
算法解释
双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多个数组的多个指针。
若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索。
若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。
对于 C++ 语言,指针还可以玩出很多新的花样。一些常见的关于指针的操作如下:
指针与常量
int x;
int *p1 = &x; // 指针可以被修改,值也可以被修改
const int *p2 = &x; // 指针可以被修改,值不可以被修改(const int)
int *const p3 = &x; // 指针不可以被修改(*const),值可以被修改
const int *const p4 = &x; // 指针不可以被修改,值也不可以被修改
指针函数与函数指针
// addition是指针函数,一个返回类型是指针的函数
int* addition(int a, int b) {
int *sum = new int(a + b);
return sum;
}
int subtraction(int a, int b) {
return a - b;
}
int operation(int x, int y, int (*func)(int, int)) {
return (*func)(x,y);
}// minus是函数指针,指向函数的指针
int (*minus)(int, int) = subtraction;
int *m = addition(1, 2);
int n = operation(3, *m, minus);
Two Sum
两数之和II
给你一个下标从 1 开始的整数数组 numbers,该数组已按非递减顺序排列,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2],则 1 <= index1 < index2 <= numbers.length。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和index2。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
采用方向相反的双指针遍历数组,因为已经排好序,所以如果两个数之和小于目标值,则左指针右移,若大于目标值,则右指针左移。
代码
1 | vector<int> twoSum(vector<int>& numbers, int target) { |
归并有序数组
合并两个有序数组
给你两个按非递减顺序排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n,分别表示nums1 和 nums2 中的元素数目。
请你合并 nums2 到 nums1 中,使合并后的数组同样按非递减顺序排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0,应忽略。nums2 的长度为 n。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
注意:因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
因为这两个数组已经排好序,我们可以把两个指针分别放在两个数组的末尾,即 nums1 的 m−1 位和 nums2 的 n−1 位。每次将较大的那个数字复制到 nums1 的后边,然后向前移动一位。
因为我们也要定位 nums1 的末尾,所以我们还需要第三个指针 pos,以便复制。
注意:如果 nums1 的数字已经复制完,不要忘记把 nums2 的数字继续复制;如果 nums2 的数字已经复制完,剩余 nums1 的数字不需要改变,因为它们已经被排好序。
代码
1 | void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) { |
快慢指针
环形链表II
给定一个链表的头节点 head,返回链表开始入环的第一个节点。如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
链表定义如下:
1 | struct ListNode { |
不允许修改链表。
示例:
输入:
head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
对于链表找环路的问题,有一个通用的解法——快慢指针(Floyd 判圈法)。给定两个指针,分别命名为 slow 和 fast,起始位置在链表的开头。每次 fast 前进两步,slow 前进一步。如果 fast 可以走到尽头,那么说明没有环路;如果 fast 可以无限走下去,那么说明一定有环路,且一定存在一个时刻 slow 和 fast 相遇。当 slow 和 fast 第一次相遇时,我们将 fast 重新移动到链表开头,并让 slow 和 fast 每次都前进一步。当 slow 和 fast 第二次相遇时,相遇的节点即为环路的开始点。
代码
1 | ListNode *detectCycle(ListNode *head) { |
滑动窗口
最小覆盖子串
给你一个字符串 s、一个字符串 t。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “”。s,t只由英文字母组成。
注意:对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:”BANC”
示例 2:
输入:s = “a”, t = “a”
输出:”a”
示例 3:
输入: s = “a”, t = “aa”
输出: “”
本题使用滑动窗口求解,即两个指针 l 和 r 都是从最左端向最右端移动,且 l 的位置一定在 r 的左边或重合。先统计 t 中字符数量,滑动窗口至包含所有字符,l 左移,得到最短子串。
代码
1 | string minWindow(string S, string T) { |
练习
平方数之和
给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a^2^ + b^2^ = c 。
示例 1:
输入:c = 5
输出:true
示例 2:
输入:c = 3
输出:false
双指针,一个指向0,另一个指向根号c取整,左指针向右移或者右指针向左移,就可以避免双重循环解决问题。
代码
1 | bool judgeSquareSum(int c) { |
验证回文字符串II
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
示例 1:
输入: s = “aba”
输出: true
示例 2:
输入: s = “abca”
输出: true
示例 3:
输入: s = “abc”
输出: false
双指针,一个在开头,一个在末尾,相向移动,不符合的字符记录下来即可。
代码
1 | bool checkPalindrome(const string &s, int low , int high) { |
删除字母匹配字符串
给你一个字符串 s 和一个字符串数组 dictionary,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。均只由小写英文字母组成。
如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。
示例 1:
输入:s = “abpcplea”, dictionary = [“ale”,”apple”,”monkey”,”plea”]
输出:”apple”
示例 2:
输入:s = “abpcplea”, dictionary = [“a”,”b”,”c”]
输出:”a”
双指针,一个用于遍历 s,另一个遍历 d 中的字符串,记录满足题目要求的字符串即可。
代码
1 | string findLongestWord(string s, vector<string>& dictionary) { |
至多包含 k 个不同字符的最长子串
给定一个字符串 s,找出至多包含 k 个不同字符的最长子串 T 的长度。
示例 1:
输入:s = “eceba”, k = 2
输出:3
示例 2:
输入:s = “aa”, k = 1
输出:2
滑动窗口。
代码
1 | int lengthOfLongestSubstringKDistinct(string s, int k) { |
因为这题是付费的,所以我并不确定是否完全AC,仅供参考
