學過編程的小伙伴們大多數都知道棧和隊列這兩種數據結構,這兩個詞我相信對大家來說并不陌生。棧和隊列是一種特殊的線性結構,是咱們根據編碼的需求衍生出來的兩種邏輯上的數據結構,在這篇文章中,我們將詳細的講解一下棧這種數據結構,至于隊列,咱們放在下一篇文章細講。
棧的核心特點是先進后出,咱們生活中,類似的例子比比皆是,像蒸包子這個過程,我們總是一層一層的放入蒸籠,靠下層的包子總是先放入的,但是蒸熟過后,我們又總是從最上層開始一層一層取出的,這個時候我相信不會有人這么傻,蒸熟過后從下層一層一層取。此外呢,在咱們JAVA中,局部變量和方法調用的過程都是存儲在棧內存的,像咱們的方法調用,遞歸調用,都是使用了棧這種先進后出的思想。比如A方法中調用B方法,B方法中又調用C方法。我們運行A方法后,總是會先讓B方法執行,B方法執行時,又會優先讓C方法執行,直到C方法執行結束后,返回給它的調用者B時,我們的B才會繼續執行,B結束過后,返回給調用者A,A才會得以繼續執行。在此過程中,我們方法的調用順序為A->B->C,但是執行結束順序則為C->B->A,剛好符合咱們棧先進后出的思想。
接下來了,我們針對棧這種數據結構,進行一個詳細的講解。
一、什么是棧?
棧是一種數據結構,是一種先進后出的特殊線性表,棧主要由兩個指針組成,一部分叫做棧底,主要用來確定咱們棧的起始位置,對棧底來說,并不能操作數據。另一部分叫做棧頂,主要用于操作數據,像咱們棧的新增和刪除都是在棧頂完成的。而對棧的新增操作,我們叫作入棧,而對棧的刪除操作,我們叫作出棧或者彈棧。下面我們來看一下棧的內存圖:
入棧:向棧中添加元素的過程,叫做入棧,每次入棧時,棧底指針保持不動,只需要移動棧頂指針即可。如下圖所示:
出棧(彈棧):向棧中刪除元素的過程,叫做出?;蛘邚棗?,每次出棧時,棧底指針保持不動,只需要移動棧頂指針即可。如下圖所示:
二、棧的實現
下面我們用最流行的Java語言完成棧的實現,咱們實現方式分為兩種,第一種基于數組,第二種基于鏈表。如果對鏈表這種數據結構不熟悉的小伙伴,可以看看我之前寫過的鏈表,在之前有專門且詳細的講解。
2.1數組實現棧
2.1.1 棧的抽象父類
因為咱們是通過兩種方式實現棧,所以將一些公共的成員提取到咱們Stack抽象類中。在Stack類中,我們定義了一個屬性size,用來表示咱們棧中實際的有效元素個數,isFull()方法主要用于判斷咱們的棧是否滿,同理isEmpty()方法用于判斷咱們的棧是否存在元素,push()為入棧操作,pop()為出棧或者彈棧操作,show()方法主要查看咱們棧的存儲情況。
package com.ignoarance.stack;
/**
* @ClassName Stack
* @Description 棧的父類
* @Author ignorance
* @Version 1.0
* bottom表示棧底指針,用于確定咱們棧的開始,top表示棧頂指針,用于完成咱們棧主要的操作。
**/
public abstract class Stack<T> {
private int size;//棧的實際有效元素個數
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
/**
* 判斷當前棧是否滿
* 是返回true,否返回false
* @return
*/
public abstract boolean isFull();
/**
* 判斷當前棧是否空
* 是返回true,否返回false
* @return
*/
public abstract boolean isEmpty();
/**
* 入棧核心方法
*/
public abstract void push(T element);
/**
* 出棧方法
* 并返回出棧元素
* @return
*/
public abstract T pop();
/**
* 遍歷方法
*/
public abstract void show();
}
2.1.2 ArrayStack實現類
ArrayStack主要為數組實現棧的核心類,在該類中,我們定義elementData數組主要用于存放元素,maxSize表示底層數組的初始化長度,也就是棧能夠存放元素的數量,bottom和top分別表示棧底和棧頂。在構造器中,我們初始化底層數組長度,以及bottom和top的初始值都為-1。那么現在咱們的棧結構示意圖如下:
核心代碼如下:
package com.ignoarance.stack;
/**
* @ClassName ArrayStack
* @Description 數組實現棧核心類
* @Author ignorance
* @Version 1.0
**/
public class ArrayStack<T> extends Stack<T> {
private T[] elementData;//棧的核心存儲結構【數組】
private final int maxSize;//數組最大長度
private int bottom;//棧底指針
private int top;//棧頂指針
public ArrayStack(T[] elementData) {
this.elementData = elementData;//初始化底層數組
this.maxSize = elementData.length;
this.bottom = -1;//將棧底索引置為-1
this.top = -1;//將棧頂索引置為-1
}
@Override
public boolean isFull() {
return false;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void push(T element) {
}
@Override
public T pop() {
return null;
}
@Override
public void show() {
}
}
2.1.3 判斷棧滿和棧空
下面我們完成isFull()這個方法,什么時候棧滿呢?也就是咱們棧中不能再添加元素了,咱們是通過數組完成棧結構的,又因為咱們的數組的最大索引為maxSize-1,所以當咱們的top移動到top=maxSize-1時也就表示咱們的棧已滿。如下圖所示:
核心代碼如下:
@Override
public boolean isFull() {
return top == maxSize - 1;
}
同理,當咱們的bottom==top==-1則表示咱們棧當前沒有一個元素,也就是棧為空。
@Override
public boolean isEmpty() {
return bottom == top;
}
2.1.4 入棧
入棧很簡單,top向上移動,并且將元素添加到數組即可,如下圖所示:
核心代碼如下:
@Override
public void push(T element) {
if (isFull()){
throw new RuntimeException("棧已滿,無法入棧...");
}
//top上移,并且添加元素
elementData[++top] = element;
//棧內有效結點+1
setSize(getSize() + 1);
}
2.1.5 出棧
出棧和入棧是一個相反的過程,只需將棧頂指針top向下移動即可:
@Override
public T pop() {
if (isEmpty()){
throw new RuntimeException("棧為空,無法出棧...");
}
setSize(getSize() - 1);
return elementData[top--];
}
2.1.6 遍歷
@Override
public void show() {
if (isEmpty()){
throw new RuntimeException("棧為空,無法遍歷...");
}
System.out.println("棧內存儲情況...");
for (int i = top;i > bottom;i--){
System.out.println(elementData[i]);
}
}
2.1.7 測試
package com.ignoarance.stack.test;
import com.ignoarance.stack.ArrayStack;
import com.ignoarance.stack.Stack;
import org.junit.Test;
/**
* @ClassName StackTest
* @Description 棧測試類
* @Author ignorance
* @Version 1.0
**/
public class StackTest {
@Test
public void test01(){
Stack<String> stack = new ArrayStack<>(new String[5]);
stack.push("周芷若");
stack.push("夏詩涵");
stack.push("張無忌");
stack.push("楊過");
stack.push("郭靖");
stack.pop();
stack.pop();
System.out.println("【當前棧的長度:】" + stack.getSize());
stack.show();
stack.push("周伯通");
stack.push("歐陽鋒");
System.out.println("【當前棧的長度:】" + stack.getSize());
stack.show();
stack.push("小龍女");
stack.show();
}
}
2.2鏈表實現棧
2.2.1 創建結點類
package com.ignoarance.stack.node;
/**
* @ClassName Node
* @Description 結點類
* @Author ignorance
* @Version 1.0
**/
public class Node<T> {
private T item;
public Node<T> next;
public Node(T item) {
this.item = item;
}
@Override
public String toString() {
return "Node{" +
"item=" + item +
'}';
}
}
2.2.2 創建LinkedListStack
在LinkedListStack鏈表實現棧結構的類中,我們定義bottom和top這兩個指針,bottom用于表示棧底,也就是咱們鏈表的頭結點,top用于表示棧頂,也就是咱們鏈表的尾結點。在構造器中,分別初始化兩個指針。
package com.ignoarance.stack;
import com.ignoarance.stack.node.Node;
/**
* @ClassName LinkedListStack
* @Description 鏈表實現棧
* @Author ignorance
* @Version 1.0
**/
public class LinkedListStack<T> extends Stack<T> {
//棧底指針作為鏈表的頭結點
private Node<T> bottom;
//棧頂指針
private Node<T> top;
public LinkedListStack() {
this.bottom = new Node<>(null);
this.top = null;
}
/**
* 判斷當前棧是否滿
* 是返回true,否返回false
* @return
*/
@Override
public boolean isFull() {
return false;
}
/**
* 判斷當前棧是否空
* 是返回true,否返回false
* @return
*/
@Override
public boolean isEmpty() {
return false;
}
/**
* 入棧核心方法
* @param element
*/
@Override
public void push(T element) {
}
/**
* 出棧方法
* 并返回出棧元素
* @return
*/
@Override
public T pop() {
return null;
}
/**
* 遍歷方法
*/
@Override
public void show() {
}
}
2.2.3 判斷棧是否為空
@Override
public boolean isEmpty() {
return bottom == null && bottom.next == null;
}
2.2.4 push()方法實現
鏈表實現棧的先進后出,我們其實可以利用單鏈表的頭插法,前面我們在單鏈表中的反轉中已經介紹過這種方法,相信咱們大家一定不會陌生,以下是思路圖:
通過頭插法,我們每次push的時候,就會將新結點插入到鏈表最前方,最后pop的時候從第一個開始依次刪除就可以。
比如我們加入的順序是Node->insertNode1->insertNode2,從頭開始查找為insertNode2->insertNode1->Node。從而通過頭插法巧妙的實現了棧先進先出的思想。代碼如下:
@Override
public void push(T element) {
//將數據組裝成一個Node結點,因為鏈表中的最小單位為結點
Node<T> insertNode = new Node<>(element);
//如果節點為空,則加到第一個結點即可
if (isEmpty()){
bottom.next = insertNode;
top = insertNode;
setSize(getSize() + 1);
return;
}
//將新插入結點插入到鏈表最前端
insertNode.next = top;
bottom.next = insertNode;
top = insertNode;
//有效結點自增
setSize(getSize() + 1);
}
2.2.5 pop()方法實現
知道了鏈表實現棧思路,那么pop()出棧就顯得比較簡單了,我們每次pop的時候將鏈表的第一個結點返回,并將其刪除即可,如下圖所示:
核心代碼如下:
@Override
public T pop() {
if (isEmpty()){
throw new RuntimeException("棧為空,無法出棧...");
}
//刪除第一個結點
Node<T> popNode = top;
bottom.next = top.next;
top = top.next;
//有效結點自減
setSize(getSize() - 1);
return popNode.getItem();
}
2.2.6 棧的遍歷
@Override
public void show() {
if (isEmpty()){
throw new RuntimeException("【棧為空,無法遍歷...】");
}
System.out.println("【當前棧的存儲情況為:】");
for (Node<T> cur = bottom.next;cur != null;cur = cur.next){
System.out.println(cur);
}
}
2.2.7 測試
@Test
public void test2(){
Stack<String> stack = new LinkedListStack<>();
stack.push("周芷若");
stack.push("夏詩涵");
stack.push("張無忌");
stack.push("楊過");
stack.push("郭靖");
System.out.println("【當前棧的長度:】" + stack.getSize());
stack.show();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
System.out.println("【當前棧的長度:】" + stack.getSize());
stack.show();
}
三、棧的經典案例
3.1括號匹配問題
力扣上有這么一道題目,給定一個字符串,需要咱們去算這個字符串中的每個"("括號是否成對存在,比如對于字符串"((我們)是)是(程序員)"每個左括號都能找到對應的右括號,說明括號能正確匹配,像"(今天))天)(氣真)好"就不滿足。
下面需要我們通過代碼來實現這個規則,這道題目看起來挺簡單,但是在我們去實現時,如果方法選擇不恰當,會發現這道題目并不簡單。
那么怎么去解決了,這正是咱們棧的一種經典的使用場景。使用其它方式也能實現,但是相比較而言復雜得多。以下是實現思路:
1.創建棧對象,用于存儲左括號;
2.遍歷字符數組,如果當前字符為左括號,則入棧;
3.如果遍歷到的當前字符為右括號,則彈棧,如果彈出的元 素為空, 則表示當前右括號沒有與之匹配的左括號。反之則說明當前右括號有匹配的左括號;
4.當循環完字符數組后,我們需要看咱們的棧是否為空,如果為空,說明全部匹配,返回true,反之說明還有多余的右括號,則返回false。
3.1.1 實現代碼
package com.ignoarance.stack.test;
import com.ignoarance.stack.LinkedListStack;
import com.ignoarance.stack.Stack;
/**
* @ClassName StackDemo01
* @Description 棧的案例-括號匹配問題
* @Author ignorance
* @Version 1.0
**/
public class StackDemo01 {
public static void mAIn(String[] args) {
String str = "((I am ) a (pretty) b)oy";
System.out.println(isMatch(str));
}
public static boolean isMatch(String resourceStr){
Stack<Character> stack = new LinkedListStack<>();
char[] chars = resourceStr.toCharArray();
for (int i = 0;i < chars.length;i++){
char item = chars[i];
if ('(' == item){
stack.push(item);
continue;
}
if (')' == item){
Character popEle = stack.pop();
if (popEle == null){
return false;
}
}
}
return stack.isEmpty();
}
}
3.1.2 測試(正確情況)
public static void main(String[] args) {
String str = "((I am ) a (pretty) b)oy";
System.out.println(isMatch(str));
}
3.1.3 測試(錯誤情況)
public static void main(String[] args) {
String str = "(((I am ) a (pretty) b)oy";
System.out.println(isMatch(str));
}
3.2逆波蘭表達式完成四則運算
相信計算機專業的同學肯定學過逆波蘭表達式,逆波蘭表達式是計算機用來進行四則運算的一個表達式,大家都知道咱們數學的四則運算,往往看起來很簡單。但是你要是把這個思想告訴計算機,卻并不是一件容易的事。
例如對10+(100/4)*5-9進行計算。像我們人腦可以很簡單的算出答案,但是如果讓你就這個表達式,讓你編碼讓計算機完成呢?是不是就有點頭疼了,可不是讓你自己分幾步做啊,第一步把100/4讓計算機算,然后又用這個結果*5。這個問題復雜就復雜在不同的算數符號具有不同的優先級,我們怎么樣讓計算機知道符號的優先級成為了我們最棘手的問題。
所以呢,為了解決這個問題,就引出了咱們的逆波蘭表達式。逆波蘭表達式又叫后綴表達式,眾所周知,一個運算符對應兩個操作數,我們將運算符寫在它所作用兩個操作數后的一種表達式就是咱們的逆波蘭表達式。比如對于a+b,所對應的逆波蘭表達式為ab+,對a+(b/c)*d-e,也就是咱們的題目對應的代數式,它所對應的逆波蘭表達式為abc/d*+e-。
那么有了逆波蘭表達式,我們可以做些什么了,對于剛才那道題目,此時我們就可以使用咱們的棧結構輕松的解決了。實現思路如下:
1.創建棧對象,主要用于存儲咱們的操作數;
2.遍歷咱們的表達式數組,如果遇到操作數,則入棧;
3.如果遇到運算符,則從棧中彈出兩個操作數,并且計算結果,將最后的結果重新入棧。
4.按照第3步的邏輯,當咱們的表達式數組遍歷完成以后,此時棧中會存在一個元素,這個元素就是咱們最后的元素結果,將其出棧即可。
3.2.1 實現代碼
package com.ignoarance.stack.test;
import com.ignoarance.stack.LinkedListStack;
import com.ignoarance.stack.Stack;
/**
* @ClassName StackDemo02
* @Description 棧應用案例-逆波蘭表達式實現四則運算
* @Author ignorance
* @Version 1.0
**/
public class StackDemo02 {
public static void main(String[] args) {
String[] express = {"10","100","4","/","5","*","+","9","-"};
System.out.println(compute(express));
}
public static Integer compute(String[] expressArray){
String sign = "+-*/";
Stack<Integer> stack = new LinkedListStack<>();
for (String item : expressArray){
if (sign.contains(item)){
Integer num1 = stack.pop();
Integer num2 = stack.pop();
stack.push(oper(num1,num2,item));
continue;
}
stack.push(Integer.parseInt(item));
}
return stack.pop();
}
private static Integer oper(Integer num1,Integer num2,String oper){
switch (oper){
case "+" : return num2 + num1;
case "-" : return num2 - num1;
case "*" : return num2 * num1;
case "/" : return num2 / num1;
default:throw new RuntimeException("不支持的操作類型...");
}
}
}
3.2.2 測試
四、綜合代碼
4.1Stack抽象父類
package com.ignoarance.stack;
/**
* @ClassName Stack
* @Description 棧的父類
* @Author ignorance
* @Version 1.0
**/
public abstract class Stack<T> {
private int size;//棧的實際有效元素個數
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
/**
* 判斷當前棧是否滿
* 是返回true,否返回false
* @return
*/
public abstract boolean isFull();
/**
* 判斷當前棧是否空
* 是返回true,否返回false
* @return
*/
public abstract boolean isEmpty();
/**
* 入棧核心方法
*/
public abstract void push(T element);
/**
* 出棧方法
* 并返回出棧元素
* @return
*/
public abstract T pop();
/**
* 遍歷方法
*/
public abstract void show();
}
4.2ArrayStack
package com.ignoarance.stack;
import java.util.Arrays;
/**
* @ClassName ArrayStack
* @Description 數組實現棧核心類
* @Author ignorance
* @Version 1.0
**/
public class ArrayStack<T> extends Stack<T> {
private T[] elementData;//棧的核心存儲結構【數組】
private final int maxSize;//數組最大長度
private int bottom;//棧底指針
private int top;//棧頂指針
public ArrayStack(T[] elementData) {
this.elementData = elementData;//初始化底層數組
this.maxSize = elementData.length;
this.bottom = -1;//將棧底索引置為-1
this.top = -1;//將棧頂索引置為-1
}
@Override
public boolean isFull() {
return top == maxSize - 1;
}
@Override
public boolean isEmpty() {
return bottom == top;
}
@Override
public void push(T element) {
if (isFull()){
throw new RuntimeException("棧已滿,無法入棧...");
}
//top上移,并且添加元素
elementData[++top] = element;
//棧內有效結點+1
setSize(getSize() + 1);
}
@Override
public T pop() {
if (isEmpty()){
throw new RuntimeException("棧為空,無法出棧...");
}
setSize(getSize() - 1);
return elementData[top--];
}
@Override
public void show() {
if (isEmpty()){
throw new RuntimeException("棧為空,無法遍歷...");
}
System.out.println("【棧內存儲情況...】");
for (int i = top;i > bottom;i--){
System.out.println(elementData[i]);
}
}
}
4.3結點類Node
package com.ignoarance.stack.node;
/**
* @ClassName Node
* @Description 結點類
* @Author ignorance
* @Version 1.0
**/
public class Node<T> {
private T item;
public Node<T> next;
public Node(T item) {
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
@Override
public String toString() {
return "Node{" +
"item=" + item +
'}';
}
}
4.4LinkedListStack
package com.ignoarance.stack;
import com.ignoarance.stack.node.Node;
/**
* @ClassName LinkedListStack
* @Description 鏈表實現棧
* @Author ignorance
* @Version 1.0
**/
public class LinkedListStack<T> extends Stack<T> {
//棧底指針作為鏈表的頭結點
private Node<T> bottom;
//棧頂指針
private Node<T> top;
public LinkedListStack() {
this.bottom = new Node<>(null);
this.top = null;
}
/**
* 判斷當前棧是否滿,也就是鏈表是否有結點
* 是返回true,否返回false
* @return
*/
@Override
public boolean isFull() {
throw new RuntimeException("不支持的操作...");
}
/**
* 判斷當前棧是否空
* 是返回true,否返回false
* @return
*/
@Override
public boolean isEmpty() {
return bottom == null || bottom.next == null;
}
/**
* 入棧核心方法
* @param element
*/
@Override
public void push(T element) {
//將數據組裝成一個Node結點,因為鏈表中的最小單位為結點
Node<T> insertNode = new Node<>(element);
//如果節點為空,則加到第一個結點即可
if (isEmpty()){
bottom.next = insertNode;
top = insertNode;
setSize(getSize() + 1);
return;
}
//將新插入結點插入到鏈表最前端
insertNode.next = top;
bottom.next = insertNode;
top = insertNode;
//有效結點自增
setSize(getSize() + 1);
}
/**
* 出棧方法
* 并返回出棧元素
* @return
*/
@Override
public T pop() {
if (isEmpty()){
throw new RuntimeException("棧為空,無法出棧...");
}
//刪除第一個結點
Node<T> popNode = top;
bottom.next = top.next;
top = top.next;
//有效結點自減
setSize(getSize() - 1);
return popNode.getItem();
}
/**
* 遍歷方法
*/
@Override
public void show() {
if (isEmpty()){
throw new RuntimeException("【棧為空,無法遍歷...】");
}
System.out.println("【當前棧的存儲情況為:】");
for (Node<T> cur = bottom.next;cur != null;cur = cur.next){
System.out.println(cur);
}
}
}
4.5Stack測試類
package com.ignoarance.stack.test;
import com.ignoarance.stack.ArrayStack;
import com.ignoarance.stack.LinkedListStack;
import com.ignoarance.stack.Stack;
import org.junit.Test;
/**
* @ClassName StackTest
* @Description 棧測試類
* @Author ignorance
* @Version 1.0
**/
public class StackTest {
@Test
public void test01(){
Stack<String> stack = new ArrayStack<>(new String[5]);
stack.push("周芷若");
stack.push("夏詩涵");
stack.push("張無忌");
stack.push("楊過");
stack.push("郭靖");
stack.pop();
stack.pop();
System.out.println("【當前棧的長度:】" + stack.getSize());
stack.show();
stack.push("周伯通");
stack.push("歐陽鋒");
System.out.println("【當前棧的長度:】" + stack.getSize());
stack.show();
stack.push("小龍女");
stack.show();
}
@Test
public void test2(){
Stack<String> stack = new LinkedListStack<>();
stack.push("周芷若");
stack.push("夏詩涵");
stack.push("張無忌");
stack.push("楊過");
stack.push("郭靖");
System.out.println("【當前棧的長度:】" + stack.getSize());
stack.show();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
System.out.println("【當前棧的長度:】" + stack.getSize());
stack.show();
}
}
4.6Stack案例一
package com.ignoarance.stack.test;
import com.ignoarance.stack.LinkedListStack;
import com.ignoarance.stack.Stack;
/**
* @ClassName StackDemo01
* @Description 棧的案例-括號匹配問題
* @Author ignorance
* @Version 1.0
**/
public class StackDemo01 {
public static void main(String[] args) {
String str = "(((I am ) a (pretty) b)oy";
System.out.println(isMatch(str));
}
public static boolean isMatch(String resourceStr){
Stack<Character> stack = new LinkedListStack<>();
char[] chars = resourceStr.toCharArray();
for (int i = 0;i < chars.length;i++){
char item = chars[i];
if ('(' == item){
stack.push(item);
continue;
}
if (')' == item){
Character popEle = stack.pop();
if (popEle == null){
return false;
}
}
}
return stack.isEmpty();
}
}
4.7Stack案例二
package com.ignoarance.stack.test;
import com.ignoarance.stack.LinkedListStack;
import com.ignoarance.stack.Stack;
/**
* @ClassName StackDemo02
* @Description 棧應用案例-逆波蘭表達式實現四則運算
* @Author ignorance
* @Version 1.0
**/
public class StackDemo02 {
public static void main(String[] args) {
String[] express = {"10","100","4","/","5","*","+","9","-"};
System.out.println(compute(express));
}
public static Integer compute(String[] expressArray){
String sign = "+-*/";
Stack<Integer> stack = new LinkedListStack<>();
for (String item : expressArray){
if (sign.contains(item)){
Integer num1 = stack.pop();
Integer num2 = stack.pop();
stack.push(oper(num1,num2,item));
continue;
}
stack.push(Integer.parseInt(item));
}
return stack.pop();
}
private static Integer oper(Integer num1,Integer num2,String oper){
switch (oper){
case "+" : return num2 + num1;
case "-" : return num2 - num1;
case "*" : return num2 * num1;
case "/" : return num2 / num1;
default:throw new RuntimeException("不支持的操作類型...");
}
}
}
總結
在本篇文章中,咱們主要講了棧這種特殊的數據結構,也是咱們特別常見的數據結構。對棧結構又通過數組和鏈表兩種方式進行實現,代碼并不算難。重點值得關注的是棧結構的核心思想,對我們來說能夠理解是特別重要的。像咱們通過棧完成了兩個案例,希望能夠幫助大家對其思想有更深的了解和認識。
我們在不斷學習的過程,會發現編程是一件特別具有挑戰性的事情,有時候咱們學得越多,總會發現自己不會的也越多,當咱們真正理解了設計者為我們提供的思想,又會突然間豁然開朗,不由對他們的智慧心生敬仰。
所以呢,在學習的路途中,有無數的同道者一起都在前行,努力的人不會一直孤獨的,總有一天我們也會因為不斷的努力,不論是技術還是生活,都會發生美好的改變。所以,加油吧!