浅谈:数据结构之单链表,java代码演示单链表
單鏈表
本文是觀看尚硅谷韓老師視頻學(xué)習(xí)總結(jié),部分來(lái)源網(wǎng)絡(luò).
單鏈表介紹
鏈表是一種物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的。鏈表由一系列結(jié)點(diǎn)(鏈表中每一個(gè)元素稱為結(jié)點(diǎn))組成,結(jié)點(diǎn)可以在運(yùn)行時(shí)動(dòng)態(tài)生成。每個(gè)結(jié)點(diǎn)包括兩個(gè)部分:一個(gè)是存儲(chǔ)數(shù)據(jù)元素的數(shù)據(jù)域,另一個(gè)是存儲(chǔ)下一個(gè)結(jié)點(diǎn)地址的指針域。
單向鏈表只有一個(gè)指針域,在整個(gè)節(jié)點(diǎn)中數(shù)據(jù)域用來(lái)存儲(chǔ)數(shù)據(jù)元素,指針域用于指向下一個(gè)具有相同結(jié)構(gòu)的節(jié)點(diǎn)。
單向鏈表中,每個(gè)節(jié)點(diǎn)的數(shù)據(jù)域都是通過(guò)一個(gè) Object 類的對(duì)象引用來(lái)指向數(shù)據(jù)元素的,與數(shù)組類似,單向鏈表中的節(jié)點(diǎn)也具有一個(gè)線性次序,即如果節(jié)點(diǎn) a1 的 next 引用指向節(jié)點(diǎn) a2,則 a1 就是 a2 的直接前驅(qū),a2 是 a1 的直接后續(xù)。只能通過(guò)前驅(qū)節(jié)點(diǎn)找到后續(xù)節(jié)點(diǎn),而無(wú)法從后續(xù)節(jié)點(diǎn)找到前驅(qū)節(jié)點(diǎn)。
特點(diǎn):
數(shù)據(jù)元素的存儲(chǔ)對(duì)應(yīng)的是不連續(xù)的存儲(chǔ)空間,每個(gè)存儲(chǔ)結(jié)點(diǎn)對(duì)應(yīng)一個(gè)需要存儲(chǔ)的數(shù)據(jù)元素。每個(gè)結(jié)點(diǎn)是由數(shù)據(jù)域和指針域組成。 元素之間的邏輯關(guān)系通過(guò)存儲(chǔ)節(jié)點(diǎn)之間的鏈接關(guān)系反映出來(lái)。
邏輯上相鄰的節(jié)點(diǎn)物理上不必相鄰。
缺點(diǎn):
1、比順序存儲(chǔ)結(jié)構(gòu)的存儲(chǔ)密度小 (每個(gè)節(jié)點(diǎn)都由數(shù)據(jù)域和指針域組成,所以相同空間內(nèi)假設(shè)全存滿的話順序比鏈?zhǔn)酱鎯?chǔ)更多)。
2、查找結(jié)點(diǎn)時(shí)鏈?zhǔn)酱鎯?chǔ)要比順序存儲(chǔ)慢(每個(gè)節(jié)點(diǎn)地址不連續(xù)、無(wú)規(guī)律,導(dǎo)致按照索引查詢效率低下)。
優(yōu)點(diǎn):
1、插入、刪除靈活 (不必移動(dòng)節(jié)點(diǎn),只要改變節(jié)點(diǎn)中的指針,但是需要先定位到元素上)。
2、有元素才會(huì)分配結(jié)點(diǎn)空間,不會(huì)有閑置的結(jié)點(diǎn)。
單鏈表Java代碼模擬詳解,演示單鏈表的增刪改查
分析
按照節(jié)點(diǎn)的循序添加分析
按照節(jié)點(diǎn)的順序插入一個(gè)節(jié)點(diǎn)分析
按照節(jié)點(diǎn)數(shù)刪除某個(gè)節(jié)點(diǎn)分析圖(修改了思路也類似,找到修改的節(jié)點(diǎn),改變其屬性即可)
代碼(詳細(xì)解釋在代碼注釋中)
package com.fs.demo_2020_07_12_LinkedList;/*** 單鏈表 演示**/ public class SingleLinkedListDemo {public static void main(String[] args) {//測(cè)試鏈表//先創(chuàng)建節(jié)點(diǎn)Node node1 = new Node(1, "節(jié)點(diǎn)一");Node node2 = new Node(2, "節(jié)點(diǎn)二");Node node3 = new Node(3, "節(jié)點(diǎn)三");Node node4 = new Node(4, "節(jié)點(diǎn)四");//創(chuàng)建鏈表,將節(jié)點(diǎn)(安裝順序)添加進(jìn)去SingleLinkedList singleLinkedList = new SingleLinkedList();//按照循序添加節(jié)點(diǎn)到單項(xiàng)鏈表 // singleLinkedList.add(node1); // singleLinkedList.add(node2); // singleLinkedList.add(node3); // singleLinkedList.add(node4); // singleLinkedList.add(node4);添加多個(gè)同樣的節(jié)點(diǎn)會(huì)無(wú)限循環(huán)下去,因?yàn)閚ext指向的是同一個(gè)節(jié)點(diǎn)//根據(jù)節(jié)點(diǎn)數(shù)的大小將節(jié)點(diǎn)插入指定位置singleLinkedList.addByOrder(node1);singleLinkedList.addByOrder(node4);singleLinkedList.addByOrder(node2);singleLinkedList.addByOrder(node3);//顯示一下singleLinkedList.showLinkedList();System.out.println("--------測(cè)試修改節(jié)點(diǎn)---------");//測(cè)試修改節(jié)點(diǎn)Node newNode = new Node(2, "小付");singleLinkedList.updateByNum(newNode);System.out.println("修改后的鏈表顯示如下");singleLinkedList.showLinkedList();//正常顯示,說(shuō)明根據(jù)節(jié)點(diǎn)num找到,修改成功System.out.println("---------測(cè)試刪除節(jié)點(diǎn)---------------");//測(cè)試刪除節(jié)點(diǎn)singleLinkedList.delNode(2);System.out.println("刪除后鏈表顯示如下");singleLinkedList.showLinkedList();} }//創(chuàng)建一個(gè)類來(lái)模擬單鏈表 class SingleLinkedList{//先初始化一個(gè)頭節(jié)點(diǎn),頭節(jié)點(diǎn)不動(dòng),不存放具體信息private Node headNode = new Node(0,"頭結(jié)點(diǎn)");/*一 按照循序添加節(jié)點(diǎn)到單項(xiàng)鏈表思路:找到當(dāng)前鏈表的最后節(jié)點(diǎn)將最后這個(gè)節(jié)點(diǎn)next指向新的節(jié)點(diǎn)*/public void add(Node node){//拿到初始化的頭結(jié)點(diǎn),作為輔助遍歷tempNode temp = headNode;//遍歷鏈表while (true){//由圖解得知,單鏈表當(dāng)遍歷到最后一個(gè)節(jié)點(diǎn)后,next存儲(chǔ)的值為null.因?yàn)闆](méi)有下一個(gè)節(jié)點(diǎn)可以存儲(chǔ)了if (temp.next==null){//那就停止循環(huán)break;}//如果沒(méi)有找到最后,那就將temp后移一個(gè)節(jié)點(diǎn)temp = temp.next;}//循環(huán)結(jié)束后,所明temp到達(dá)最后一個(gè)節(jié)點(diǎn)了,那么就在最后一個(gè)節(jié)點(diǎn)后添加數(shù)據(jù),將temp的next存儲(chǔ)添加的節(jié)點(diǎn)值//將最后這個(gè)節(jié)點(diǎn)指向新的節(jié)點(diǎn)temp.next = node;}/*二 根據(jù)節(jié)點(diǎn)數(shù)的大小將節(jié)點(diǎn)插入指定位置*/public void addByOrder(Node node){//同樣,讓頭結(jié)點(diǎn)作為我們輔助遍歷Node temp = headNode;//創(chuàng)建一個(gè)標(biāo)記,默認(rèn)為falseboolean flag = false;while (true){if (temp.next==null){//說(shuō)明已經(jīng)到最后一個(gè)節(jié)點(diǎn),說(shuō)明就插入到最后一個(gè)的后面,循環(huán)結(jié)束break;}//若插入的節(jié)點(diǎn)數(shù)小于遍歷當(dāng)前temp的節(jié)點(diǎn)數(shù),那么就插入在當(dāng)前節(jié)點(diǎn)數(shù)的后面,找到插入位置,循環(huán)結(jié)束if (temp.next.num>node.num){break;//若當(dāng)前節(jié)點(diǎn)數(shù)等于插入的節(jié)點(diǎn)數(shù),說(shuō)明在鏈表中已經(jīng)存在這個(gè)節(jié)點(diǎn),所以無(wú)需添加//那么就改變標(biāo)記true,停止循環(huán)}else if (temp.next.num == node.num){flag = true;break;}//每次循環(huán)將當(dāng)前節(jié)點(diǎn)向后移temp = temp.next;}//循環(huán)結(jié)束后要么是找到了插入的位子,要么就是找到了相同的節(jié)點(diǎn)if (flag){//說(shuō)明找到了相同的節(jié)點(diǎn),就打印說(shuō)明System.out.println("準(zhǔn)備插入的節(jié)點(diǎn)數(shù):"+node.num+"已經(jīng)存在,不能在次插入~~~");}else {//將temp.next接在node.nextnode.next = temp.next;//說(shuō)明找到了插入的節(jié)點(diǎn)位置的,就是在temp后面temp.next = node;}}/*三 修改節(jié)點(diǎn)信息,根據(jù)num來(lái)修改,但是num不能修改,只能修改num節(jié)點(diǎn)的name*/public void updateByNum(Node newNode){//還是先判斷鏈表是否為空,判斷頭部節(jié)點(diǎn)的下一位是否為空if (headNode.next==null){System.out.println("鏈表為空~~~");return;}//找到需要修改的節(jié)點(diǎn),根據(jù)num號(hào)//定義一個(gè)輔助節(jié)點(diǎn),這次就不是頭結(jié)點(diǎn)了,因?yàn)轭^結(jié)點(diǎn)不能更改Node temp = headNode.next;//立一個(gè)flag,表示是否找到這個(gè)節(jié)點(diǎn)boolean flag = false;while (true){if (temp == null){break;//說(shuō)明已經(jīng)遍歷完了}if (temp.num == newNode.num){//說(shuō)明找到flag = true;break;}//每次循環(huán)指針指向下一個(gè)temp = temp.next;}//根據(jù)flag來(lái)判斷是否找到要修改的節(jié)點(diǎn)if (flag){//說(shuō)明找到temp.name = newNode.name;}else {//沒(méi)有找到System.out.println("沒(méi)有找到編號(hào)為:"+newNode.num+"的節(jié)點(diǎn)~~~");}}/*刪除節(jié)點(diǎn)思路:比較的是temp.next.num 與需要?jiǎng)h除的節(jié)點(diǎn)的num比較*/public void delNode(int num){//同樣,標(biāo)志是否摘掉帶刪除的節(jié)點(diǎn)Node temp = headNode;//立flagboolean flag = false;while (true){if (temp == null){//已經(jīng)到達(dá)最后的節(jié)點(diǎn)break;}if (temp.next.num == num){//說(shuō)明找到了刪除節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)tempflag = true;break;}temp = temp.next;//循環(huán)后移}//判斷flagif (flag){//說(shuō)明找到.可以刪除/*temp.next = temp.next.next;因?yàn)樯厦媸钦业搅艘獎(jiǎng)h除的前一個(gè)節(jié)點(diǎn),所以我們只需要將當(dāng)前節(jié)點(diǎn)的next賦值為下下個(gè)節(jié)點(diǎn),那么就達(dá)到了有個(gè)節(jié)點(diǎn)沒(méi)有連接上,就會(huì)等待java垃圾回收假設(shè)我們需要?jiǎng)h除2,那么上面的邏輯是temp.next.num = 2 的時(shí)候,flag為true那么temp就是節(jié)點(diǎn)1,那么節(jié)點(diǎn)1的next設(shè)置為下下一個(gè)那么就是節(jié)點(diǎn)三,那么就變相的將節(jié)點(diǎn)2移除鏈表*/temp.next = temp.next.next;}else {System.out.println("您需要?jiǎng)h除的節(jié)點(diǎn):"+num+"在鏈表中不存在~~~");}}/*顯示鏈表,遍歷查看*/public void showLinkedList(){//判斷鏈表否為空if (headNode.next == null){System.out.println("鏈表為空,請(qǐng)?zhí)砑雍蟛榭磣~~");return;}//同樣,頭結(jié)點(diǎn)不能動(dòng),作為輔助遍歷,從頭節(jié)點(diǎn)的下一位開始顯示,頭節(jié)點(diǎn)不顯示Node temp = headNode.next;while (true){//判斷是否到最后一個(gè)節(jié)點(diǎn)了if (temp == null){break;}//輸出節(jié)點(diǎn)信息System.out.println(temp);//將temp后移temp = temp.next;}}}//創(chuàng)建一個(gè)類類模擬節(jié)點(diǎn) class Node{public int num;//當(dāng)前節(jié)點(diǎn)數(shù)public String name;//給當(dāng)前節(jié)點(diǎn)取個(gè)名字,模擬節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)public Node next;//當(dāng)前節(jié)點(diǎn)存儲(chǔ)的下一個(gè)節(jié)點(diǎn)信息//提供構(gòu)造方法來(lái)初始化這個(gè)節(jié)點(diǎn)信息public Node(int num, String name) {this.num = num;this.name = name;}@Overridepublic String toString() {return "Node{" +"num=" + num +", name='" + name + '\'' +'}';} }運(yùn)行結(jié)果控制臺(tái)顯示
---------添加4個(gè)節(jié)點(diǎn)(1,2,3,4)------------ Node{num=1, name='節(jié)點(diǎn)一'} Node{num=2, name='節(jié)點(diǎn)二'} Node{num=3, name='節(jié)點(diǎn)三'} Node{num=4, name='節(jié)點(diǎn)四'} --------測(cè)試修改節(jié)點(diǎn)2的數(shù)據(jù)--------- 修改后的鏈表顯示如下 Node{num=1, name='節(jié)點(diǎn)一'} Node{num=2, name='小付'} Node{num=3, name='節(jié)點(diǎn)三'} Node{num=4, name='節(jié)點(diǎn)四'} ---------測(cè)試刪除節(jié)點(diǎn)2--------------- 刪除后鏈表顯示如下 Node{num=1, name='節(jié)點(diǎn)一'} Node{num=3, name='節(jié)點(diǎn)三'} Node{num=4, name='節(jié)點(diǎn)四'}單鏈表的試題案列
/*** 試題,在不改變鏈表結(jié)構(gòu)的情況下逆序打印輸出* @param headNode 給一個(gè)頭結(jié)點(diǎn)*/public static void reversePrint(Node headNode){//判斷是否有下一個(gè)if (headNode.next == null){return;}//定一個(gè)變量的獲取Node temp = headNode.next;//創(chuàng)建一個(gè)棧來(lái)實(shí)現(xiàn)Stack<Node> nodes = new Stack<>();//循環(huán)壓棧while (temp!=null){nodes.push(temp);//后移temp,這樣就可以壓入下一個(gè)節(jié)點(diǎn)了temp = temp.next;}//將棧中的節(jié)點(diǎn)彈棧取出while (nodes.size()>0){//stack棧的特點(diǎn)就是先進(jìn)后出System.out.println(nodes.pop());}}/*** 試題:將鏈表反轉(zhuǎn)* @param headNode 給定一個(gè)鏈表的頭節(jié)點(diǎn),讓鏈表反轉(zhuǎn)*/public static void reverseLinkedList(Node headNode){//先判斷鏈表是否為空,或者只有一個(gè)節(jié)點(diǎn),那么就無(wú)需返轉(zhuǎn)headNode.next.next 說(shuō)明只有一個(gè)節(jié)點(diǎn)if (headNode.next == null || headNode.next.next == null){return;}//定義一個(gè)輔助指針,幫助我們遍歷原來(lái)的鏈表Node temp = headNode.next;//將頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)給這個(gè)臨時(shí)節(jié)點(diǎn)Node next = null;//定義一個(gè)節(jié)點(diǎn),來(lái)暫時(shí)保存下一個(gè)節(jié)點(diǎn),Node reverseHead = new Node(0,"反轉(zhuǎn)節(jié)點(diǎn)");//創(chuàng)建一個(gè)新的頭結(jié)點(diǎn),來(lái)接反轉(zhuǎn)后的節(jié)點(diǎn)//遍歷原來(lái)的鏈表,沒(méi)遍歷一個(gè)節(jié)點(diǎn),將其取出,并放在新的reverseHead 的最前端while (temp!=null){//當(dāng)頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)不為null的時(shí)候,遍歷next = temp.next;//先將temp的下一個(gè)節(jié)點(diǎn)暫時(shí)保存在next中,方便后面使用temp.next = reverseHead.next;//將temp的下一個(gè)節(jié)點(diǎn)指向新的鏈表的最前端reverseHead.next = temp;//在將temp連接到新的鏈表上temp = next;//讓temp后移}//在將headNode指向reverseHead,實(shí)現(xiàn)單鏈表反轉(zhuǎn)headNode.next = reverseHead.next;}/*** 試題:查找單鏈表中倒數(shù)第幾個(gè)節(jié)點(diǎn)* @param headNode 頭結(jié)點(diǎn)* @param index 要查找的倒數(shù)第幾個(gè)節(jié)點(diǎn)* @return 查找出來(lái)返回的節(jié)點(diǎn)*/public static Node findNodeByIndex(Node headNode, int index){//先判斷頭結(jié)點(diǎn)是否為空,為空就返回nullif (headNode.next == null){return null;}//調(diào)用剛剛寫的方法獲取到鏈表的有效個(gè)數(shù)int length = getLength(headNode);//先對(duì)傳入的index做判斷,判斷數(shù)據(jù)是否子啊合理范圍內(nèi)if (index <=0 || index > length){//index<=0 不能找倒數(shù)-1 或者倒數(shù)0 個(gè)節(jié)點(diǎn)吧//index>length 假設(shè)有效節(jié)點(diǎn)就3個(gè),不能找倒數(shù)第4個(gè)節(jié)點(diǎn)吧return null;//滿足條件就返回null}//定義零時(shí)Node為頭結(jié)點(diǎn)的下一個(gè),for循環(huán)定位倒數(shù)的indexNode temp = headNode.next;//那么當(dāng)前temp為節(jié)點(diǎn)1//循環(huán)遍歷有效個(gè)數(shù)//假設(shè)length = 3 ,(節(jié)點(diǎn)順序?yàn)?,2,3)我們要找的是倒數(shù)第1個(gè),那么循環(huán)遍歷2次,//為什么:i=0的時(shí)候,temp.next = 2, i = 1的時(shí)候,temp.next = 3,//i = 2 的時(shí)候,for循環(huán)結(jié)束,返回temp,那么temp = 3,所以倒數(shù)第一個(gè)節(jié)點(diǎn)為節(jié)點(diǎn)3for(int i = 0 ; i < length - index; i++){//每次循環(huán),就讓temp指向下一個(gè)節(jié)點(diǎn)temp = temp.next;}//返回查找的節(jié)點(diǎn)return temp;}/*** 試題:獲取單鏈表的節(jié)點(diǎn)個(gè)數(shù),不統(tǒng)計(jì)頭節(jié)點(diǎn)* @param headNode 把鏈表的頭節(jié)點(diǎn)傳遞給方法* @return 返回鏈表有效節(jié)點(diǎn)的個(gè)數(shù)*/public static int getLength(Node headNode){//先判斷頭結(jié)點(diǎn)的next是否為空,為空則表示鏈表為空if (headNode.next==null){System.out.println("鏈表為空,無(wú)有效個(gè)數(shù)~~~");}//定義一個(gè)變量來(lái)統(tǒng)計(jì)個(gè)數(shù)int length = 0;//代碼走到這里說(shuō)明有下一個(gè)節(jié)點(diǎn),定義一個(gè)節(jié)點(diǎn)來(lái)保存下一個(gè)節(jié)點(diǎn)Node temp = headNode.next;// 循環(huán) 判斷temp是否為nullwhile (temp!=null){//說(shuō)明為有效數(shù)據(jù)length++;//在將temp指向下一個(gè)節(jié)點(diǎn)temp = temp.next;}//返回最后的值return length;}測(cè)試結(jié)果
反轉(zhuǎn)單鏈表圖解
在不改變單鏈表的結(jié)構(gòu)倒序打印圖解
總結(jié)
以上是生活随笔為你收集整理的浅谈:数据结构之单链表,java代码演示单链表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 队列与环形队列使用数组模拟
- 下一篇: spring核心配置文件引入外部prop