模拟实现单链表(三级)
生活随笔
收集整理的這篇文章主要介紹了
模拟实现单链表(三级)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
講了順序表的實(shí)現(xiàn)呢,下面我們來(lái)講單鏈表每個(gè)節(jié)點(diǎn)包括兩部分,哪兩部分,一部分是存放數(shù)據(jù)的,另一部分是存放地址的,存放下一個(gè)節(jié)點(diǎn)的地址,這叫單鏈表,單鏈表也就是單向鏈表,有單向的就應(yīng)該有雙向的,雙向表該怎么辦啊,每個(gè)節(jié)點(diǎn)包括三部分,一個(gè)數(shù)據(jù),一個(gè)是指向后面節(jié)點(diǎn)的,一個(gè)是指向前面節(jié)點(diǎn)的,這就是雙向鏈表,現(xiàn)在我們來(lái)看一下最簡(jiǎn)單的單向鏈表鏈表是一系列的存儲(chǔ)數(shù)據(jù)元素的單元通過(guò)指針串聯(lián)起來(lái)的,因此每個(gè)單元至少有兩個(gè)域,單向表有兩個(gè)域就可以了,一個(gè)是存放數(shù)據(jù)的,一個(gè)是指向下一個(gè)元素的,我們稱(chēng)之為節(jié)點(diǎn)Node,一個(gè)是存放數(shù)據(jù)的,一個(gè)是作為指針域指向下一個(gè)元素的,我們要想實(shí)現(xiàn)單相鏈表的話(huà)首先要給出Node類(lèi)
package com.learn.datastructure;/*** 新建一個(gè)類(lèi)Node* 單鏈表的節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)有兩部分組成* * 這個(gè)就是一個(gè)單鏈表的結(jié)構(gòu),單鏈表的最后一個(gè)元素他的指針寫(xiě)成空,* 這個(gè)單鏈表有一個(gè)特點(diǎn),他只能通過(guò)一個(gè)節(jié)點(diǎn)找到他的后繼,* 我知道a1了,我就知道他的后繼是誰(shuí),但不能知道他的前驅(qū)是誰(shuí)* 有人說(shuō)我就想知道他的前驅(qū),可以嗎,可以,就是比較繁瑣,* 你就寫(xiě)兩個(gè)指針唄,一個(gè)只想前一個(gè),一個(gè)只想后一個(gè),* 或者你就設(shè)一個(gè)指針,我想找一下a1的后繼,我想找一下a1當(dāng)前前驅(qū)* 我想找a2的前驅(qū),我怎么知道a2的后繼呢,但是我怎么知道a2的前驅(qū)是a1* 從a0開(kāi)始,我們是不是找a2的前驅(qū),那你就從a0開(kāi)始找,a0的下一個(gè)節(jié)點(diǎn)數(shù)據(jù)是a2嗎* 不是,怎么辦,指針往下移,指向下一個(gè),他的下一個(gè)節(jié)點(diǎn)是a2嗎,是,那就是你了* 得這么來(lái)找* * 單鏈表的特征是只能通過(guò)前驅(qū)節(jié)點(diǎn)找到后繼節(jié)點(diǎn),不能從后繼直接找到前驅(qū),你想從頭來(lái)找比較繁瑣* 可以實(shí)現(xiàn),我們來(lái)看一下查詢(xún)操作,添加操作,刪除操作,這個(gè)我們講單鏈表的時(shí)候講過(guò)* 我要找第三個(gè)元素,就是索引是3的吧,怎么來(lái)找,索引是3的,只能一個(gè)一個(gè)的找,效率比較低* 為什么呢,因?yàn)樗牡刂肥遣贿B續(xù)的,是沒(méi)有規(guī)律的,同樣我要找值是a3的元素,那你更得一個(gè)一個(gè)找* 所以這一塊他的查詢(xún)是比較低的,后面我們回來(lái)講怎么來(lái)動(dòng)這個(gè)指針* * 添加操作:這是一個(gè)節(jié)點(diǎn),我們要把一個(gè)新的節(jié)點(diǎn)加到兩個(gè)節(jié)點(diǎn)之間,改一下指針,改兩個(gè)指針就可以了* 當(dāng)然你首先要找到前驅(qū)節(jié)點(diǎn),從頭找找到他* * 刪除操作:刪除的話(huà)就和添加類(lèi)似,我準(zhǔn)備把這個(gè)節(jié)點(diǎn)刪了,怎么就把它刪了,待刪除的節(jié)點(diǎn),Java* 里面就自動(dòng)就回收了* * 下邊我們說(shuō)一個(gè)特別重要的內(nèi)容,下面所有的操作都是基于這一點(diǎn)的,第一個(gè)節(jié)點(diǎn)它是存數(shù)據(jù)的,第二個(gè)* 節(jié)點(diǎn)也是存數(shù)據(jù)的,為了方便,我們都是這樣來(lái)處理,在整個(gè)鏈表的前面再加上一個(gè)節(jié)點(diǎn),整個(gè)節(jié)點(diǎn)不存任何* 數(shù)據(jù),它是不存任何數(shù)據(jù)的,這個(gè)頭結(jié)點(diǎn)head,它是一直存在,他不存任何數(shù)據(jù),為什么這么做,就是* 為了我們編程的方便,相當(dāng)于你刪第一個(gè)元素,你如果沒(méi)有這個(gè)頭節(jié)點(diǎn)的話(huà),你刪第一個(gè)元素和刪其他節(jié)點(diǎn)的操作* 是不一樣的,他的處理代碼會(huì)有所不同,但是加上頭結(jié)點(diǎn)之后會(huì)怎么辦,相當(dāng)于第一個(gè)節(jié)點(diǎn)也不是首節(jié)點(diǎn)了,* 他可以向后面節(jié)點(diǎn)一樣處理,為了是程序更加的簡(jiǎn)潔,我們通常在單鏈表的最前面添加一個(gè)啞元節(jié)點(diǎn),叫頭結(jié)點(diǎn)* 他里面不存儲(chǔ)任何實(shí)質(zhì)的對(duì)象,他的next指向真正的第一個(gè),存儲(chǔ)數(shù)據(jù)的節(jié)點(diǎn),這么一來(lái)有什么好處,* 對(duì)空表和非空表的處理,對(duì)首節(jié)點(diǎn)的處理,都是一樣的,代碼就簡(jiǎn)化了,下面我們就來(lái)實(shí)現(xiàn)了* * * * @author Leon.Sun**/
public class Node {/*** 他什么也不做,就相當(dāng)于兩個(gè)值都為空*/public Node() {}/*** 只給數(shù)據(jù)賦值* 這個(gè)類(lèi)沒(méi)有任何的難度* @param data*/public Node(Object data) {super();this.data = data;}/*** 提供 一個(gè)構(gòu)造方法* 同時(shí)給數(shù)據(jù)和指針賦值的* 單鏈表每個(gè)節(jié)點(diǎn)都寫(xiě)好了* @param data* @param next*/public Node(Object data, Node next) {super();this.data = data;this.next = next;}/*** 代表要存儲(chǔ)的數(shù)據(jù)* 我們先把private去了,為什么要去了他,因?yàn)樵谕粋€(gè)包里面* 就可以直接點(diǎn)data,點(diǎn)next,這樣就更簡(jiǎn)單,要不讓私有的,* 出了這個(gè)類(lèi),就需要get和set,我們?yōu)榱吮阌诶斫馕覀儼裵rivate去了* 這樣一來(lái)就只限于當(dāng)前包的*/Object data;/*** 引用指向下一個(gè)節(jié)點(diǎn)* 我們?nèi)€(gè)名字叫next* 關(guān)鍵它是什么類(lèi)型,選項(xiàng)的下一個(gè)元素是不是還是Node,* 所以這里寫(xiě)一個(gè)Node*/Node next;Object getData() {return data;}void setData(Object data) {this.data = data;}Node getNext() {return next;}void setNext(Node next) {this.next = next;}}
package com.learn.datastructure;/*** 線性接口表* 我怎么覺(jué)得這些方法我們都學(xué)過(guò),* 是不是都學(xué)過(guò),大同小異,* 注意這是一個(gè)接口,和存儲(chǔ)結(jié)構(gòu)無(wú)關(guān)* 無(wú)論是順序表還是鏈表,都要把這些功能給我實(shí)現(xiàn),* 首先我們來(lái)講順序表啦* * 我們現(xiàn)在寫(xiě)了一個(gè)添加,然后帶著查找* 那下面就是寫(xiě)刪除了,刪除和這個(gè)應(yīng)該是類(lèi)似的,* 這個(gè)需要大家好好的想一想* 舉一反三,看能不能自己把刪除寫(xiě)出來(lái)* * 節(jié)點(diǎn)的類(lèi),我們已經(jīng)實(shí)現(xiàn)了添加操作,* 同時(shí)把查詢(xún)操作也寫(xiě)了,寫(xiě)了一個(gè)get* @author Leon.Sun**/
public interface List {// 返回線性表的大小,即數(shù)據(jù)元素的個(gè)數(shù)。/*** 線性表里又幾個(gè)元素* @return*/public int size();// 返回線性表中序號(hào)為 i 的數(shù)據(jù)元素/*** 獲取第i個(gè)元素* @param i* @return*/public Object get(int i);// 如果線性表為空返回 true,否則返回 false。/*** 線性表是不是空的,* @return*/public boolean isEmpty();// 判斷線性表是否包含數(shù)據(jù)元素 e/*** 線性表是不是包括某個(gè)元素* 是不是查找* @param e* @return*/public boolean contains(Object e);// 返回?cái)?shù)據(jù)元素 e 在線性表中的序號(hào)/*** 某個(gè)元素在線性表的索引* @param e* @return*/public int indexOf(Object e);// 將數(shù)據(jù)元素 e 插入到線性表中 i 號(hào)位置/*** 添加* 這是加到指定位置,線性表的插入操作* @param i* @param e*/public void add(int i, Object e);// 將數(shù)據(jù)元素 e 插入到線性表末尾/*** 這兩個(gè)添加有什么區(qū)別,* 這是加到最后,又插入就有添加* @param e*/public void add(Object e);// 將數(shù)據(jù)元素 e 插入到元素 obj 之前/*** 在誰(shuí)誰(shuí)之前加* @param obj* @param e* @return*/public boolean addBefore(Object obj, Object e);// 將數(shù)據(jù)元素 e 插入到元素 obj 之后/*** 在誰(shuí)誰(shuí)之后加* 這個(gè)大家自己都可以來(lái)寫(xiě)* @param obj* @param e* @return*/public boolean addAfter(Object obj, Object e);// 刪除線性表中序號(hào)為 i 的元素,并返回之/*** 刪除第幾個(gè),這是刪除第幾個(gè)元素* 比如我刪除第5個(gè)元素* @param i* @return*/public Object remove(int i);// 刪除線性表中第一個(gè)與 e 相同的元素/*** 刪除指定值的元素* 比如我刪除值是30的元素* @param e* @return*/public boolean remove(Object e);// 替換線性表中序號(hào)為 i 的數(shù)據(jù)元素為 e,返回原數(shù)據(jù)元素/*** 修改,把第幾個(gè)元素改成新的值* @param i* @param e* @return*/public Object replace(int i, Object e);}
package com.learn.datastructure;/*** 單鏈表就是他了,同樣也要實(shí)現(xiàn)List* @author Leon.Sun**/
public class SingleLinkedList implements List {/*** 在這里面首先要提供一個(gè)頭結(jié)點(diǎn)* 他本來(lái)就存在的,頭結(jié)點(diǎn)首先是一個(gè)Node類(lèi)型* 名字叫head,頭結(jié)點(diǎn),不存儲(chǔ)數(shù)據(jù),為了編程方便,* head節(jié)點(diǎn)我們給他指向new Node(),*/private Node head = new Node();/*** 我們?cè)俅嬉粋€(gè)整形的變量,size是一個(gè)有幾個(gè)節(jié)點(diǎn)* 一共有幾個(gè)元素,有人問(wèn)沒(méi)有他不行嗎,沒(méi)有他也可以* 但是有他的話(huà)要數(shù)量我們就直接拿就行了,沒(méi)有他的話(huà)每次需要數(shù)一下* 那不是效率更低了,一共有多少個(gè)節(jié)點(diǎn),*/private int size;@Overridepublic int size() {/*** size太簡(jiǎn)單了,直接size*/return size;}/*** 這個(gè)可就不一樣了,可就和順序表不一樣了* 不能通過(guò)索引直接計(jì)算定位,而需要從頭結(jié)點(diǎn)開(kāi)始進(jìn)行查找* 這個(gè)并不難,只要把添加寫(xiě)完,這里只是一個(gè)循環(huán),移動(dòng)指針就可以*/@Overridepublic Object get(int i) {Node p = head;/*** 找索引等于5的,*/for(int j=0;j<=i;j++) {p = p.next;}/*** p指向這個(gè)節(jié)點(diǎn),我怎么把他的789找出來(lái)* 這不是p.data嗎,是不是叫他啊,*/return p.data;}/*** 是不是空的*/@Overridepublic boolean isEmpty() {return size==0;}@Overridepublic boolean contains(Object e) {return false;}@Overridepublic int indexOf(Object e) {return 0;}/*** 這兩個(gè)有什么區(qū)別,這個(gè)是加到指定位置,我們說(shuō)誰(shuí)是誰(shuí)的特殊情況* 只要把這個(gè)實(shí)現(xiàn)了,下面的就非常的簡(jiǎn)單,*/@Overridepublic void add(int i, Object e) {/*** 我們?cè)谶@里寫(xiě)一個(gè)完整的,如果i的位置錯(cuò)誤報(bào)異常* i可以等于size,等于size就是加到最后* 我的這個(gè)界是i*/if(i<0 || i>size) {throw new MyArrayIndexOutOfBoundsException("數(shù)組指針越界異常:" + i);}/*** 做這個(gè)之前要先做一個(gè)操作,找到前一個(gè)節(jié)點(diǎn)* 怎么找到前一個(gè)節(jié)點(diǎn),從頭開(kāi)始找,* 定義一個(gè)變量,header值是0X2012,* 你把head存的地址值賦值給p,那就相當(dāng)于p和head都指向于第一個(gè)節(jié)點(diǎn)*/Node p = head;/*** 然后我們來(lái)個(gè)循環(huán),不是第i個(gè)嗎,* 這個(gè)一直做一個(gè)操作,做什么操作啊,讓這個(gè)p指向下一個(gè)節(jié)點(diǎn)* j等于0的時(shí)候動(dòng)一下,這個(gè)操作是什么,現(xiàn)在我的p要指向后一個(gè)節(jié)點(diǎn),* 一條語(yǔ)句就夠了,0X4012是哪個(gè)變量的值,是p點(diǎn)next的,* */for(int j=0;j<i;j++) {/*** 把我們的p.next的值賦值給p就可以了* 有人說(shuō)暫時(shí)還不理解,我們還有呢,怎么樣我們的p指向0X5012了* 因?yàn)槲覀兊膒指向這一塊的話(huà)我們知道,怎么表示123,123怎么表示* p.data是p的數(shù)據(jù),我們想在這個(gè)節(jié)點(diǎn)和這個(gè)節(jié)點(diǎn)中間加數(shù)據(jù),* 效率比較低,需要逐個(gè)的來(lái)找,i要是5的話(huà),j小于5那就是4,* 這里不能差不多,要對(duì)就是對(duì),要錯(cuò)就是錯(cuò),一個(gè)都不能錯(cuò),* 目前分析的是沒(méi)有發(fā)現(xiàn)任何問(wèn)題,找到這個(gè)節(jié)點(diǎn)了這里該怎么辦,* */p = p.next;}/*** 我們就從中間某個(gè)節(jié)點(diǎn)來(lái)加吧* 先寫(xiě)思路,沒(méi)有思路怎么寫(xiě)代碼呢* 新創(chuàng)建一個(gè)節(jié)點(diǎn),指向新節(jié)點(diǎn)的前驅(qū)* 第一步新創(chuàng)建一個(gè)節(jié)點(diǎn),只是666,我們現(xiàn)在調(diào)用的是add方法* 我們?cè)跅@飫?chuàng)建一個(gè)add方法變量,add里面有一個(gè)變量Node* newNode,這個(gè)地址指向了0X5555,newNode就指向了0X5555* 這是我們的第一步,第二步怎么辦,第二步存一個(gè)后繼的地址,第三步是把前驅(qū)的所存的指向* 地址改成newNode地址,我們選擇存數(shù)據(jù)的構(gòu)造方法,我們只要存值就可以*/// Node newNode = new Node(e);/*** 我們這么來(lái)寫(xiě),他就沒(méi)有值了* */Node newNode = new Node();/*** 他直接給data賦值*/newNode.data = e;/*** 這個(gè)可以不寫(xiě),因?yàn)楸緛?lái)默認(rèn)就是空* 你明白為什么Node里面的屬性不加private* 因?yàn)榧觩rivate他就不讓你直接訪問(wèn)data了* 基于這一點(diǎn)考慮,真可謂用心良苦,也就是newNode.data* 存指針的就是newNode.next,我要給next賦值了,* */// newNode.next = null;/*** 指明新節(jié)點(diǎn)的直接后繼*/newNode.next = p.next;/*** 指明新節(jié)點(diǎn)的直接后繼節(jié)點(diǎn)* newNode是指向新節(jié)點(diǎn)* 把newNode的值賦值給p.next*/p.next = newNode;/*** 指明新節(jié)點(diǎn)的直接前驅(qū)節(jié)點(diǎn)*//*** 加了這個(gè)節(jié)點(diǎn)之后別忘了再做一件事size++* 數(shù)量加加,你加了這么多值size沒(méi)有變過(guò),* 每增加一個(gè)節(jié)點(diǎn)這個(gè)size就要加1*/size++; }/*** 我們來(lái)寫(xiě)添加吧,這個(gè)是加到最后,這個(gè)是上面的特殊情況,*/@Overridepublic void add(Object e) {this.add(size, e);}@Overridepublic boolean addBefore(Object obj, Object e) {return false;}@Overridepublic boolean addAfter(Object obj, Object e) {return false;}@Overridepublic Object remove(int i) {return null;}@Overridepublic boolean remove(Object e) {return false;}@Overridepublic Object replace(int i, Object e) {return null;}/*** 鏈表里面哪有elementData*/@Overridepublic String toString() {if(size==0) {return "[]";}StringBuilder builder = new StringBuilder("[");/*** 我們定義一個(gè)Node指向head*/Node p = head.next;for(int i=0;i<size;i++) {/*** 然后循環(huán)加p.data*/builder.append(p.data);if(i!=size-1) {builder.append(",");}/*** 同時(shí)要移動(dòng)指針到下一個(gè)節(jié)點(diǎn)* 因?yàn)椴灰苿?dòng)永遠(yuǎn)指向第一個(gè)節(jié)點(diǎn)* 死循環(huán) 了*/p = p.next; }builder.append("]");return builder.toString();}}
package com.learn.datastructure;/*** 這個(gè)永遠(yuǎn)都不會(huì)出現(xiàn)越界的問(wèn)題,底層不是數(shù)組* * @author Leon.Sun**/
public class TestSingleLinkedList {public static void main(String[] args) {// java.util.ArrayList list;/*** 代碼不用變,變的是底層不一樣了,順序表里的刪除需要大量的移動(dòng)* 鏈?zhǔn)奖砝锏膭h除不需要移動(dòng)* * 這條語(yǔ)句發(fā)生了什么,我們?cè)跅@锩娼⒁粋€(gè)變量list,我們畫(huà)SingleLinkedList的時(shí)候* 它里面有屬性嗎,有兩個(gè)屬性,他就在堆里面創(chuàng)建了一個(gè)節(jié)點(diǎn),這里面有兩個(gè)元素,第一個(gè)元素叫head,* 第二個(gè)叫size,那不用說(shuō)了,size是0,head是new了一個(gè)Node,head的data是null,* 棧里面的list變量指向了堆里面的一塊空間,head指向了一個(gè)頭結(jié)點(diǎn),0X2012是頭結(jié)點(diǎn),不存儲(chǔ)數(shù)據(jù)的,* 頭結(jié)點(diǎn)在這里,下面我們要一個(gè)一個(gè)的添加了,代碼我們先不寫(xiě),當(dāng)我們加123會(huì)怎樣,就會(huì)創(chuàng)建一個(gè)節(jié)點(diǎn),* 這是往最后加的,這個(gè)地址是多少,是0X4012,可不能說(shuō)0X2013,不可能只占一個(gè)字節(jié),2013那這兩個(gè)* 只占一個(gè)字節(jié),不可能的,創(chuàng)建一個(gè)新的節(jié)點(diǎn),值是123,怎么頭就只想他了,就是在head里存一個(gè)地址指向它* 剛剛又新建的節(jié)點(diǎn)索引是0,就是第0個(gè)節(jié)點(diǎn),再加個(gè)321,321給一個(gè)地址是0X5012,往下456,0X6012* 還有678,0X8012,地址是沒(méi)有規(guī)律的,畫(huà)了圖一行代碼也沒(méi)有寫(xiě),這是為什呢,第一個(gè)當(dāng)我們一個(gè)一個(gè)添加節(jié)點(diǎn)* 的時(shí)候,一共有7個(gè)節(jié)點(diǎn)在這里,*/List list = new SingleLinkedList();list.add(123);list.add(321);list.add(456);list.add(678);list.add(789);list.add(111);list.add(222);list.add(111);list.add(222);/*** 我們只要這個(gè)代碼寫(xiě)了,上面的代碼就寫(xiě)了,為什么呢,剛才已經(jīng)說(shuō)了* 它是他的一種特殊情況,加在最后就是加在中間的一種特殊情況,* 我們就不在20加了,那我們加在哪里比較合適,加在4和5中間,* 那我應(yīng)該寫(xiě)幾,我應(yīng)該寫(xiě)4還是寫(xiě)5,應(yīng)該寫(xiě)5,為什么,因?yàn)閷?xiě)了4,* 就是3和4中間了* * 10就報(bào)java.lang.NullPointerException這個(gè)異常了* */list.add(10, 666);System.out.println(list.size());System.out.println(list.isEmpty());/*** 同樣是get(3),數(shù)組里面是怎么get的,直接計(jì)算就可以了* 鏈表里面就是一個(gè)一個(gè)數(shù)了* * get(3)怎么是null了,因?yàn)槲覀兊膅et沒(méi)有寫(xiě)*/System.out.println(list.get(5));System.out.println(list);}}
package com.learn.datastructure;/*** 這個(gè)叫自定義異常,他要繼承RuntimeException* 這里面也非常的簡(jiǎn)單,只要實(shí)現(xiàn)兩個(gè)構(gòu)造方法,* 我們要一個(gè)無(wú)參的,和一個(gè)帶有異常信息的* @author Leon.Sun**/
public class MyArrayIndexOutOfBoundsException extends RuntimeException {public MyArrayIndexOutOfBoundsException() {super();}public MyArrayIndexOutOfBoundsException(String message) {super(message);}}
?
總結(jié)
以上是生活随笔為你收集整理的模拟实现单链表(三级)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 模拟实现顺序表ArrayList2(三级
- 下一篇: 其他类型的链表和线性表的总结(一级)