鍊錶的經典問題

2021-09-06 19:48:00 字數 3671 閱讀 6842

如果兩個單鏈表相交,那應該呈「y」字形,也就是從交點以後的部分是兩個鍊錶的公共節點。

所以,判斷是否相交只要看兩個鍊錶的最後乙個節點是否為同乙個即可。

那麼如何找到交點呢?設兩個單鏈表的長度分別為l1、l2,(假設l1>l2),則(l1-l2)的值就是交匯之前兩個鍊錶的長度差;

因此,只有讓更長的鍊錶先走l1-l2步,然後兩個鍊錶開始一起走,如果某次走到乙個相同的節點,該節點即為交點。

c**實現:

typedef struct

_listnode listnode;

static

int getlistlength(listnode *t)

returnn;}

static listnode* findfirstcommonnode(listnode *t1, listnode *t2)

for (i=0; i)

while (t1 && t1 !=t2)

return

t1;

}

該問題還有一種思路,就是將其中乙個鍊錶首尾相連,然後檢測另外乙個鍊錶是否有環,如果存在環,則兩個鍊錶相交。

如果乙個單鏈表有環,那應該呈「6」字形。

設定兩個指標(fast, slow),初始值都指向頭節點,slow每次前進一步,fast每次前進二步,如果鍊錶存在環,則fast必定先進入環,而slow後進入環,兩個指標必定 相遇:如果鍊錶是呈"o"字形,則slow剛好遍歷完一次的時候,與fast相遇;如果呈「6」字形,則更早相遇。

當fast若與slow相遇時,slow還沒有遍歷完鍊錶,而fast已經在環內迴圈了n圈(1<=n)。假設slow走了s步,則 fast走了2s步(fast步數還等於s 加上在環上多轉的n圈),設環長為r,則:

2s = s + nr,簡化為 s= nr

s = x + y,x為鍊錶起點到環入口點的距離,y是slow在環內走過的距離;

可以得到 x = y - s = y - nr,從煉表頭、相遇點分別設乙個指標(p1, p2),每次各走一步,當p1走過距離x時到達入口點,而p2走過的距離為y-nr,y是相遇點與入口點的距離,因此y也走到了入口點,也就是說p1、p2在環入口點相遇了。

c**實現:

static listnode* findloopport(listnode *head)  

if (fast == null || fast->next ==null)

return

null;

//找到環入口點

slow =head;

while (slow !=fast)

return

slow;

}

設定兩個指標(fast, slow),初始值都指向頭節點,slow每次前進一步,fast每次前進二步,當fast走到末尾時,slow剛好指向中間節點。

思路:用兩個指標,指標p1先走k-1步,然後指標p2才開始走,當指標p1遍歷完煉表時,p2還剩k個結點沒有遍歷。

實現如下:

listnode *findlastknode(listnode *head, int k)

return t2; }

在鍊錶中刪除乙個結點,最常規的做法是遍歷鍊錶,找到要刪除的結點後再刪除,這種做法的時間複雜度是o(n);

換一種思路,根據待刪除結點a,可以知道其下乙個結點是b=a->next,將結點b值拷貝給a,然後刪除b即可。

這種方法需要考慮一種特殊情況,a如果是尾結點,則b不存在,此時仍需要遍歷鍊錶一次。

c**實現:

void deletenode(listnode* head, listnode *pdel)

else

}

方法一:從頭到尾遍歷鍊錶,每經過乙個結點的時候,把該結點放到乙個棧中;當遍歷完整個鍊錶後,再從棧頂開始輸出結點的值。

該方法需要維護乙個額外的棧,實現起來比較麻煩。我們注意到遞迴本質上就是乙個棧結構,所以,也可以用遞迴來實現反向輸出鍊錶。

方法二:也就是說,每訪問到乙個結點的時候,先遞迴輸出它後面的結點,再輸出該結點自身。

c**實現:

void reverseprint(listnode *head)

printf(

"%d

", head->data);}}

利用輔助指標就地修改節點的next域,**如下:

static listnode *reverselist(listnode *head)

return

head;

}

遞迴 的實現方法:

static

void reverselist2(listnode**head)

歸併排序實現的時間複雜度為 nlgn,

struct listnode* merge(struct listnode* p1, struct listnode *p2)  

else

}struct listnode* sortlist(struct listnode*head)

p->next =null;

h1 =sortlist(p1);

h2 =sortlist(head);

return

merge(h1, h2);

}

以上遞迴的實現會占用lgn的空間(遞迴壓棧),非遞迴的實現如下:

假設有乙個複雜鍊錶,它除了有乙個next指標外,還有乙個other指標,指向鍊錶中的任一結點或者null,

typedef struct

_listnode listnode;

如下圖,是乙個含義5個節點的該型別的複雜鍊錶,實線表示next指標,虛線表示other指標,null指標未標出。

最簡單的方法是,先複製所有節點,並用next指標鏈結起來,然後假設原始鍊錶的某節點n的other指標指向節點s,由於s的位置可能在n的前面,也可能在n的後面,所以要定位n的位置需要從原始鍊錶的頭節點開始找,直到確認節點s在鍊錶中的位置為s;然後在複製鍊錶上節點n的other指標也要指向距離煉表頭的第s個節點。這種方法的時間複雜度是o(n2)。

上面這種方法的主要缺點在於無法快速定位n節點的other所指向的s節點的位置,

下面將介紹一種時間複雜度是o(n)的方法,首先把複製的節點串到原節點後面,如下圖:

然後設定複製節點的other指標(例如 a'->other = a->other->next),如下圖

最後,把偶數順序的節點和奇數節點的指標分開。

//

逐個節點複製,並串到原節點後面

static

void clonenodes(listnode *head)}//

設定新節點的other指標

static

void connectnodes(listnode *head)

p = pcloned->next;}}

經典鍊錶問題

題目描述 在單鏈表中輸出倒數第k個節點 要求 如果鍊錶長為n,時間複雜度為o n 額外空間複雜度達到o 1 思路 當我們用num來表示鍊錶中節點個數,當我們輸出節點的時候會出現三種情況 不存在第k個節點,此時返回空 num第k個節點就是第乙個節點,操作較容易 num k 第k個節點在鍊錶中 num ...

鍊錶經典問題彙總

原帖位址 收集了一下鍊錶常見的面試題 1 如何判斷乙個單鏈表有環 2 如何判斷乙個環的入口點在 3 如何知道環的長度 4 如何知道兩個單鏈表 無環 是否相交 5 如果兩個單鏈表 無環 相交,如何知道它們相交的第乙個節點是什麼 6 如何知道兩個單鏈表 有環 是否相交 7 如果兩個單鏈表 有環 相交,如...

鍊錶及經典問題

鍊錶及經典問題 鍊錶及經典問題 第一周 鍊錶及經典問題 1 鍊錶的基礎知識鍊錶的結構 訪問鍊錶的時間複雜度幾種經典的鍊錶實現方法 2 鍊錶的典型應用場景 3 經典面試題鍊錶的訪問鍊錶的反轉 鍊錶的節點刪除 4 彩蛋習題及相關說明 1 鍊錶的基礎知識 鍊錶的結構 節點資料域指標域 實現方式包括位址 下...