lintcode-鏈表

來看第二題-兩個鏈表的交叉

題目連接:https://www.lintcode.com/problem/intersection-of-two-linked-lists/description

uploading.4e448015.gif正在上傳…重新上傳取消

要求我們找到兩鏈表最開始的交叉節點。

爲了方便解釋,我們以樣例1爲例,設兩個鏈表分別爲

a1->a2->c1->c2-c3

b1->b2->b3->c1->c2->c3

由於最開始的交叉節點在c1,所以會輸出c1

這裏我們的做法是在先分別計算兩個鏈表的長度,計算長度差值,然後較長的字符串走一段長度,該長度等於鏈表長度之差。然後兩個鏈表同時向後遍歷即可

uploading.4e448015.gif正在上傳…重新上傳取消

這裏由”//”表示的註釋是針對代碼的,/**/表示的註釋是針對實例的。

將代碼寫入後,進行測試

uploading.4e448015.gif轉存失敗重新上傳取消

輸出爲accepted,說明成功解決了該問題。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int list_length(struct ListNode *phead)//計算鏈表長度
{
   int length=0;
   struct ListNode *pnode=phead;
   while(pnode != NULL)//通過next遍歷鏈表,直到爲null說明鏈表遍歷完成,length的值即爲鏈表長度
   {
       length++;
       pnode=pnode->next;
   }
   return length;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int A_length=list_length(headA);
    int B_length=list_length(headB);
    int gap_length=A_length>B_length?A_length-B_length:B_length-A_length;//計算兩鏈表長度差值
    
    struct ListNode *plong=headA;
    struct ListNode *pshort=headB;
    
    if(A_length<B_length)//plong指向長鏈表,pshort指向短鏈表
    {
        plong=headB;
        pshort=headA;
    }
    
    for(int i=0;i<gap_length;i++) plong=plong->next;  //長鏈表多走length步
    /*以實例1而言,b1->b2->b3->c1->c2->c3多走1步,此時plong指向b2*/
    
    while(plong && pshort && plong->val != pshort->val)//兩個鏈表同時向後遍歷比較
    /*b1->b2->b3->c1->c2->c3從b2開始,a1->a2->c1->c2-c3從a1開始,如果plong的數值域不等於pshort的數值域則繼續通過next遍歷*/
    {
        plong=plong->next;
        pshort=pshort->next;
    }/*plong,pshort的數值域爲c1時相同,故返回c1*/
    return plong;
}

 

 

 

 

第三題我們來學習lintcode上的一道題目-旋轉鏈表。

題目鏈接:

https://www.lintcode.com/problem/rotate-list/description

uploading.4e448015.gif正在上傳…重新上傳取消

題目給定一個鏈表,旋轉鏈表,使得每個結點向右移動k個位置,其中k是一個非負數。

 

使用很簡單,我們在下圖紅框處寫入代碼就可以了

uploading.4e448015.gif正在上傳…重新上傳取消

這種oj形式的好處在於更注重算法與數據結構的本質,不需要我們寫其他無關的函數,宏,結構體等等,而只需要我們注重解決問題。而且,oj給出的一些特殊的輸入例子來測試代碼時,有助於我們在編程時培養嚴密謹慎的思維。在後續的課程中,大部分題目都會以oj的形式,和大家一起學習,選擇有特色的、典型的題目進行分析講解。

oj的背景介紹好了,接下來我們分析這道題目。

以題目給出的1->2->3->4->5->null,k=2,輸出爲4->5->1->2->3->NULL

這裏的關鍵在於看出:輸出的鏈表實際上是由兩部分組成的,4->5,1->2->3->NULL

那麼我們直觀的想法就是找到第一個字符串4->5的頭結點,尾結點;而4的前驅結點的後繼指向null,此爲第二個字符串

然後將這兩個字符串連接在一起即可。

那麼關鍵就是如何找到第一個字符串的頭結點。

實際上,我們注意到這和k以及鏈表長度len有關。K mod n,如果結果爲0,則旋轉鏈表後實際上是不變的,直接返回即可。否則的話我們需要計算從原字符串頭結點走到新的第一個字符串的頭結點的步數,其值爲鏈表長度-(k mod n)-1。

我們來看代碼及註釋:

uploading.4e448015.gif正在上傳…重新上傳取消

這裏由”//”表示的註釋是針對代碼的,/**/表示的註釋是針對實例的。

代碼在1-2.c

將代碼寫在紅框中後,點擊右下角的“運行測試數據”

uploading.4e448015.gif正在上傳…重新上傳取消

右側顯示通過,說明我們的代碼是沒問題的

struct ListNode* rotateRight(struct ListNode* head, int k) {
    struct ListNode* p1;
    struct ListNode* p2;
    struct ListNode* p3;
    int len=1;
    p1=head;//p1,p2,p3初始化都指向頭結點
    p2=head;
    p3=head;
    //判斷如果鏈表爲空或者鏈表只有一個節點,則直接返回
    if(!head||!head->next)
        return head;
    while(p1->next)//該while循環用於計算鏈表長度,得到長度爲len
    {
        len++;
        p1=p1->next;
    }
    k=k%len; //k對len取模
    //如果k爲0,直接返回head,即旋轉鏈表前後實際上是不變的
    if(k==0)
        return head;
    //否則,首先計算出鏈表分界點所要走的步數
    len=len-k-1;/*以1->2->3->4->5爲例,經過計算後len=2*/
    while(len--)/*while終止時,p2指向3*/
        p2=p2->next;
    //記錄第一部分的頭結點
    p3=p2->next;/*p3指向4,此即新的第一個字符串的頭結點*/
    p1=p3;/*p1也指向4*/
    //找出第一部分的尾節點
    while(p1->next)/*while循環結束時,p1指向5,此即的第一個字符串的尾結點*/
        p1=p1->next;
    p2->next=NULL;/*讓3指向null*/
    p1->next=head;/*讓5指向了head,實際上就將兩個4->5和1->2->3->null連在了一起,從而實現了字符串的旋轉*/
    return p3;
}

 

 

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