數據結構與算法——棧(一)【棧的快速入門】


(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
棧空,沒有數據


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM