使用两个指针可以轻松的解决许多算法问题,归纳出如下几种
1、 判断链表是否带环
带环链表的判断是链表中经常考察的内容。一个循环链表可以无休止地遍历下去。我们可以定义两个指针,一个快指针一个慢指针,如果在遍历到尾端前二者相遇,那么链表就是有环链表
bool haveCycle(LinkList * Head) { if (!Head) { return false; } LinkList * fast=Head; LinkList * slow=Head; while (!fast) { fast=fast->next; if (!fast) { return false; } fast=fast->next; //快指针走两步 slow=slow->next; //慢指针走一步
if (fast==slow) //如果两指针相遇,则返回真 { return true; }
}
return false;
}
2、 求链表中倒数第K个节点
如果用普通的思路得遍历两遍链表,第一遍先求出链表的总长度N,然后第二遍走到第N-k个节点,这个节点就是所求的节点。如果链表很长,那么遍历两次的话就很费时,我们用两个指针的方法遍历一次链表即可,先让一个指针走K步,然后两个指针再一起走,直到第一个指针遍历到链表末尾。
LinkList * FindRevK(LinkList * Head,int k) { if (!Head) { return NULL; } LinkList * first=Head; LinkList * second=Head; for (int i=0;i<k;i++) //先让first走K步 { if (!first) { return NULL; //链表长度小于K则返回NULL } first=first->next; } while (first) { first=first->next; second=second->next; } return second; }
3、 二分法查找某个数
用经典的二分法在一个有序数组中可以以log(n)的时间复杂度查找出给定的数。同样也是设定两个位置下表,low与high。由于该方法大家都熟悉不过了,只把代码贴上来就行了
int search(int array[],int n,int value) { if (!array||n<0) { return -1; } int low=0; int high=n-1; while (low<high) { int med=low+(high-low)/2; //这样可以防止low+high发生溢出 if (array[med]<value) { low=med+1; } else if (array[med]>value) { high=med-1; } else return med; } return -1; }
4、 在一个有序数组中找出和为N的两个数
定义两个位置low和high,一个在开始处,一个在结尾处,如果二者之和大于N,high递减,如果二者之和小于N则low递增,直到和为N或者二者相遇为止
void findNum(int * array,int arraysize,int N,int & low,int & high) { low=0; high=arraysize-1; while (low<high) { if (low+high==N) { return; //和为N,返回 } else if (low+high<N) { low++; } else { high--; } } //没有两个数的和为N,则置下表为-1 low=-1; high=-1; }
5、 输入一个正数n,输出所有和为n连续正数序列
输入 15
输出
15=1+2+3+4+5
15=4+5+6
15=7+8我们可用两个数low和high分别表示序列的最小值和最大值。首先把low初始化为1,high初始化为2。如果从low到high的序列的和大于n的话,我们向右移动low,相当于从序列中去掉较小的数字。如果从low到high的序列的和小于n的话,我们向右移动high,相当于向序列中添加high的下一个数字。一直到low等于(1+n)/2,因为序列至少要有两个数字
void printResult(int low,int high,int num) //该函数实现将low到high之间的数输出到屏幕上 { cout<<num<<"="; for (int i=low;i<high;i++) { cout<<i<<"+"; } cout<<high<<endl; } void consecutiveN(int num) { int low=1,high=2; int sum=low+high; while (low<(num+1)/2) { if (sum==num) { printResult(low,high,num); //输出结果 sum-=low; low++; } while(sum<num) { high++; sum+=high; } while(sum>num) { sum-=low; low++; } } }
6、 输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分
如果不考虑时间复杂度,最简单的思路应该是从头扫描这个数组,每碰到一个偶数时,拿出这个数字,并把位于这个数字后面的所有数字往前挪动一位。挪完之后在数组的末尾有一个空位,这时把该偶数放入这个空位。由于碰到一个偶数,需要移动O(n)个数字,因此总的时间复杂度是O(n2)。
要求的是把奇数放在数组的前半部分,偶数放在数组的后半部分,因此所有的奇数应该位于偶数的前面。也就是说我们在扫描这个数组的时候,如果发现有偶数出现在奇数的前面,我们可以交换他们的顺序,交换之后就符合要求了。因此我们可以维护两个指针,第一个指针初始化为数组的第一个数字,它只向后移动;第二个指针初始化为数组的最后一个数字,它只向前移动。在两个指针相遇之前,第一个指针总是位于第二个指针的前面。如果第一个指针指向的数字是偶数而第二个指针指向的数字是奇数,我们就交换这两个数字
void Reorder(int *pData, unsigned int length, bool (*func)(int)); bool isEven(int n) { return (n & 1) == 0; //判断一个数字是不是偶数并没有用%运算符而是用&。理由是通常情况下位运算符比%要快一些 } void ReorderOddEven(int *pData, unsigned int length) { if(pData == NULL || length == 0) return; Reorder(pData, length, isEven); } void Reorder(int *pData, unsigned int length, bool (*func)(int)) { if(pData == NULL || length == 0) return; int *pBegin = pData; int *pEnd = pData + length - 1; while(pBegin < pEnd) { if(!func(*pBegin)) //从前半部分找出第一个偶数 { pBegin ++; continue; } if(func(*pEnd)) //从后半部分找出第一个奇数 { pEnd --; continue; } //二者交换 int temp = *pBegin; *pBegin = *pEnd; *pEnd = temp; } }
这道题有很多变种。这里要求是把奇数放在偶数的前面,如果把要求改成:把负数放在非负数的前面等,思路都是都一样的