資料結構與演算法——棧(一)【棧的快速入門】

天然呆dull發表於2021-08-27

(stack)又名堆疊,它是一種運算受限的線性表。限定僅在表尾進行插入和刪除操作的線性表。這一端被稱為棧頂,相對地,把另一端稱為棧底。向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成為新的棧頂元素;從一個棧刪除元素又稱作出棧或退棧,它是把棧頂元素刪除掉,使其相鄰的元素成為新的棧頂元素。

下面就用計算器作為示例進行講解。

計算器需求

如上圖:輸入一個表示式 7*2*2-5+1-5+3-3,然後計算出他的結果。

問:計算機底層是如何運算得到結果的?對於計算機而言他接受到的是一個 字串,怎麼計算出來的?

針對這個問題,我們討論的就是

棧介紹

stack 棧,是一個 先入後出(FILO,First In Last Out)的 有序列表

是限制 線性表 中元素的插入和刪除只能線上性表的 **同一端 **進行的一種特殊線性表:

  • 棧頂(Top):允許插入和刪除的一端,為 變化的一端。稱為棧頂
  • 棧底(Bottom):另一端為 固定的一端,稱為棧底

根據上述定義,可知:

  • 最先 放入棧中元素在 棧底
  • 最後 放入棧中元素在 棧頂

而刪除元素則剛好相反:

  • 最先 放入棧中元素,最後 刪除
  • 最後 放入棧中元素,最先 刪除

可以參考下圖的,入棧和出棧圖示:

棧的應用場景

  • 子程式的呼叫

    在跳往子程式前,會先將 下個指令的地址 存到堆疊中,直到子程式執行完後再 將地址取出,以 回到原來的程式中

    如方法中呼叫方法。

  • 處理遞迴呼叫

    和子程式呼叫類似,只是除了儲存下一個指令的地址外,也將引數、區域變數等資料存入堆疊中。

  • 表示式的轉換(中綴表示式轉字尾表示式)與求值(實際解決)

  • 二叉樹的遍歷

  • 圖形的深度優先(depth-first)搜尋法

陣列模擬棧

參考前面的入棧和出棧的圖,思路如下:

  • 定義一個陣列,來模擬棧
  • 定義一個 top 變數表示棧頂,初始化為 -1
  • 入棧:stack[++top]=data
  • 出棧:return stack[top--]
/**
 * 陣列模擬棧
 */
//定義一個 ArrayStack 表示棧
class ArrayStack {
	private int maxSize; // 棧的大小
	private int[] stack; // 陣列,陣列模擬棧,資料就放在該陣列
	private int top = -1;// top表示棧頂,初始化為-1
	
	//構造器
	public ArrayStack(int maxSize) {
		this.maxSize = maxSize;
		stack = new int[this.maxSize];
	}
	
	//棧滿
	public boolean isFull() {
		return top == maxSize - 1;
	}
	//棧空
	public boolean isEmpty() {
		return top == -1;
	}
	//入棧-push
	public void push(int value) {
		//先判斷棧是否滿
		if(isFull()) {
			System.out.println("棧滿");
			return;
		}
		top++;
		stack[top] = value;
	}
	//出棧-pop, 將棧頂的資料返回
	public int pop() {
		//先判斷棧是否空
		if(isEmpty()) {
			//丟擲異常
			throw new RuntimeException("棧空,沒有資料~");
		}
		int value = stack[top];
		top--;
		return value;
	}
	//顯示棧的情況[遍歷棧], 遍歷時,需要從棧頂開始顯示資料
	public void list() {
		if(isEmpty()) {
			System.out.println("棧空,沒有資料~~");
			return;
		}
		//需要從棧頂開始顯示資料
		for(int i = top; i >= 0 ; i--) {
			System.out.printf("stack[%d]=%d\n", i, stack[i]);
		}
	}
	
}

測試用例

public class ArrayStackTest {
    @Test
    public void pushTest() {
        ArrayStack stack = new ArrayStack(4);
        stack.push(1);
        stack.push(2);
        stack.push(3);
        stack.push(4);
        stack.list();
        stack.push(5);
    }

    @Test
    public void popTest() {
        ArrayStack stack = new ArrayStack(4);
        stack.push(1);
        stack.push(2);
        stack.list();
        System.out.println("pop 資料:" + stack.pop());
        stack.list();
        System.out.println("pop 資料:" + stack.pop());
        stack.list();
    }
}

