煉錶筆面問題

2021-07-15 19:12:47 字數 4030 閱讀 1955

下面是幾個常見的煉錶筆面問題:

[toc]

很簡單:(複雜度o(n))

int

list_len(node_t *head)

測試:

int main(int argc, const char *argv)

, c = , b = , a = ;

printf("%d\n", list_len(&a));//4

return 0;

}

我們多用幾個指標就可以在o(n)完成反轉任務:

演算法:t遍歷鍊錶, q記錄t的上乙個結點, p是乙個臨時變數用來快取t的值

void

reverse(node_t *head)

測試:

node_t d = , c = , b = , a = ; 

list_display(&a);

reverse(&a);

list_display(&d);

演算法:2個指標p, q初始化指向頭結點.p先跑到k結點處, 然後q再開始跑, 當p跑到最後跑到尾巴時, q正好到達倒數第k個.複雜度o(n)

node_t *_kth(node_t *head, int k)

測試:

node_t d = , c = , b = , a = ; 

printf("_0 :%s _1: %s _2:%s _3:%s\n"

, _kth(&a, 0)->data, _kth(&a, 1)->data, _kth(&a, 2)->data, _kth(&a, 3)->data);

輸出:

_0 :d _1: c _2:b _3:a
找出中間的那個結點

演算法:設兩個初始化指向頭結點的指標p, q.p每次前進兩個結點, q每次前進乙個結點, 這樣當p到達鍊錶尾巴的時候, q到達了中間.複雜度o(n)

node_t *middle(node_t *head)

return q;

}

測試:

node_t e = , d = , c = , b = , a = ; 

printf("%s\n", middle(&a)->data);

給你鍊錶的頭結點, 逆序列印這個鍊錶.使用遞迴(即讓系統使用棧), 時間複雜度o(n)

void

r_display(node_t *t)

}

如果乙個鍊錶有環, 那麼它肯定只有乙個環.(乙個相交結點)

演算法:設兩個指標p, q, 初始化指向頭.p以步長2的速度向前跑, q的步長是1.這樣, 如果鍊錶不存在環, p和q肯定不會相遇.如果存在環, p和q一定會相遇.(就像兩個速度不同的汽車在乙個環上跑絕對會相遇).複雜度o(n)

int

any_ring(node_t *head)

return

0; //fail find

}

測試:

node_t e = , d = , c = , b = , a = ; 

e.next = &a;

printf("%d\n", any_ring(&a));

還是使用倆指標p和q, p掃瞄的步長為1, q掃瞄的步長為2.它們的相遇點為圖中meet處(在環上).

假設頭指標head到入口點entry之間的距離是k.則當q入環的時候, p已經領先了q為: d = k%n(n為環的周長).

我們設meet處相對entry的距離(沿行進方向)為x, 則有

(n-d)+x = 2x (p行進的路程是q的兩倍)

解得x = n-d

那麼當p和q在meet處相遇的時候, 從head處再發出乙個步長為1的指標r, 可以知道, r和q會在entry處相遇!

演算法就是:

初始化三個指標p, q, r全部指向head. 然後p以2的速度行進, q以1的速度行進.當p和q相遇的時候, 發出r指標並以1的速度行進, 當p和r相遇返回這個結點.複雜度o(n)
**:

node_t *find_entry(node_t *head)

if (!p) return

0; //no ring in list

for (r = head, q = q->next; q != r; r = r->next, q = q->next);

return r;

}

測試:

node_t e = , d = , c = , b = , a = ; 

e.next = &d;

printf("

%s\n

", find_entry(&a)->data);

演算法:兩個指標遍歷這兩個鍊錶,如果他們的尾結點相同,則必定相交.複雜度o(m+n)

**實現:

int

is_intersect(node_t *a, node_t *b)

測試** :

node_t e = , d = , c = , b = , a = ; 

node_t z = , y = , x = ;

printf("

%d\n

", is_intersect(&a, &x));

假設兩個鍊錶a,b.a比b長k個結點(k>=0).

那麼當a_ptr,b_ptr兩個指標同時分別遍歷a,b的時候, 必然b_ptr先到達結尾(null),而此時a_ptr落後a的尾巴k個結點.

如果此時再從a的頭髮出乙個指標t,繼續和a_ptr 一起走,當a_ptr達到結尾(null)時,t恰好走了k個結點.此時從b的頭髮乙個指標s, s和t一起走,因為a比b長k個結點,所以,t和s會一起到達交點.

演算法便是:

p,q分別遍歷鍊錶a,b,假設q先到達null,此時從a的頭髮出乙個指標t,當p到達null時,從b的頭髮出s,當s==t的時候即交點.
**實現: (注,當a,b不相交,函式返回0,即相交在null)

node_t *intersect_point(node_t *a, node_t *b)

測試

node_t e = , d = , c = , b = , a = ; 

node_t z = , y = , x = ;

printf("

%s\n

", intersect_point(&a, &x)->data);

其實我很反對這個做法.

不給頭結點的時候怎麼刪除乙個結點d:

把d的下乙個結點e的資料拷貝到d中,然後刪除e
我認為這是個偽刪除,並且這個演算法無法處理d是最後乙個結點的情況

**實現:

node_t *delete(node_t *d) 

為了列印的整齊性,我們把結點儲存的資料型別改為int(我們存放數字)

比如兩個鍊錶1->2->3->4->56->7->8,我們想要列印這種效果:

1 2 3 4 5 

6 7 8

演算法:

p和q兩個指標分別遍歷鍊錶a和b,假如q先到達0(即a比b長),此時由a頭髮出t,列印完鍊錶a.

p繼續移動到0,並列印空格.

從b頭髮出指標s列印鍊錶b

**:

void

foo(node_t *a, node_t *b)

測試:

node_t e = , d = , c = , b = , a = ; 

node_t o = , n = , m = ;

foo(&a, &m);

面經 煉錶筆試題

鍊錶的基本題型主要就以下這幾種,其他的基本是在這基礎上引申出來,所以如果基本掌握這幾種單向鍊錶的題,鍊錶基本沒什麼太大問題了。1.刪除無頭非尾節點 2.鍊錶的氣泡排序 3.反轉鍊錶 4.在當前節點前插入乙個資料x 5.查詢鍊錶的中間節點。6.刪除單鏈表的倒數第k個節點 k 1 k 總長度 對於上面這...

煉錶筆試題

反轉鍊錶 節點 class node object def init self,elem self.elem elem self.next none 鍊錶 class singlelist object def init self,head self.head head 反轉鍊錶 defrevers...

雙向鍊錶筆記

雙向鍊錶增刪,準備未來複習自己用 include using namespace std typedef struct node node 測試雙向鍊錶 void showlink node p puts 雙向鍊錶插入 void addnode node head,node tail 在中間新增 w...