為什麼要講鍊表呢?這是因為java中有很多集合類底層都是通過鍊表來實現的。而且面試的時候,鍊表的實現是經常考的一個知識點。所以這篇文章的重點在於,如何使用代碼去實現這些數據結構。但是這篇文章我不打算直接上來就講鍊表,而是先從線性表開始。按照慣例先給出這篇文章的大致脈絡吧。
首先,是對數據結構中線性表,做一個回顧。還講了其兩大存儲結構,順序存儲結構和鏈式存儲結構。接下來,重點講各種鍊表的介紹,以及常用方法和特點最後,對java中使用鍊表的集合類,進行一個介紹。當然,還有一些常見的面試題。
OK,開始。
一、線性表
為了很好的描述這個概念,先從一個例子開始吧,比如幼兒園的小朋友放學之後,都是手拉手排著隊走出校園,這些小朋友從外表來看就像是一條鏈子,而線性表就和這個鏈子一樣,這些小朋友就像是線性表裡面的數據元素,從外面看一個接一個。比較正式的定義那就是:線性表:0個或者多個數據元素的有限序列。
剛剛我提到的都是從表面來看都是一樣的,說明在內部的實現方式是不一樣的,下面就是線性表的兩種存儲結構:順序存儲結構和鏈式存儲結構。
順序存儲結構:用一段地址連續的存儲單元依次存儲線性表的數據元素。
鏈式存儲結構:地址可以連續也可以不連續的存儲單元存儲數據元素
今天主要講的就是基於鏈式存儲結構的鍊表。你可以這樣理解,比如說你要找一個人,名字叫張三,你首先跑到A,發現沒有,A告訴你B可能知道,你跑到了B。B說C可能知道,你跑到了C,張三果然在C那裡,如果沒有就這樣一直不停的去找。一張圖看一下
但是這只是基礎,對線性表的描述,不想花費太多時間在這。下面用一張圖表述吧。
有了上面這張圖,相信對分類等都有了了解和認識了。下面開始今天的主題單鍊表。
下面在對這兩種存儲結構進行一個對比。
二、鍊表
1、單鍊表
對於鍊表,裡面主要有幾個重要的信息,對於每一個節點(比如上一節講到的A,B,C三處)都有下一個節點的地址和當前節點的數據。下面代碼實現一下:
首先是節點的定義(由於直接上傳代碼,效果不好,這裡就給出圖片了)):
接下來無非是對節點的增刪改查罷了,先看基本的操作。
下面就是正刪改查了,但是有一個思路需要我們要牢記,這樣在使用其他語言實現的時候,就能快速的掌握,這是我自己的方法。上面已經描述了通過索引查找元素,所以下面只考慮,插入刪除操作就好了。
首先,我們要判斷當前的空間是否滿足增刪該查的條件接下來,判斷增刪改查的位置是否正確最後,增刪改查之後沒有副作用產生。
第一個操作:插入(三種方式:頭插入、尾插入、中間插入)
對於插入操作,使用一張圖簡單表述一下
第二種操作:刪除
對刪除操作同樣一張圖
小結:上述是對單鍊表的增刪改查操作,對於其他鍊表關鍵就在於對單鍊表的更改。當然這裡只給出了鍊表的代碼實現。對於順序存儲結構的順序表實現。同樣也是這個道理。你只需要定義一個節點進行增刪改查就好了。
2、循環鍊表
如果你對單鍊表的操作能夠掌握的話,剩下的這幾種鍊表,我相信你也能很快掌握,只不過把node中的基本數據改一下,增刪的時候多一兩行代碼。對於循環鍊表,你可以想像成y一個閉環的鍊表。也就是最後一個元素又指向了第一個元素。其特點是這裡的討論重點。
特點
(1)適合合併兩個循環鍊表:有了尾指針直接合併,比如說下面有兩個循環鍊表。
現在使用尾指針合併他們。
(2)從表中任何一個節點出發,都能訪問鍊表的全部節點。這一點很好理解。
3、雙向鍊表
從名字就可以看出,每一個節點既指向前驅又指向後繼。
這個雙向鍊表相對於單鍊表還是比較複雜的,畢竟每個節點多了一個前驅,因此對於插入和刪除要格外小心。雙向鍊表的優點在於對每個節點的相鄰接點操作時候,效率會比較高。
三、java中的線性表表
最典型的就是Collection接口了。下面還包括了一些他的實現類,比如List接口,ArrayList類和LinkList類,其底層都是實現了線性表。我們只需要知道Collection這些全部都是使用了線性表,因此在遇見面試題的時候,如果要求不能使用java中提供的集合類。那麼我們應該學會使用上述的線性表表的操作去解決問題。
四、常見面試題,你會幾個?
對於面試題,由於篇幅問題,所以這裡只給出思路,本來我把代碼放進來了,但是又刪除了(原諒我顫抖的手)
1、單鍊表的創建和遍歷
本題上面已經給出答案,這裡不再說了。
2、求單鍊表中節點的個數
這一題相當於,遍歷一遍鍊表
3、查找單鍊表中的倒數第k個結點(劍指offer,題15)
先計算出鍊表的長度size,然後直接輸出第(size-k)個節點就可以了
4、查找單鍊表中的中間結點
你可以先獲取整個鍊表的長度N,N/2就好了。
5、合併兩個有序的單鍊表,合併之後的鍊表依然有序【出現頻率高】
這個類似於歸併排序,你創建一個新鍊表,然後把上面兩個鍊表依次比較,插入新鍊表
6、單鍊表的反轉【出現頻率最高】(劍指offer,題16)
這個是對插入操作的考察,在上面寫了三種插入操作實現方式,從頭到尾遍歷鍊表,取出當前鍊表節點,插入新鍊表的頭結點。這樣第一個取出的節點,在新鍊表就是最後一個
7、從尾到頭列印單鍊表(劍指offer,題5)
8、判斷單鍊表是否有環
這裡也是用到兩個指針,如果一個鍊表有環,那麼用一個指針去遍歷,是永遠走不到頭的。因此,我們用兩個指針去遍歷:first指針每次走一步,second指針每次走兩步,如果first指針和second指針相遇,說明有環。
9、取出有環鍊表中,環的長度
10、單鍊表中,取出環的起始點(劍指offer,題56)。
11、判斷兩個單鍊表相交的第一個交點(劍指offer,題37)
12、 已知一個單鍊表中存在環,求進入環中的第一個節點
基本上就寫這麼多吧,由於是基礎內容,也比較容易理解,在大學的時候都學到過,這裡算是一個複習鞏固吧。
有什麼問題,還請批評指正。謝謝