日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

模拟实现单链表(三级)

發布時間:2024/4/13 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 模拟实现单链表(三级) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

講了順序表的實現呢,下面我們來講單鏈表每個節點包括兩部分,哪兩部分,一部分是存放數據的,另一部分是存放地址的,存放下一個節點的地址,這叫單鏈表,單鏈表也就是單向鏈表,有單向的就應該有雙向的,雙向表該怎么辦啊,每個節點包括三部分,一個數據,一個是指向后面節點的,一個是指向前面節點的,這就是雙向鏈表,現在我們來看一下最簡單的單向鏈表鏈表是一系列的存儲數據元素的單元通過指針串聯起來的,因此每個單元至少有兩個域,單向表有兩個域就可以了,一個是存放數據的,一個是指向下一個元素的,我們稱之為節點Node,一個是存放數據的,一個是作為指針域指向下一個元素的,我們要想實現單相鏈表的話首先要給出Node類

package com.learn.datastructure;/*** 新建一個類Node* 單鏈表的節點,這個節點有兩部分組成* * 這個就是一個單鏈表的結構,單鏈表的最后一個元素他的指針寫成空,* 這個單鏈表有一個特點,他只能通過一個節點找到他的后繼,* 我知道a1了,我就知道他的后繼是誰,但不能知道他的前驅是誰* 有人說我就想知道他的前驅,可以嗎,可以,就是比較繁瑣,* 你就寫兩個指針唄,一個只想前一個,一個只想后一個,* 或者你就設一個指針,我想找一下a1的后繼,我想找一下a1當前前驅* 我想找a2的前驅,我怎么知道a2的后繼呢,但是我怎么知道a2的前驅是a1* 從a0開始,我們是不是找a2的前驅,那你就從a0開始找,a0的下一個節點數據是a2嗎* 不是,怎么辦,指針往下移,指向下一個,他的下一個節點是a2嗎,是,那就是你了* 得這么來找* * 單鏈表的特征是只能通過前驅節點找到后繼節點,不能從后繼直接找到前驅,你想從頭來找比較繁瑣* 可以實現,我們來看一下查詢操作,添加操作,刪除操作,這個我們講單鏈表的時候講過* 我要找第三個元素,就是索引是3的吧,怎么來找,索引是3的,只能一個一個的找,效率比較低* 為什么呢,因為他的地址是不連續的,是沒有規律的,同樣我要找值是a3的元素,那你更得一個一個找* 所以這一塊他的查詢是比較低的,后面我們回來講怎么來動這個指針* * 添加操作:這是一個節點,我們要把一個新的節點加到兩個節點之間,改一下指針,改兩個指針就可以了* 當然你首先要找到前驅節點,從頭找找到他* * 刪除操作:刪除的話就和添加類似,我準備把這個節點刪了,怎么就把它刪了,待刪除的節點,Java* 里面就自動就回收了* * 下邊我們說一個特別重要的內容,下面所有的操作都是基于這一點的,第一個節點它是存數據的,第二個* 節點也是存數據的,為了方便,我們都是這樣來處理,在整個鏈表的前面再加上一個節點,整個節點不存任何* 數據,它是不存任何數據的,這個頭結點head,它是一直存在,他不存任何數據,為什么這么做,就是* 為了我們編程的方便,相當于你刪第一個元素,你如果沒有這個頭節點的話,你刪第一個元素和刪其他節點的操作* 是不一樣的,他的處理代碼會有所不同,但是加上頭結點之后會怎么辦,相當于第一個節點也不是首節點了,* 他可以向后面節點一樣處理,為了是程序更加的簡潔,我們通常在單鏈表的最前面添加一個啞元節點,叫頭結點* 他里面不存儲任何實質的對象,他的next指向真正的第一個,存儲數據的節點,這么一來有什么好處,* 對空表和非空表的處理,對首節點的處理,都是一樣的,代碼就簡化了,下面我們就來實現了* * * * @author Leon.Sun**/ public class Node {/*** 他什么也不做,就相當于兩個值都為空*/public Node() {}/*** 只給數據賦值* 這個類沒有任何的難度* @param data*/public Node(Object data) {super();this.data = data;}/*** 提供 一個構造方法* 同時給數據和指針賦值的* 單鏈表每個節點都寫好了* @param data* @param next*/public Node(Object data, Node next) {super();this.data = data;this.next = next;}/*** 代表要存儲的數據* 我們先把private去了,為什么要去了他,因為在同一個包里面* 就可以直接點data,點next,這樣就更簡單,要不讓私有的,* 出了這個類,就需要get和set,我們為了便于理解我們把private去了* 這樣一來就只限于當前包的*/Object data;/*** 引用指向下一個節點* 我們取個名字叫next* 關鍵它是什么類型,選項的下一個元素是不是還是Node,* 所以這里寫一個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;/*** 線性接口表* 我怎么覺得這些方法我們都學過,* 是不是都學過,大同小異,* 注意這是一個接口,和存儲結構無關* 無論是順序表還是鏈表,都要把這些功能給我實現,* 首先我們來講順序表啦* * 我們現在寫了一個添加,然后帶著查找* 那下面就是寫刪除了,刪除和這個應該是類似的,* 這個需要大家好好的想一想* 舉一反三,看能不能自己把刪除寫出來* * 節點的類,我們已經實現了添加操作,* 同時把查詢操作也寫了,寫了一個get* @author Leon.Sun**/ public interface List {// 返回線性表的大小,即數據元素的個數。/*** 線性表里又幾個元素* @return*/public int size();// 返回線性表中序號為 i 的數據元素/*** 獲取第i個元素* @param i* @return*/public Object get(int i);// 如果線性表為空返回 true,否則返回 false。/*** 線性表是不是空的,* @return*/public boolean isEmpty();// 判斷線性表是否包含數據元素 e/*** 線性表是不是包括某個元素* 是不是查找* @param e* @return*/public boolean contains(Object e);// 返回數據元素 e 在線性表中的序號/*** 某個元素在線性表的索引* @param e* @return*/public int indexOf(Object e);// 將數據元素 e 插入到線性表中 i 號位置/*** 添加* 這是加到指定位置,線性表的插入操作* @param i* @param e*/public void add(int i, Object e);// 將數據元素 e 插入到線性表末尾/*** 這兩個添加有什么區別,* 這是加到最后,又插入就有添加* @param e*/public void add(Object e);// 將數據元素 e 插入到元素 obj 之前/*** 在誰誰之前加* @param obj* @param e* @return*/public boolean addBefore(Object obj, Object e);// 將數據元素 e 插入到元素 obj 之后/*** 在誰誰之后加* 這個大家自己都可以來寫* @param obj* @param e* @return*/public boolean addAfter(Object obj, Object e);// 刪除線性表中序號為 i 的元素,并返回之/*** 刪除第幾個,這是刪除第幾個元素* 比如我刪除第5個元素* @param i* @return*/public Object remove(int i);// 刪除線性表中第一個與 e 相同的元素/*** 刪除指定值的元素* 比如我刪除值是30的元素* @param e* @return*/public boolean remove(Object e);// 替換線性表中序號為 i 的數據元素為 e,返回原數據元素/*** 修改,把第幾個元素改成新的值* @param i* @param e* @return*/public Object replace(int i, Object e);} package com.learn.datastructure;/*** 單鏈表就是他了,同樣也要實現List* @author Leon.Sun**/ public class SingleLinkedList implements List {/*** 在這里面首先要提供一個頭結點* 他本來就存在的,頭結點首先是一個Node類型* 名字叫head,頭結點,不存儲數據,為了編程方便,* head節點我們給他指向new Node(),*/private Node head = new Node();/*** 我們再存一個整形的變量,size是一個有幾個節點* 一共有幾個元素,有人問沒有他不行嗎,沒有他也可以* 但是有他的話要數量我們就直接拿就行了,沒有他的話每次需要數一下* 那不是效率更低了,一共有多少個節點,*/private int size;@Overridepublic int size() {/*** size太簡單了,直接size*/return size;}/*** 這個可就不一樣了,可就和順序表不一樣了* 不能通過索引直接計算定位,而需要從頭結點開始進行查找* 這個并不難,只要把添加寫完,這里只是一個循環,移動指針就可以*/@Overridepublic Object get(int i) {Node p = head;/*** 找索引等于5的,*/for(int j=0;j<=i;j++) {p = p.next;}/*** p指向這個節點,我怎么把他的789找出來* 這不是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;}/*** 這兩個有什么區別,這個是加到指定位置,我們說誰是誰的特殊情況* 只要把這個實現了,下面的就非常的簡單,*/@Overridepublic void add(int i, Object e) {/*** 我們在這里寫一個完整的,如果i的位置錯誤報異常* i可以等于size,等于size就是加到最后* 我的這個界是i*/if(i<0 || i>size) {throw new MyArrayIndexOutOfBoundsException("數組指針越界異常:" + i);}/*** 做這個之前要先做一個操作,找到前一個節點* 怎么找到前一個節點,從頭開始找,* 定義一個變量,header值是0X2012,* 你把head存的地址值賦值給p,那就相當于p和head都指向于第一個節點*/Node p = head;/*** 然后我們來個循環,不是第i個嗎,* 這個一直做一個操作,做什么操作啊,讓這個p指向下一個節點* j等于0的時候動一下,這個操作是什么,現在我的p要指向后一個節點,* 一條語句就夠了,0X4012是哪個變量的值,是p點next的,* */for(int j=0;j<i;j++) {/*** 把我們的p.next的值賦值給p就可以了* 有人說暫時還不理解,我們還有呢,怎么樣我們的p指向0X5012了* 因為我們的p指向這一塊的話我們知道,怎么表示123,123怎么表示* p.data是p的數據,我們想在這個節點和這個節點中間加數據,* 效率比較低,需要逐個的來找,i要是5的話,j小于5那就是4,* 這里不能差不多,要對就是對,要錯就是錯,一個都不能錯,* 目前分析的是沒有發現任何問題,找到這個節點了這里該怎么辦,* */p = p.next;}/*** 我們就從中間某個節點來加吧* 先寫思路,沒有思路怎么寫代碼呢* 新創建一個節點,指向新節點的前驅* 第一步新創建一個節點,只是666,我們現在調用的是add方法* 我們在棧里創建一個add方法變量,add里面有一個變量Node* newNode,這個地址指向了0X5555,newNode就指向了0X5555* 這是我們的第一步,第二步怎么辦,第二步存一個后繼的地址,第三步是把前驅的所存的指向* 地址改成newNode地址,我們選擇存數據的構造方法,我們只要存值就可以*/// Node newNode = new Node(e);/*** 我們這么來寫,他就沒有值了* */Node newNode = new Node();/*** 他直接給data賦值*/newNode.data = e;/*** 這個可以不寫,因為本來默認就是空* 你明白為什么Node里面的屬性不加private* 因為加private他就不讓你直接訪問data了* 基于這一點考慮,真可謂用心良苦,也就是newNode.data* 存指針的就是newNode.next,我要給next賦值了,* */// newNode.next = null;/*** 指明新節點的直接后繼*/newNode.next = p.next;/*** 指明新節點的直接后繼節點* newNode是指向新節點* 把newNode的值賦值給p.next*/p.next = newNode;/*** 指明新節點的直接前驅節點*//*** 加了這個節點之后別忘了再做一件事size++* 數量加加,你加了這么多值size沒有變過,* 每增加一個節點這個size就要加1*/size++; }/*** 我們來寫添加吧,這個是加到最后,這個是上面的特殊情況,*/@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("[");/*** 我們定義一個Node指向head*/Node p = head.next;for(int i=0;i<size;i++) {/*** 然后循環加p.data*/builder.append(p.data);if(i!=size-1) {builder.append(",");}/*** 同時要移動指針到下一個節點* 因為不移動永遠指向第一個節點* 死循環 了*/p = p.next; }builder.append("]");return builder.toString();}} package com.learn.datastructure;/*** 這個永遠都不會出現越界的問題,底層不是數組* * @author Leon.Sun**/ public class TestSingleLinkedList {public static void main(String[] args) {// java.util.ArrayList list;/*** 代碼不用變,變的是底層不一樣了,順序表里的刪除需要大量的移動* 鏈式表里的刪除不需要移動* * 這條語句發生了什么,我們在棧里面建立一個變量list,我們畫SingleLinkedList的時候* 它里面有屬性嗎,有兩個屬性,他就在堆里面創建了一個節點,這里面有兩個元素,第一個元素叫head,* 第二個叫size,那不用說了,size是0,head是new了一個Node,head的data是null,* 棧里面的list變量指向了堆里面的一塊空間,head指向了一個頭結點,0X2012是頭結點,不存儲數據的,* 頭結點在這里,下面我們要一個一個的添加了,代碼我們先不寫,當我們加123會怎樣,就會創建一個節點,* 這是往最后加的,這個地址是多少,是0X4012,可不能說0X2013,不可能只占一個字節,2013那這兩個* 只占一個字節,不可能的,創建一個新的節點,值是123,怎么頭就只想他了,就是在head里存一個地址指向它* 剛剛又新建的節點索引是0,就是第0個節點,再加個321,321給一個地址是0X5012,往下456,0X6012* 還有678,0X8012,地址是沒有規律的,畫了圖一行代碼也沒有寫,這是為什呢,第一個當我們一個一個添加節點* 的時候,一共有7個節點在這里,*/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);/*** 我們只要這個代碼寫了,上面的代碼就寫了,為什么呢,剛才已經說了* 它是他的一種特殊情況,加在最后就是加在中間的一種特殊情況,* 我們就不在20加了,那我們加在哪里比較合適,加在4和5中間,* 那我應該寫幾,我應該寫4還是寫5,應該寫5,為什么,因為寫了4,* 就是3和4中間了* * 10就報java.lang.NullPointerException這個異常了* */list.add(10, 666);System.out.println(list.size());System.out.println(list.isEmpty());/*** 同樣是get(3),數組里面是怎么get的,直接計算就可以了* 鏈表里面就是一個一個數了* * get(3)怎么是null了,因為我們的get沒有寫*/System.out.println(list.get(5));System.out.println(list);}} package com.learn.datastructure;/*** 這個叫自定義異常,他要繼承RuntimeException* 這里面也非常的簡單,只要實現兩個構造方法,* 我們要一個無參的,和一個帶有異常信息的* @author Leon.Sun**/ public class MyArrayIndexOutOfBoundsException extends RuntimeException {public MyArrayIndexOutOfBoundsException() {super();}public MyArrayIndexOutOfBoundsException(String message) {super(message);}}

?

總結

以上是生活随笔為你收集整理的模拟实现单链表(三级)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。