我的一個朋友過來面試引發我要說的一個小話題

一線碼農發表於2014-10-15

 

  在很多家公司面試,也包括在攜程,大多都會被問到一些演算法的問題,其中機票事業部的面試,基本上算是演算法問題的重災區,沒辦法,有幾個領導喜歡

用資料結構來考人家,其中包括一些常見資料結構的複雜度以及手寫一些演算法,比如快排,單連結串列等等,前幾天我一個推薦過來的朋友膝蓋就被中了一箭。

  題目就不方便具體說了,第一小問就是用非遞迴來構建一個單連結串列,我們知道構建單連結串列可以說是學資料結構的基本功,一說到用鏈式結構,它跟遞迴

又有了千絲萬縷的聯絡,很多鏈式的問題,我們用遞迴就可以輕輕鬆鬆的解決,幾乎不需要動一下腦子,但是如果用非遞迴的話,那就稍微比遞迴要複雜一點

了,起碼會多考一個引用型別記憶體分配的問題。

     後來QQ上我就用遞迴和非遞迴的形式構建單連結串列回覆了他,先給了一個遞迴的版本,這個沒問題,可以消化,然後給了一個非遞迴的版本,看了之後就扛

不住了。

 

一:遞迴版本

 1 class LinkList
 2     {
 3         public class LinkNode
 4         {
 5             public int data;
 6 
 7             public LinkNode next;
 8         }
 9 
10         private LinkNode head;
11 
12         public void Add(int data)
13         {
14             if (head == null)
15             {
16                 head = new LinkNode() { data = data };
17             }
18             else
19             {
20                 Add(head, data);
21             }
22         }
23 
24         public void Add(LinkNode node, int data)
25         {
26             if (node.next == null)
27             {
28                 node.next = new LinkNode() { data = data };
29                 return;
30             }
31 
32             Add(node.next, data);
33         }
34     }


二:非遞迴版本

 1    class LinkList
 2     {
 3         public class LinkNode
 4         {
 5             public int data;
 6 
 7             public LinkNode next;
 8         }
 9 
10         private LinkNode head;
11 
12         public void Add(int data)
13         {
14             LinkNode node = new LinkNode() { data = data };
15 
16             if (head == null)
17             {
18                 head = node;
19             }
20             else
21             {
22                 LinkNode temp = head;
23 
24                 while (temp.next != null)
25                 {
26                     temp = temp.next;
27                 }
28 
29                 temp.next = node;
30             }
31         }
32     }


這個非遞迴不理解的地方在於臨時變數temp,提出的問題就是為什麼:“temp.next=node” 之後,head的值發生了改變?我想之所以不能理解,絕逼是對

引用型別的記憶體分配不瞭解,而這個非遞迴版本恰恰就是用引用型別這個記憶體分配技巧來實現 ”非遞迴構建單連結串列“。。。為了不讓別人踩上這個坑,我還是大

概說一下流程,大概是這樣的,當我們在new一個引用型別的時候,CLR就要計算例項欄位和所有基類上的例項欄位的大小,然後再在堆上分配合理的記憶體塊,

最後把堆上的記憶體塊的首地址儲存在棧上面。

 

為了方便理解,現在假如LinkList裡面有三個結點:instance1 -> instance2 -> instance3, 

第一句:

1 LinkNode temp = head;

 

這個句子不難理解吧,把head的地址賦給temp,那麼棧上temp的地址也就是head的地址,head的地址就是指向instacnce1記憶體塊地址。

 

第二句: 從這句while中可以看到,一直在找instance的next,可以看出之後把instance2的記憶體地址給了temp,再next之後就把instance3的記憶體地址給

            了temp,然後就發現instance3的next為null,然後就跳出迴圈。

1                 while (temp.next != null)
2                 {
3                     temp = temp.next;
4                 }

第三句:從上一句可以看到,instance3的next已經為null了,這時候就把新構建的結點:LinkNode node = new LinkNode() { data = data };賦

           給temp的next指標上來繼續構建連結串列。

1 temp.next = node;

可以看到這時候instance4就構造到了instance3之後,同時temp.next已經是儲存instance4的記憶體地址,這一些操作對head來說都是透明的,它也不管

後面怎麼操作,當你遍歷head的時候會驚奇的發現居然我的連結串列中多了一個instance4,這個也就是朋友疑惑的地方,如果看到這個記憶體分配圖的話,

也許會豁然開朗,當然這篇博文沒什麼技術含量,也是自己一時有感而發。

 

相關文章