这里双指针指的是在同一个可迭代对象a中使用两个迭代器(下面用i,j)。
反正就要能类似数组这样使用索引i, j来得到对应的值a[i], a[j],然后通过移动i跟j达到目的。下面以数组为例
常见的使用方法有几种:
1. i跟j在数组的两边开始,用来把数组分成两部分
这个我们早遇到过了,我第一次遇见它被用来分成大于最左边的数(基准数)跟小于最左边的数两部分。不过它结合了分治法导致你可能没发现,它就是快速排序。当然快速选择算法中也是同样。记得注意是括号里是取小于等于,因为我们只把右边小于(或大于)的数丢到另一边。
//双指针分组:将数组分成>=temp以及<=temp的两部分
int quickSort(int a[], int left, int right)
{
int i = left, j = right;
int temp = a[left];
while(i < j)
{
// 这个可以不需要=,但浪费时间,比起换来换去排序好,不如不动就排序好
while(a[j] >= temp && i < j)
j--;
// i必须有=,否则才第一个就不会动
while(a[i] <= temp && i < j)
i++;
if(i < j)
{
//交换两个数的函数,可以自己编写或者加入algorithm头文件使用
swap(a[i], a[j]);
}
}
swap(a[left], a[i]);
return i;
}
//分治法
void quick(int a[], int left, int right)
{
if(left >= right)
return;
int mid = quickSort(a, left, right);
quick(a, left, mid-1);
quick(a, mid + 1, right);
}
当然,它还可以用来分奇偶数。
void division(int a[], int n)
{
int i = 0, j = n - 1;
while(true)
{
while(i < j && a[i] % 2 == 1) i++;
while(i < j && a[j] % 2 == 0) j--;
if(i < j)
{
swap(a[i], a[j]);
}
}
}
2. 快慢指针:求不定长数组(链表)的中间元素(1/3,1/4都一样)
思路:使用两个指针,快指针速度是慢指针的2倍(若1/3则3倍,1/4则4倍),当快指针到达终点时,慢指针到达所求的点
List middle(List)
{
List qucik = a;
List slow = a;
while(quick != NULL && quick->next != NULL)
{
qucik = quick->next->next;
slow = slow->next;
}
return slow;
}
3. 固定距离指针: 求距离n的点
比如,求链表倒数第n个节点
思路:使用两个指针,一个先走n步,然后一起走
List tailN(List a, int n)
{
List quick = a;
List slow = a;
for(int i = 0; i < n; i++)
{
quick = quick->next;
}
while(quick != NULL)
{
quick = quick->next;
slow = slow->next;
}
return slow;
}
4.两边微调:有序数组找到两个数的和
比如: 一个递增序列中,找到两个数的和为xx,返回他们的坐标
思路:由于是递增的,因此使用两个指针,一个左边i一个右边j,小于xx则让i向右,大于xx则让j向左。
Result sumExist(int a[],int n, int sum)
{
int i = 0, j = n - 1;
Result result;
while(i < j)
{
if(a[i] + a[j] < sum)
i++;
else if (a[i] + a[j] > sum)
j--;
else
result.add(i);
result.add(j);
return result;
}
return NULL;
}