1.算法解释:
双指针主要用于遍历数组,两个指针指向不同的元素。如果两个指针指向同一个数组,但是遍历方向相反,则可以用来搜索。通常这个数组时排好序的。
2. 指针函数与函数指针:
指针函数是指返回类型为指针的函数,函数指针是指向函数的指针。可详细通过如下示例来理解。
//addition是指针函数,一个返回类型为指针的函数
int* additition(int a, int b) {
int* sum = a + b;
return sum;
}
int subtraction(int a, int b) {
return a - b;
}
//minus是函数指针,一个指向函数subtraction的指针
int (*minus) (int, int) = subtraction;
int operation(int x, int y, int (*func)(int, int)) {
return (*func)(x,y);
}
int* m = addititon(1, 2); //返回一个指针,指向的结果是3
int n = operation(3, *m, minus); //注意这里*操作符为解引用操作符,它返回指针m所指的地址所保存的值,也就是3。最终返回结果为3-3=0。
3. LeetCode 167 力扣
题目:在一个增序的整数数组里找到两个数,使它们的和为给定值。已知有且只有一对解。
思路:使用两个指针,一个指向开头,一个指向结尾。如果双指针指向的和为Target,则结束。如果小于Target,则头指针后移。如果大于Target,则尾指针前移。
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int i = 0;
int j = numbers.size()-1;
while (i<j) {
if (numbers[i] + numbers[j] < target) i++;
else if (numbers[i] + numbers[j] > target) j--;
else return vector{i+1, j+1};
}
return vector{i+1, j+1};
}
};
4. LeetCode 88 归并两个有序数组 力扣
题目:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你合并 nums2 到 nums1 中,使合并后的数组同样按非递减顺序排列。
思路:使用两个指针,分别指向nums1和nums2的原始数组的结尾。将指针指向的值中,较大的值copy到nums1[m+n-1]。注意遍历结束后,如果nums2的值没有被遍历到最开头,需要将剩下的值赋值到nums1。
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
while (m > 0 && n > 0) {
if (nums1[m-1] >= nums2[n-1]) {
nums1[m+n-1] = nums1[m-1];
m--;
}
else {
nums1[m+n-1] = nums2[n-1];
n--;
}
}
for (int i = 0; i < n; i++) { //为什么是n,如果m=0,n可能还是>0,则需要把nums2剩余的序列拷贝到nums1。也就是从0到n-1位置的元素。
nums1[i] = nums2[i];
}
}
};
5. LeetCode 142 快慢指针
题目:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
思路:使用快慢指针,快指针每次走两步,慢指针每次走一步。当快慢指针第一次相遇时,将快指针指向开头,然后让他慢走,第二次相遇即为环的入口。
证明:
在fast和slow第一次相遇的时候,假定slow走了n步,环路的入口是在p步,那么:
slow走的路径: p+c = n;(1) c为fast和slow相交点 距离环路入口的距离
fast走的路径: p+c+k*L = 2*n;(2), L为环路的周长,k是整数
fast从头开始走,步长为1. 当fast又走了p步时,slow也走了p步,slow在环上走了c+p步,而c+p=n, 而n是L的倍数(将(2)-(1)得k*L = n,说明n是L的倍数)。所以这时候就走到了入口处。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head;
ListNode *fast = head;
//第一次相遇,如果不相遇就已经走到尽头,表示无环,直接return null。
do {
if (!fast || !fast->next) return NULL;
slow = slow->next;
fast = fast->next->next;
}while (slow != fast); //因为最开始的时候fast和slow都被初始化为head,是相等的。所以需要先执行命令再判断条件,所以用do while而非while
fast = head;
while (fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
6. LeetCode最小覆盖子串 力扣
题目:给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
思路:首先是我们怎么找到涵盖t的子串? 使用双指针l和r, 它们最开始都指向起始位置,然后向右滑动r指针,直到l和r之间的子串,包含了所有的t的字符。但是这样的子串不一定是最小的。为了找到最小的,我们要尝试向右滑动l指针。如果[l+1, r]区间也包括所有t,那就可以直接继续滑动,如果滑动l导致果[l+1, r]区间不再包括所有t,则需要统计差哪个字符,然后向右滑动r。知道r到达s的最右边。
string minWindow(string s, string t) {
vector<bool> flags(128, false);
vector<int> counts(128,0);
for (int i=0; i<t.size();i++) {
counts[t[i]]++;
flags[t[i]] = true;
}
int cnt = 0, l = 0, min_l = 0, min_size = s.size()+1;
for (int r = 0; r < s.size(); r++) {
if (flags[s[r]]) {
if (counts[s[r]]-- > 0) cnt++;
while (cnt == t.size()) {
if (r-l+1 < min_size) {
min_l = l;
min_size = r-l+1;
}
if (flags[s[l]] && ++counts[s[l]] > 0) {
cnt--;
}
l++;
}
}
}
return min_size > s.size()? "":s.substr(min_l, min_size);
}