兩個指針之美(筆試)

兩個指針之美(筆試)

轉自:http://blog.163.com/linux___/blog/static/20643000520124194322107/

使用兩個指針可以輕鬆的解決許多算法問題,歸納出如下幾種

1、  判斷鏈表是否帶環

帶環鏈表的判斷是鏈表中經常考察的內容。一個循環鏈表可以無休止地遍歷下去。我們可以定義兩個指針,一個快指針一個慢指針,如果在遍歷到尾端前二者相遇,那麼鏈表就是有環鏈表

  1. bool haveCycle(LinkList * Head) 
  2.     if (!Head) 
  3.     { 
  4.         return false
  5.     } 
  6.     LinkList * fast=Head; 
  7.     LinkList * slow=Head; 
  8.     while (!fast) 
  9.     { 
  10.         if (fast==slow) //如果兩指針相遇,則返回真 
  11.         { 
  12.             return true
  13.         } 
  14.         fast=fast->next; 
  15.         if (!fast) 
  16.         { 
  17.             return false
  18.         } 
  19.         fast=fast->next;   //快指針走兩步 
  20.         slow=slow->next;   //慢指針走一步 
  21.     } 
  22.     return false

 


2、  求鏈表中倒數第K個節點

如果用普通的思路得遍歷兩遍鏈表,第一遍先求出鏈表的總長度N,然後第二遍走到第N-k個節點,這個節點就是所求的節點。如果鏈表很長,那麼遍歷兩次的話就很費時,我們用兩個指針的方法遍歷一次鏈表即可,先讓一個指針走K步,然後兩個指針再一起走,直到第一個指針遍歷到鏈表末尾。

  1. LinkList * FindRevK(LinkList * Head,int k) 
  2.     if (!Head) 
  3.     { 
  4.         return NULL; 
  5.     } 
  6.     LinkList * first=Head; 
  7.     LinkList * second=Head; 
  8.     for (int i=0;i<k;i++) //先讓first走K步 
  9.     { 
  10.         if (!first) 
  11.         { 
  12.             return NULL; //鏈表長度小於K則返回NULL 
  13.         } 
  14.         first=first->next; 
  15.     } 
  16.     while (first) 
  17.     { 
  18.         first=first->next; 
  19.         second=second->next; 
  20.     } 
  21.     return second; 

 


3、  二分法查找某個數

用經典的二分法在一個有序數組中可以以log(n)的時間複雜度查找出給定的數。同樣也是設定兩個位置下表,lowhigh。由於該方法大家都熟悉不過了,只把代碼貼上來就行了

  1. int search(int array[],int n,int value) 
  2.     if (!array||n<0) 
  3.     { 
  4.         return -1; 
  5.     } 
  6.     int low=0; 
  7.     int high=n-1;    
  8.     while (low<high) 
  9.     { 
  10.         int med=low+(high-low)/2; //這樣可以防止low+high發生溢出 
  11.         if (array[med]<value) 
  12.         { 
  13.             low=med+1; 
  14.         } 
  15.         else if (array[med]>value) 
  16.         { 
  17.             high=med-1; 
  18.         } 
  19.         else 
  20.             return med; 
  21.     } 
  22.     return -1; 

 

4、  在一個有序數組中找出和爲N的兩個數

定義兩個位置lowhigh,一個在開始處,一個在結尾處,如果二者之和大於Nhigh遞減,如果二者之和小於Nlow遞增,直到和爲N或者二者相遇爲止

  1. void findNum(int * array,int arraysize,int N,int & low,int & high) 
  2.     low=0; 
  3.     high=arraysize-1; 
  4.     while (low<high) 
  5.     { 
  6.         if (low+high==N) 
  7.         { 
  8.             return//和爲N,返回 
  9.         } 
  10.         else if (low+high<N) 
  11.         { 
  12.             low++; 
  13.         } 
  14.         else 
  15.         { 
  16.             high--; 
  17.         } 
  18.     } 
  19.     //沒有兩個數的和爲N,則置下表爲-1 
  20.     low=-1; 
  21.     high=-1; 

 