輸出資訊

====== pushTest ======
stack[3]=4
stack[2]=3
stack[1]=2
stack[0]=1
棧滿

====== popTest
stack[1]=2
stack[0]=1
pop 資料:2
stack[0]=1
pop 資料:1
棧空,沒有資料~~

連結串列模擬棧

思路:

  1. 首先定義一個連結串列節點類
  2. 想的是怎麼壓入資料? 根據棧的特點,可以用頭插法來新增連結串列的節點,實現先入後出。
  3. 定義一個連結串列棧類
    • 入棧push通過頭插法進行插入節點
    • 出棧pop通過刪除連結串列的第一個節點
    • 展示棧內資料,遍歷連結串列即可

連結串列節點類

class node{
    private int data;//資料域
    private node next = null;//下一個節點,預設為空

    public node(int data) {
        this.data = data;//設定資料
    }

    public int getData() {
        return data;
    }

    public node getNext() {
        return next;
    }

    public void setNext(node next) {
        this.next = next;
    }

    //為了列印方便重寫toString
    @Override
    public String toString() {
        return data + "";
    }
}

連結串列棧類

class LinkedListStack {
    private int maxSize;//棧的最大容量
    private int size;//用來記錄棧中資料個數
    private node head = new node(0);//頭節點
    private node helper;//定義一個輔助變數用來幫助實現頭插法

    public LinkedListStack(int maxSize) {
        this.maxSize = maxSize;//設定棧的最大容量
    }

    //判斷是否滿棧
    public boolean isFull() {
        return size == maxSize;
    }

    //判斷是否空棧
    public boolean isEmpty() {
        return size == 0;
    }

    //入棧
    public void push(int data) {
        //判斷是否滿棧
        if (isFull()) {
            System.out.println("棧滿");
            return;
        }
        //建立新節點
        node node = new node(data);
        //入棧
        if (size == 0) {
            head.setNext(node);
            helper = node;//將輔助變數指向新新增的節點
            size++;
        } else {
            head.setNext(node);//將頭節點的next指向新的節點
            node.setNext(helper);//將新的節點的next指向久節點
            helper = node;//再將輔助變數指向新節點
            size++;
        }
    }

    //出棧
    public int pop() {
        //判斷是否是空棧
        if (isEmpty()) {
            throw new RuntimeException("棧空,沒有資料~~");
        }
        //獲取第一個節點的資料,因為是頭插法,所以滿足棧的性質
        int data = head.getNext().getData();
        //將第一個節點刪除
        head.setNext(helper.getNext());//將頭節點的next指向出棧節點的下一個節點
        helper = helper.getNext();//將輔助變數移動到出棧節點的下一個節點
        size--;
        return data;
    }

    //顯示棧的情況[遍歷棧], 遍歷時,需要從棧頂開始顯示資料
    public void list() {
        if (isEmpty()) {
            System.out.println("棧空,沒有資料");
            return;
        }
        //設定輔助變數,來遍歷連結串列
        node temp = helper;//helper就是指向連結串列中第一個節點
        for (int i = size - 1; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, temp.getData());
            temp = temp.getNext();
        }
    }
}

測試用例

/**
 * 連結串列實現棧
 */
public class test {
    public static void main(String[] args) {
        //============push========================
        LinkedListStack linkedListStack = new LinkedListStack(4);
        System.out.println("================push==================");
        linkedListStack.push(1);
        linkedListStack.push(2);
        linkedListStack.push(3);
        linkedListStack.push(4);
        linkedListStack.list();
        linkedListStack.push(5);
        //============pop========================
        System.out.println("==================pop====================");
        System.out.println(linkedListStack.pop());
        System.out.println(linkedListStack.pop());
        linkedListStack.list();
        System.out.println(linkedListStack.pop());
        System.out.println(linkedListStack.pop());
        linkedListStack.list();
        try {
            System.out.println(linkedListStack.pop());
        } catch (Exception e) {}
    }
}

測試輸出

================push==================
stack[3]=4
stack[2]=3
stack[1]=2
stack[0]=1
棧滿
==================pop====================
4
3
stack[1]=2
stack[0]=1
2
1
棧空,沒有資料

相關文章