无意中看见了这篇帖子。感觉写的还不错。尤其是求链表中倒数第K个节点,其他技巧都是一些比较常见的思路了,不过也能带给我更多的思考。
源:http://blog.csdn.net/dlengong/article/details/7418420
使用两个指针可以轻松的解决许多算法问题,归纳出如下几种
1、 判断链表是否带环
带环链表的判断是链表中经常考察的内容。一个循环链表可以无休止地遍历下去。我们可以定义两个指针,一个快指针一个慢指针,如果在遍历到尾端前二者相遇,那么链表就是有环链表
- bool haveCycle(LinkList * Head)
- {
- if (!Head)
- {
- return false;
- }
- LinkList * fast=Head;
- LinkList * slow=Head;
- while (!fast)
- {
- if (fast==slow)
- {
- return true;
- }
- fast=fast->next;
- if (!fast)
- {
- return false;
- }
- fast=fast->next;
- slow=slow->next;
- }
- 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++)
- {
- if (!first)
- {
- return 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;
- 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;
- }
- else if (low+high<N)
- {
- low++;
- }
- else
- {
- high--;
- }
- }
-
- 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)
- {
- 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;
- }
- }
这道题有很多变种。这里要求是把奇数放在偶数的前面,如果把要求改成:把负数放在非负数的前面等,思路都是都一样的