5、  輸入一個正數n,輸出所有和爲n連續正數序列

輸入 15
輸出
15=1+2+3+4+5
15=4+5+6
15=7+8

我們可用兩個數lowhigh分別表示序列的最小值和最大值。首先把low初始化爲1high初始化爲2。如果從lowhigh的序列的和大於n的話,我們向右移動low,相當於從序列中去掉較小的數字。如果從lowhigh的序列的和小於n的話,我們向右移動high,相當於向序列中添加high的下一個數字。一直到low等於(1+n)/2,因爲序列至少要有兩個數字

  1. void printResult(int low,int high,int num)  //該函數實現將low到high之間的數輸出到屏幕上 
  2.     cout<<num<<"="
  3.     for (int i=low;i<high;i++) 
  4.     { 
  5.         cout<<i<<"+"
  6.     } 
  7.     cout<<high<<endl; 
  8. void consecutiveN(int num) 
  9.     int low=1,high=2
  10.     int sum=low+high; 
  11.     while (low<(num+1)/2
  12.     { 
  13.         if (sum==num) 
  14.         { 
  15.             printResult(low,high,num);//輸出結果 
  16.             sum-=low; 
  17.             low++; 
  18.         } 
  19.         while(sum<num) 
  20.         { 
  21.             high++; 
  22.             sum+=high; 
  23.         } 
  24.         while(sum>num) 
  25.         { 
  26.             sum-=low; 
  27.             low++; 
  28.         } 
  29.     } 

 

6、 輸入一個整數數組,調整數組中數字的順序,使得所有奇數位於數組的前半部分,所有偶數位於數組的後半部分

如果不考慮時間複雜度,最簡單的思路應該是從頭掃描這個數組,每碰到一個偶數時,拿出這個數字,並把位於這個數字後面的所有數字往前挪動一位。挪完之後在數組的末尾有一個空位,這時把該偶數放入這個空位。由於碰到一個偶數,需要移動O(n)個數字,因此總的時間複雜度是O(n2)

要求的是把奇數放在數組的前半部分,偶數放在數組的後半部分,因此所有的奇數應該位於偶數的前面。也就是說我們在掃描這個數組的時候,如果發現有偶數出現在奇數的前面,我們可以交換他們的順序,交換之後就符合要求了。因此我們可以維護兩個指針,第一個指針初始化爲數組的第一個數字,它只向後移動;第二個指針初始化爲數組的最後一個數字,它只向前移動。在兩個指針相遇之前,第一個指針總是位於第二個指針的前面。如果第一個指針指向的數字是偶數而第二個指針指向的數字是奇數,我們就交換這兩個數字

  1. void Reorder(int *pData, unsigned int length,bool (*func)(int)); 
  2. bool isEven(int n) 
  3.     return (n & 1) == 0; //判斷一個數字是不是偶數並沒有用%運算符而是用&。理由是通常情況下位運算符比%要快一些 
  4. void ReorderOddEven(int *pData, unsigned int length) 
  5.     if(pData == NULL || length == 0) 
  6.         return;  
  7.     Reorder(pData, length, isEven); 
  8. void Reorder(int *pData, unsigned int length,bool (*func)(int)) 
  9.     if(pData == NULL || length == 0) 
  10.         return;  
  11.     int *pBegin = pData; 
  12.     int *pEnd = pData + length - 1;  
  13.     while(pBegin < pEnd) 
  14.     { 
  15.         if(!func(*pBegin)) //從前半部分找出第一個偶數 
  16.         { 
  17.             pBegin ++; 
  18.             continue
  19.         } 
  20.         if(func(*pEnd))  //從後半部分找出第一個奇數 
  21.         { 
  22.             pEnd --; 
  23.             continue
  24.         } 
  25.         //二者交換 
  26.         int temp = *pBegin; 
  27.         *pBegin = *pEnd; 
  28.         *pEnd = temp; 
  29.     } 

 

這道題有很多變種。這裏要求是把奇數放在偶數的前面,如果把要求改成:把負數放在非負數的前面等,思路都是都一樣的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章