資料結構演算法

風的王子發表於2013-11-04
一.判斷連結串列是否存在環型連結串列問題:

  說明:判斷一個連結串列是否存在環,例如下面這個連結串列就存在環,n1-->n2-->n3-->n4-->n5-->n2,環的開始結點是n5

  解法:這裡有個比較簡單的解法:設兩個指標p1,p2,每次迴圈p1向前走一步,之向前走兩步,直到p2碰到NULL指標(無環)或兩個指標相等結束迴圈演算法(有環),即如果兩個指標相等則說明存在環。

程式碼如下:

         

  1. /*節點資料結構*/  
  2. struct link{  
  3.     int data;  
  4.     link * next;  
  5. };  
  6.   
  7. /* 
  8. *判斷是否有環的方法 
  9. * 
  10. *引數來頭節點的指標 
  11. */  
  12. bool isLoop(link * head){  
  13.     //p1步長為1;p2步長為2  
  14.     link* p1 = head,*p2 = head;  
  15.       
  16.     //如果只有一個節點或兩個節點,直接return false  
  17.     if(head == NULL || head->next == NULL){  
  18.         return false;  
  19.     }  
  20.       
  21.     //迴圈前進,直到p2走到NULL或p2追上p1  
  22.     do{  
  23.         p1 = p1->next;  
  24.         p2 = p2->next->next;  
  25.     }while(p2 && p2->next && p1!=p2);  
  26.   
  27.     //如果有環  
  28.     if(p1 == p2){  
  29.         return true;  
  30.     else  
  31.         return false;  
  32. }  


二.連結串列反轉:

說明:連結串列反轉,比如原連結串列是1-->2-->3-->4-->5 通過反轉後成為5-->4-->3-->2-->1

解法一:利用三個指標,指向當前要反轉的節點的指標,這個當前節點之前的節點指標,這個當前節點之後的節點指標;反轉後再向後繼續遍歷

程式碼如下:

  1. /*節點資料結構*/  
  2. struct linka{  
  3.     int data;  
  4.     link * next;  
  5. };  
  6.   
  7. /* 
  8. *反轉 
  9. * 
  10. *引數:頭節點的指標的引用 
  11. */  
  12. bool reverse(link * &head){  
  13.     //只有一個節點,即不用反轉,直接返回  
  14.     if(head == NULL)  
  15.         return;  
  16.   
  17.     //定義3個輔助指標,pre指向當前要反轉的節點的前一個節點;cur為當前要反轉的節點;ne指向當前反轉的節點的下一個節點  
  18.     linka * pre,*cur,*ne;  
  19.     //初始化指標  
  20.     pre = head;  
  21.     cur = head->next;  
  22.     //迴圈,直到cur為NULL  
  23.     while(cur){  
  24.         ne = cur->next;  
  25.         cur->next = pre;  
  26.         pre = cur;  
  27.         cur = ne;  
  28.     }  
  29.       
  30.     //反轉到最後,設定頭節點指標  
  31.     head->next = NULL;  
  32.     head = pre;  
  33. }  
解法二:利用遞迴。這種方法的基本思想是在反轉當前節點之前先呼叫遞迴函式反轉後續節點。不過,這個方法有個缺點:在反轉後的最後一個節點會形成一個環,所以必須將函式的返回節點的next設為NULL.因為要改變head指標,所以我用了引用

程式碼如下:

  1. /*節點資料結構*/  
  2. struct linka{  
  3.     int data;  
  4.     link * next;  
  5. };  
  6.   
  7. /* 
  8. *反轉 
  9. * 
  10. *引數:頭節點的指標的引用 
  11. */  
  12. linka * reverse(linka * p,link * &head){  
  13.     if(p == NULL || p->next == NULL){  
  14.         head = p;  
  15.         return p;  
  16.     }else {  
  17.         linka* tmp = reverse(p->next,head);  
  18.         tmp->next = p;  
  19.         return p;     
  20.     }  
  21. }  

三.連結串列的合併:

說明:遞增有序的2個單連結串列合併成一個遞增有序的單連結串列,不用任何庫函式呼叫

程式碼如下:

  1. <pre name="code" class="cpp">#include <iostream>  
  2. using namespace std;  
  3.   
  4. /* 單連結串列節點 */  
  5. struct node{  
  6.     int value;  
  7.     node* next;  
  8. };  
  9.   
  10. /* 給單連結串列新增節點 */  
  11. void insertNode(node* head, int value){  
  12.     node* p = head->next;  
  13.     if ( p == NULL ){  
  14.        p = new node;  
  15.        p->value = value;  
  16.        p->next = NULL;  
  17.        head->next = p;  
  18.        return;  
  19.     }  
  20.   
  21.     while ( p->next != NULL ){  
  22.        p = p->next;  
  23.     }  
  24.     node* tmp = new node;  
  25.     tmp->value = value;  
  26.     tmp->next = NULL;  
  27.     p->next = tmp;  
  28. }  
  29.   
  30. /* 遍歷輸出連結串列節點 */  
  31. void print(node* head){  
  32.     node* p = head->next;  
  33.     while ( p != NULL ){  
  34.        cout << p->value << " ";  
  35.        p = p->next;  
  36.     }  
  37.     cout << endl;  
  38. }  
  39.   
  40.   
  41. /* 下面實現不使用任何庫函式, 利用交換的方法在原空間實現整體有序。 方法是先確定哪一個連結串列 
  42. 的第一個節點的值小,把這個連結串列的頭結點作為合併後連結串列的頭結點,然後比較2個有序連結串列的當前節點 
  43. 的值,如果代表最後合併連結串列的值小,則不用交換,否則把兩個值交換,最後合併連結串列始終保持兩個值中 
  44. 的小值。另一個連結串列由於交換了一個元素,當前元素可能影響該連結串列的有序遞增,對其進行調整使其保持 
  45. 遞增有序,然後重複上述動作,直到一個連結串列遍歷結束,然後把剩餘的連結串列連線起來就行。*/  
  46.   
  47. /* 調整連結串列的第一個節點,使其變成遞增有序 */  
  48. void chg2sort(node* head, node* &p){  
  49.   
  50.     if (head->next == NULL ){ //沒有節點,直接返回  
  51.         return;  
  52.     }  
  53.   
  54.     node* s = head;  
  55.     while ( s->next != p ){ //s指向p的前一個節點  
  56.        s = s->next;  
  57.     }  
  58.   
  59.     //下面的一段找到第一個大於p節點值的節點  
  60.     node* q = p;  
  61.     node* r = q;  
  62.       
  63.     while ( q != NULL ){  
  64.       
  65.        if ( q->value <= p->value ){  
  66.         r = q; //r始終指向q的前一個節點  
  67.         q = q->next;  
  68.        }else {  
  69.         break;  
  70.        }  
  71.     }  
  72.   
  73.     //下面調整指標,其實可以統一寫出來,為了閱讀清晰把q為NULL和非NULL分開寫出來  
  74.       
  75.     if ( q == NULL ){  
  76.        r->next = p;  
  77.        s->next = p->next;  
  78.        p->next = NULL;  
  79.     }else if ( q != NULL ){  
  80.        s->next = p->next;  
  81.        r->next = p;  
  82.        p->next = q;  
  83.     }  
  84.       
  85.     //由於連結串列進行了調換,當前連結串列指標也需要改變  
  86.     p = s->next;  
  87. }  
  88.   
  89.   
  90. /* 兩個有序連結串列進行合併 */  
  91. node* merge(node* head1, node* head2){  
  92.     node* head; //合併後的頭指標  
  93.     node* p = head1->next;  
  94.     node* q = head2->next;  
  95.   
  96.     //有一個連結串列為空的情況,直接返回另一個連結串列  
  97.     if ( p == NULL ){  
  98.        head = head2;  
  99.        return head;  
  100.     }else if ( q == NULL ){  
  101.        head = head1;  
  102.        return head;  
  103.     }  
  104.   
  105.     //兩個都不為空,先確定哪個連結串列作為合併後的連結串列  
  106.     if ( (p != NULL) && (q != NULL) ){  
  107.        if ( p->value < q->value ){  
  108.         head = head1;  
  109.        }else{  
  110.         head = head2;  
  111.        }  
  112.     }  
  113.   
  114.     node* p_prior; //始終指向p節點的前一個節點  
  115.     node* q_prior;  
  116.   
  117.     while ( (p != NULL) && (q != NULL) ){  
  118.        if ( p ->value < q->value ){  
  119.             if ( head == head1 ){//如果連結串列1的值小於連結串列2的值,連結串列1的指標向下指  
  120.                  p_prior = p;  
  121.                  p = p->next;  
  122.             }else if ( head == head2 ){  
  123.                  //進行當前節點值的交換  
  124.                  int tmp = p->value;  
  125.                  p->value = q->value;  
  126.                  q->value = tmp;  
  127.                  chg2sort(head1, p); //交換元素後的調整  
  128.                  q_prior = q;  
  129.                  q = q->next;  
  130.             }  
  131.        }else if ( p->value == q->value ){//連結串列1的值等於連結串列2時,兩連結串列指標都向下指  
  132.             p_prior = p;  
  133.             p = p->next;  
  134.             q_prior = q;  
  135.             q = q->next;  
  136.        }else if ( p->value > q->value ){  
  137.             if ( head == head1 ){//如果連結串列1的值大於連結串列2的值,交接兩節點的值後,排序連結串列2後,再下指  
  138.                  int tmp = p->value;  
  139.                  p->value = q->value;  
  140.                  q->value = tmp;  
  141.                  chg2sort(head2, q);  
  142.                  p_prior = p;  
  143.                  p = p->next;  
  144.             }else if ( head == head2 ){  
  145.                  q_prior = q;  
  146.                  q = q->next;  
  147.             }  
  148.        }  
  149.     }  
  150.   
  151.     if ( p != NULL ){  
  152.        q_prior->next = p;  
  153.     }  
  154.   
  155.     if ( q != NULL ){  
  156.        p_prior->next = q;  
  157.     }  
  158.   
  159.     return head;  
  160. }  
  161.   
  162. int main(){  
  163.     /* 建立有序連結串列A */  
  164.     int a[5] = {1, 5, 8, 10, 20};  
  165.     node* headA = new node;  
  166.     headA->next = NULL;  
  167.       
  168.     for (int i = 0; i < 5; ++i){  
  169.        insertNode(headA, a[i]);  
  170.     }  
  171.       
  172.     print(headA);  
  173.   
  174.     /* 建立有序連結串列B */  
  175.     int b[3] = {3, 4, 9};  
  176.     node* headB = new node;  
  177.     headB->next = NULL;  
  178.       
  179.     for (int i = 0; i < 3; ++i){  
  180.        insertNode(headB, b[i]);  
  181.     }  
  182.       
  183.     print(headB);  
  184.   
  185.     head = merge(headA, headB);  
  186.     print(head);  
  187.       
  188.     return 0;  
  189. }</pre>  
  190. <pre></pre>  
  191. <pre></pre>  
  192. <p>如果可以用庫函式合併的話:程式碼如下:</p>  
  193. <p></p>  
  194. <pre name="code" class="cpp">Node* mergeAction(Node* head1, Node* head2){  
  195. //兩個連結串列的合併操作   
  196.         Node* head=(Node*)malloc(sizeof(Node));   
  197.         Node* q=head; //q指向合併後的連結串列的最後一個  
  198.         while(head1 && head2){ //  
  199.                 if(head1->data<=head2->data){   
  200.                         Node* p=(Node*)malloc(sizeof(Node)); //p是新生節點的指標  
  201.                         p->data=head1->data;   
  202.                         p->next=NULL;   
  203.                         q->next=p;   
  204.                         q=q->next;   
  205.                         head1=head1->next;   
  206.                 }   
  207.                 else if(head1->data > head2->data){   
  208.                         Node* p=(Node*)malloc(sizeof(Node));   
  209.                         p->data=head2->data;   
  210.                         p->next=NULL;   
  211.                         q->next=p;   
  212.                         q=q->next;   
  213.                         head2=head2->next;   
  214.                 }   
  215.         }   
  216.         return head->next;   
  217. }</pre><br>  
  218. <br>  
  219. <p></p>  
  220. <pre></pre>  
  221. <p><strong>四.判斷兩個陣列中是否存在相同的數字,給定兩個排好序的資料,怎麼高效得判斷這兩個陣列中存在相同的數字:</strong></p>  
  222. <p><span style="white-space:pre"></span><strong>說明:</strong>O(n)演算法,因為兩個陣列都是排序好的,所以只要一次遍歷就行了,首先設兩個下標,分別初始化為兩個陣列的起始地址,依次向前推進,推進的規則是比較兩個陣列中的數字,小的那個陣列的下標各前推進一步,直到任何一個陣列的下標到達陣列末尾時,如果這時還沒碰到相同的數字,說明陣列中沒有相同的數字。</p>  
  223. <p><span style="white-space:pre"></span><strong>程式碼如下:</strong></p>  
  224. <p></p><pre name="code" class="cpp">bool findcommon2(int a[], int size1,int b[],int size2){  
  225.     int i = 0, j = 0;  
  226.       
  227.     while(i<size1 && j<size2){  
  228.         if(a[i]==b[j])  
  229.             return true;  
  230.           
  231.         if(a[i]>b[j])  
  232.             j++;//j標記b陣列  
  233.           
  234.         if(a[i]<b[j])  
  235.             i++;//i標記a陣列  
  236.     }  
  237.       
  238.     return false;  
  239. }</pre><br>  
  240. <strong>五.按單詞反轉字串:</strong><p></p>  
  241. <p><span style="white-space:pre"></span><strong>說明:</strong>單詞用空格分開,如,Here is blog.csdn.net/wufenglong 經過反轉後變為:blog.csdn.net/wufenglong is Here如果只是簡單的將所有字串翻轉的話,可以遍歷字串,將第一個字元和最後一個交換,第二個和倒數第二個交換,依次迴圈。其實按照單詞反轉的話可以在第一遍遍歷的基礎上,再遍歷一遍字串,對每一個單詞再反轉一次,這樣每個單詞又恢復了原來的順序</p>  
  242. <p><span style="white-space:pre"></span><strong>程式碼如下:</strong></p>  
  243. <p></p><pre name="code" class="cpp">char * reverse_word(const char *str){  
  244.     int len = strlen(str);  
  245.     char * restr = new char[len+1];  
  246.       
  247.     strcpy(restr,str);  
  248.       
  249.     //首尾交換,i是首的索引 j是尾的索引  
  250.     for(int i=0,j=len-1;i<j;i++,j--){  
  251.         char tmp = restr[i];  
  252.         restr[i] = restr[j];  
  253.         restr[j] = tmp;  
  254.     }  
  255.       
  256.     //再把每個單詞反轉  
  257.     int i,j,k = 0;  
  258.     while(k<len){  
  259.         i=j=k;  
  260.         while(restr[j]!=' ' && restr[j]!='\0')  
  261.             j++;//j為空格的索引  
  262.           
  263.         k = j+1;//k為  
  264.         j--;  
  265.           
  266.         //反轉單詞  
  267.         for(;i<j;i++,j--){  
  268.             char tmp = restr[i];  
  269.             restr[i] = restr[j];  
  270.             restr[j] = tmp;  
  271.         }  
  272.     }  
  273.       
  274.     return restr;  
  275. }</pre><br>  
  276. <strong>六.字串反轉:</strong><p></p>  
  277. <p><span style="white-space:pre"></span><strong>題意:</strong>給定一個字串,一個這個字串的子串,將第一個字串反轉,但保留子串的順序不變。</p>  
  278. <p>例如:輸入 每一個串 “this is wufl's Chinese site: http://blog.csdn.net/wufenglong”</p>  
  279. <p>子串:“wufl”</p>  
  280. <p>輸出: gnolgnefuw/tne.ndsc/golb//:ptth:eits esenihC s'wufl si siht</p>  
  281. <p><span style="white-space:pre"></span><strong>說明:</strong>一般的方法是先掃描一邊第一個字串,然後用stack把它反轉,同時記錄下子串出現的位置。然後再掃描一遍把記錄下來的子串再用stack反轉,我用的方法是用一遍掃描陣列的方法,掃描中如果發現子串,就將子串倒過來壓入堆疊。</p>  
  282. <p><strong><span style="white-space:pre"></span>程式碼如下:</strong></p>  
  283. <p></p><pre name="code" class="cpp">#include <stack>  
  284. using namespace std;  
  285.   
  286. //reverse the string 's1' ,the substring 'token'  
  287. const char * reverse(const char * s1, const char * token){  
  288.     assert(s1 && token);  
  289.       
  290.     stack<char> stack1;  
  291.       
  292.     const char * ptoken = token, *head = s1, *rear =s1;  
  293.       
  294.     while(*head !=''){  
  295.         while(*head !='' && *ptoken == *head){  
  296.             ptoken++;  
  297.             head++;  
  298.         }  
  299.           
  300.         if(*ptoken==''){  
  301.             const char *p;  
  302.               
  303.             for(p=head-1;p>=rear;p--){  
  304.                 stark1.push(*p);  
  305.             }  
  306.               
  307.             ptoken = token;  
  308.             rear = head;  
  309.         }else{  
  310.             stack1.push(*rear);  
  311.             head = ++rear;  
  312.             ptoken = token;  
  313.         }  
  314.     }  
  315.       
  316.     char * returnV = new char[strlen(s1)+1];  
  317.     int i=0;  
  318.       
  319.     while(!stack1.empty()){  
  320.         returnV[i++] = stack1.top();  
  321.         stack1.top();  
  322.     }  
  323.       
  324.     returnV[i]="";  
  325.     return returnV;  
  326. }</pre><br>  
  327. <br>  
  328. <p></p>  
  329. <p><br>  
  330. </p>  
  331. <p><span style="white-space:pre"></span></p> 

相關文章