Java實(shí)現(xiàn)鏈棧的示例代碼
前言
線性表和棧都是我們常用的數(shù)據(jù)結(jié)構(gòu),棧可以看成一種特殊狀態(tài)的線性表,棧的實(shí)現(xiàn),一般都是使用線性表來(lái)實(shí)現(xiàn),線性表分為順序表和鏈表,使用線性表中的順序表來(lái)實(shí)現(xiàn)棧時(shí)這種棧被稱為順序棧,相應(yīng)的使用線性表中的鏈表來(lái)實(shí)現(xiàn)棧時(shí)這種棧被稱為鏈棧,但是需要說(shuō)明的是,雖然棧是一種特殊的線性表,但是棧和線性表并不是一種數(shù)據(jù)結(jié)構(gòu)。這篇文章總結(jié)如何使用鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)來(lái)實(shí)現(xiàn)棧,也就是鏈棧的實(shí)現(xiàn)。如果想要了解另一種棧(順序棧)的實(shí)現(xiàn)請(qǐng)看這里:順序棧的實(shí)現(xiàn)
一、實(shí)現(xiàn)過(guò)程
這部分總結(jié)鏈棧的實(shí)現(xiàn)過(guò)程,以及對(duì)應(yīng)方法實(shí)現(xiàn)思路,這里提供一個(gè)棧的頂層接口IStack,用以聲明棧中所應(yīng)實(shí)現(xiàn)的方法,提供該接口不僅可供鏈棧使用,順序棧也是可以使用的。下面鏈棧的實(shí)現(xiàn)通過(guò)實(shí)現(xiàn)ISstack接口來(lái)完成,詳細(xì)步驟如下。
1.提供棧接口:IStack
該接口定義了棧必須實(shí)現(xiàn)的接口,有如下方法:
/** * 該接口是:棧的頂層接口 * 他的實(shí)現(xiàn)類會(huì)有:順序棧、鏈棧 * * 棧:先入后出 */ public interface IStack { void clear();//清空方法 boolean isEmpty();//判空方法 int length();//棧深度方法 Object peek();//取棧頂元素并返回值,若棧為空,返回null void push(Object object) throws Exception;//入棧操作,元素進(jìn)入棧頂 Object pop();//將棧頂元素出站 }
2.提供結(jié)點(diǎn)類:Node
鏈棧的實(shí)現(xiàn)使用單鏈表來(lái)實(shí)現(xiàn),因此我們需要提供一個(gè)結(jié)點(diǎn)的信息類,該結(jié)點(diǎn)是鏈表的存儲(chǔ)單元,他包含有兩個(gè)屬性,一個(gè)是數(shù)據(jù)域用以存儲(chǔ)數(shù)據(jù),一個(gè)是指針域用以指向下一個(gè)結(jié)點(diǎn),這兩個(gè)屬性這里都聲明成了public,也可以聲明成private,這樣就需要使用get、set方法來(lái)操作這些屬性了,這里為了方便使用public來(lái)聲明這兩個(gè)屬性。
public class Node { public Object object; public Node next; public Node(){ } public Node(Object object){ this.object = object; } public Node(Node next){ this.next = next; } public Node(Object object,Node next){ this.object = object; this.next = next; } @Override public String toString() { return "Node{" + "object=" + object + ", next=" + next + '}'; } }
3.提供鏈棧的實(shí)現(xiàn)類:LinkedStack
使用鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)實(shí)現(xiàn)的棧被稱為鏈棧,這里使用單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn),該單鏈表的實(shí)現(xiàn)可以使用帶頭結(jié)點(diǎn)和不帶頭結(jié)點(diǎn)兩種方式。兩種實(shí)現(xiàn)沒(méi)有太大區(qū)別,這里使用不帶頭結(jié)點(diǎn)的方式來(lái)實(shí)現(xiàn)。不帶頭結(jié)點(diǎn)那我們需要提供一個(gè)首結(jié)點(diǎn),首結(jié)點(diǎn)需要在棧創(chuàng)建時(shí)被初始化,代碼如下:
/** * * @author pcc * @version 1.0.0 * @className LinkedStack:該棧類,使用鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)實(shí)現(xiàn),是鏈棧,這里使用單鏈表的方式實(shí)現(xiàn) * @date 2021-04-20 16:32 * * 棧的特點(diǎn)是先進(jìn)后出: * 因此我們可以使用頭插法,每次在頭部插入,出棧時(shí)只需獲取鏈表的頭結(jié)點(diǎn)即可。 * * 鏈棧與順序棧的區(qū)別: * 順序棧底層是數(shù)組,因此必須初始化一個(gè)數(shù)組的容量,也就是棧的容量,鏈棧則無(wú)需此操作 * 對(duì)比鏈棧和順序棧的實(shí)現(xiàn),可以發(fā)現(xiàn)入棧和出戰(zhàn)方法的時(shí)間復(fù)雜度都是O(1),效率上沒(méi)有區(qū)別,但是順序棧占用的空間會(huì)相對(duì)更多 * 一些,順序棧是通過(guò)指針指向假設(shè)的棧頂,其他元素其實(shí)依然存在,但鏈棧的棧頂之前的元素會(huì)被垃圾回收,因此鏈棧的實(shí)現(xiàn)綜合時(shí)間和 * 空間來(lái)看,更優(yōu)秀一些。 */ public class LinkedStack implements IStack { //這是首結(jié)點(diǎn) Node node; public LinkedStack(){ node = new Node(); } }
4.提供清空(clear)、判空(isEmpty)、棧深度(length)等方法
這些方法的實(shí)現(xiàn)都比較簡(jiǎn)單,都一起寫了,鏈表的起始就是首結(jié)點(diǎn),因此我們只需要將首結(jié)點(diǎn)的數(shù)據(jù)域與指針域置空即可實(shí)現(xiàn)鏈棧的清空。判空也只需要判斷首結(jié)點(diǎn)的數(shù)據(jù)域是否有值即可。棧深度則需要遍歷鏈表的長(zhǎng)度了(也可以設(shè)置一個(gè)整型,每次入棧操作時(shí)整型加1,這里通過(guò)遍歷實(shí)現(xiàn))。
下面是三個(gè)方法的實(shí)現(xiàn):
@Override public void clear() { node.next = null; node.object = null; } @Override public boolean isEmpty() { return node.object==null?true:false; } @Override public int length() { if(node.object==null) return 0; int j= 1; Node nodeNew = node; while(nodeNew.next!=null){ j++; nodeNew = nodeNew.next; } return j; }
5.提供入棧的方法:push(Object object)
入棧方法當(dāng)然就是將數(shù)據(jù)元素放入棧頂?shù)牟僮髁恕J紫任覀儜?yīng)該知道對(duì)于鏈表每次獲取鏈表首結(jié)點(diǎn)的時(shí)間復(fù)雜度是O(1),獲取尾結(jié)點(diǎn)的時(shí)間復(fù)雜度是O(n),因此很明顯我們使用鏈表作為棧的數(shù)據(jù)結(jié)構(gòu)時(shí),應(yīng)該使用頭插法來(lái)將數(shù)據(jù)存入鏈棧,這樣我們每次插入的棧頂元素都在鏈表的開(kāi)始位置,獲取該結(jié)點(diǎn)的時(shí)間復(fù)雜度是O(1)。所以我們使用頭插法實(shí)現(xiàn)數(shù)據(jù)的存儲(chǔ),代碼如下:
@Override public void push(Object object) throws Exception { if(node.object==null){ node.object = object; return; } //頭插法 node = new Node(object,node); }
6.提供獲取棧頂元素方法:peek()
該方法只是獲取棧頂元素的信息,并不會(huì)將該元素出棧,因?yàn)闂m斣鼐褪擎湵淼氖捉Y(jié)點(diǎn),因此我們只需要返回首結(jié)點(diǎn)的數(shù)據(jù)域即可,代碼實(shí)現(xiàn)如下:
@Override public Object peek() { return node.object; }
7.提供出棧方法:pop()
棧是一種先入后出的數(shù)據(jù)結(jié)構(gòu),每次出棧的只能是最后進(jìn)入的數(shù)據(jù)元素,因此我們每次只需要將鏈棧的首結(jié)點(diǎn)出棧即可,代碼實(shí)現(xiàn)如下:
@Override public Object pop() { if(node.object==null) return null; Node tem = node; node = node.next==null?new Node():node.next; return tem.object; }
8.提供鏈棧的完整實(shí)現(xiàn)代碼
/** * * @author pcc * @version 1.0.0 * @className LinkedStack:該棧類,使用鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)實(shí)現(xiàn),是鏈棧,這里使用單鏈表的方式實(shí)現(xiàn) * @date 2021-04-20 16:32 * * 棧的特點(diǎn)是先進(jìn)后出: * 因此我們可以使用頭插法,每次在頭部插入,出棧時(shí)只需獲取鏈表的頭結(jié)點(diǎn)即可。 * * 鏈棧與順序棧的區(qū)別: * 順序棧底層是數(shù)組,因此必須初始化一個(gè)數(shù)組的容量,也就是棧的容量,鏈棧則無(wú)需此操作 * 對(duì)比鏈棧和順序棧的實(shí)現(xiàn),可以發(fā)現(xiàn)入棧和出戰(zhàn)方法的時(shí)間復(fù)雜度都是O(1),效率上沒(méi)有區(qū)別,但是順序棧占用的空間會(huì)相對(duì)更多 * 一些,順序棧是通過(guò)指針指向假設(shè)的棧頂,其他元素其實(shí)依然存在,但鏈棧的棧頂之前的元素會(huì)被垃圾回收,因此鏈棧的實(shí)現(xiàn)綜合時(shí)間和 * 空間來(lái)看,更優(yōu)秀一些。 */ public class LinkedStack implements IStack { //這是首結(jié)點(diǎn) Node node; public LinkedStack(){ node = new Node(); } @Override public void clear() { node.next = null; node.object = null; } @Override public boolean isEmpty() { return node.object==null?true:false; } @Override public int length() { if(node.object==null) return 0; int j= 1; Node nodeNew = node; while(nodeNew.next!=null){ j++; nodeNew = nodeNew.next; } return j; } @Override public Object peek() { return node.object; } @Override public void push(Object object) throws Exception { if(node.object==null){ node.object = object; return; } //頭插法 node = new Node(object,node); } @Override public Object pop() { if(node.object==null) return null; Node tem = node; node = node.next==null?new Node():node.next; return tem.object; } }
二、測(cè)試鏈棧的相應(yīng)方法
第一部分已經(jīng)詳細(xì)描述了鏈棧的實(shí)現(xiàn)過(guò)程,下面就來(lái)測(cè)試下這些方法是否可以正常使用吧。
1.測(cè)試入棧和出棧
創(chuàng)建一個(gè)測(cè)試類,然后往棧中插入五個(gè)數(shù)據(jù)元素,并依次出棧,若是出棧順序和入棧順序相反則說(shuō)明是正確的了,測(cè)試結(jié)果如下圖:
可以看見(jiàn)輸出和輸入順序是相反的,結(jié)果滿足先入后出的預(yù)期。說(shuō)明入棧與出棧的操作沒(méi)有問(wèn)題。
2.驗(yàn)證獲取棧頂元素方法peek、棧深度方法length、清空方法clear
還是往棧里面放入原先的五個(gè)元素,然后棧頂元素應(yīng)該是“李四5”,長(zhǎng)度應(yīng)該是5,第二次長(zhǎng)度應(yīng)該是0,如果輸出內(nèi)容是這些說(shuō)明棧的實(shí)現(xiàn)就沒(méi)有問(wèn)題了,結(jié)果見(jiàn)下圖:
從輸出結(jié)果可以看到,這些方法實(shí)現(xiàn)并沒(méi)有問(wèn)題,到這里棧常用的所有方法就已經(jīng)都實(shí)現(xiàn)了,方法的實(shí)現(xiàn)都很簡(jiǎn)單,并沒(méi)有有難度的地方。
三、總結(jié)
鏈棧即使用鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)實(shí)現(xiàn)的棧,其實(shí)這里的鏈棧就是一個(gè)特殊的單鏈表,一種限制了只能在首結(jié)點(diǎn)插入和刪除的單鏈表。這也是鏈表的本質(zhì),無(wú)論是在何種語(yǔ)言中棧的實(shí)現(xiàn)要么是使用順序表要么是使用鏈表。無(wú)論使用哪種方法實(shí)現(xiàn)其實(shí)都很簡(jiǎn)單。
1.順序棧與鏈棧的區(qū)別
順序棧與鏈棧作為兩種不同的棧的實(shí)現(xiàn),他們肯定是有區(qū)別的,我們首先對(duì)比下入棧的操作,順序棧的入棧操作是直接根據(jù)頭指針將數(shù)據(jù)加入到數(shù)組指定的下標(biāo)位置,鏈棧的入棧則是直接在首結(jié)點(diǎn)插入,他們的時(shí)間復(fù)雜度都是O(1),那么再對(duì)比下出棧的操作,順序棧是通過(guò)頭指針直接拿到下標(biāo)為頭指針的數(shù)組元素,鏈棧則是直接將鏈表的頭部刪除返回,他們的時(shí)間復(fù)雜度也都是O(1),因此在入棧和出棧的操作上他們的性能并沒(méi)有什么區(qū)別,其他的區(qū)別還是要看不同的實(shí)現(xiàn),若是實(shí)現(xiàn)的順序棧每次出棧后不刪除棧頂以后的元素,則順序棧會(huì)占用更多的空間,因?yàn)殒湕C看纬鰲:笏臄?shù)據(jù)元素與GC ROOTS就失去了連接,下次觸發(fā)GC時(shí)就會(huì)回收相應(yīng)內(nèi)存。這種情況順序棧會(huì)占用更多的空間,鏈棧則更少,但是若是每次出棧時(shí)將順序棧頭指針以后的數(shù)據(jù)元素刪除,就不會(huì)有這種區(qū)別了,所以綜合來(lái)說(shuō)順序棧與鏈棧在入棧和出棧的操作上性能沒(méi)有區(qū)別,但是其他場(chǎng)景的性能消耗,比如空間復(fù)雜度上則需要看順序棧與鏈棧的具體實(shí)現(xiàn)來(lái)定。
到此這篇關(guān)于Java實(shí)現(xiàn)鏈棧的示例代碼的文章就介紹到這了,更多相關(guān)Java鏈棧內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot2.0整合jackson配置日期格式化和反序列化的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot2.0整合jackson配置日期格式化和反序列化的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11JPA添加Pageable實(shí)現(xiàn)翻頁(yè)時(shí)報(bào)錯(cuò)的問(wèn)題
這篇文章主要介紹了解決JPA添加Pageable實(shí)現(xiàn)翻頁(yè)時(shí)報(bào)錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09java語(yǔ)言基礎(chǔ)之標(biāo)識(shí)符和命名規(guī)則詳解
這篇文章主要給大家介紹了關(guān)于java語(yǔ)言基礎(chǔ)之標(biāo)識(shí)符和命名規(guī)則的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03詳解spring cloud使用Hystrix實(shí)現(xiàn)單個(gè)方法的fallback
本篇文章主要介紹了詳解spring cloud-使用Hystrix實(shí)現(xiàn)單個(gè)方法的fallback,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01詳解Java編程中protected修飾符與static修飾符的作用
這篇文章主要介紹了Java編程中protected關(guān)鍵字與static關(guān)鍵字的作用,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2016-01-01SpringBoot項(xiàng)目整合Redis教程詳解
這篇文章主要介紹了SpringBoot項(xiàng)目整合Redis教程詳解,Redis?是完全開(kāi)源的,遵守?BSD?協(xié)議,是一個(gè)高性能的?key-value?數(shù)據(jù)庫(kù)。感興趣的小伙伴可以參考閱讀本文2023-03-03