数据结构与算法学习笔记之后进先出的“桶”
前言
棧最為一種的常用的數據結構,用“桶”來形容最合適不過;今天我們就來學習一下
正文
一、棧的定義?
1.“后進先出,先進后出”的數據結構。
2.從操作特性來看,是一種“操作受限”的線性表,只可以在一端插入和刪除數據。
二、為什么需要棧?
1.任何數據結構都是對特定應用場景的抽象,棧是一種操作受限的數據結構,其操作特性用數組和鏈表均可實現,但卻暴露太多的操作接口,使用時容易出錯;
2.當某個數據集合只涉及在一端插入和刪除數據,且滿足后進者先出,先進者后出的操作特性時,我們應該首選棧這種數據結構
三、如何實現棧?
棧可以用數組,鏈表來實現
1.以數組為例
空間復雜度為O(1)
時間復雜度大多數為O(1),特殊情況自動擴容拷貝原數組時為O(n);
均攤時間復雜度接近于O(1);
// 基于數組實現的順序棧
public class ArrayStack {
private String[] items; // 數組
private int count; // 棧中元素個數
private int n; // 棧的大小
// 初始化數組,申請一個大小為 n 的數組空間
public ArrayStack(int n) {
this.items = new String[n];
this.n = n;
this.count = 0;
}
// 入棧操作
public boolean push(String item) {
// 數組空間不夠了,直接返回 false,入棧失敗。
if (count == n) return false;
// 將 item 放到下標為 count 的位置,并且 count 加一
items[count] = item;
++count;
return true;
}
// 出棧操作
public String pop() {
// 棧為空,則直接返回 null
if (count == 0) return null;
// 返回下標為 count-1 的數組元素,并且棧中元素個數 count 減一
String tmp = items[count-1];
--count;
return tmp;
}
}
(代碼來自于網絡,如有侵權請告知我刪除)
2.以鏈表為例(網上找的)
空間復雜度為O(1)
時間復雜度大多數為O(1)
public class StackOfLinked<Item> implements Iterable<Item> {
//定義一個內部類,就可以直接使用類型參數
private class Node{
Item item;
Node next;
}
private Node first;
private int N;
//構造器
public StackOfLinked(){}
//添加
public void push(Item item){
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
N++;
}
//刪除
public Item pop(){
Item item = first.item;
first = first.next;
N--;
return item;
}
//是否為空
public boolean isEmpty(){
return N == 0;
}
//元素數量
public int size(){
return N;
}
//返回棧中最近添加的元素而不刪除它
public Item peek(){
return first.item;
}
@Override
public Iterator<Item> iterator() {
return new LinkedIterator();
}
//內部類:迭代器
class LinkedIterator implements Iterator{
int i = N;
Node t = first;
@Override
public boolean hasNext() {
return i > 0;
}
@Override
public Item next() {
Item item = (Item) t.item;
t = t.next;
i--;
return item;
}
}
}
(代碼來自于網絡,如有侵權請告知我刪除)
四、棧的應用
1.函數調用中的應用
操作系統給每個線程分配了一塊獨立的內存空間,這塊內存被組織成“?!边@種結構,用來存儲函數調用時的臨時變量。每進入一個函數,就會將其中的臨時變量作為棧幀入棧,當被調用函數執行完成,返回之后,將這個函數對應的棧幀出棧。
2.在表達式求值中的應用(比如:34+13*9+44-12/3)
利用兩個棧,一個用來保存操作數,一個用來保存運算符。我們從左向右遍歷表達式,當遇到數字,我們就直接壓入操作數棧;當遇到運算符,就與運算符棧的棧頂元素進行比較,若比運算符棧頂元素優先級高,就將當前運算符壓入棧,若比運算符棧頂元素的優先級低或者相同,從運算符棧中取出棧頂運算符,從操作數棧頂取出2個操作數,然后進行計算,把計算完的結果壓入操作數棧,繼續比較。
(圖片來自于王爭)
3.棧在括號匹配中的應用(比如:{}{[()]()})
用棧保存為匹配的左括號,從左到右一次掃描字符串,當掃描到左括號時,則將其壓入棧中;當掃描到右括號時,從棧頂取出一個左括號,如果能匹配上,則繼續掃描剩下的字符串。如果掃描過程中,遇到不能配對的右括號,或者棧中沒有數據,則說明為非法格式。
當所有的括號都掃描完成之后,如果棧為空,則說明字符串為合法格式;否則,說明未匹配的左括號為非法格式。
4.如何實現瀏覽器的前進后退功能?
我們使用兩個棧X和Y,我們把首次瀏覽的頁面依次壓如棧X,當點擊后退按鈕時,再依次從棧X中出棧,并將出棧的數據一次放入Y棧。當點擊前進按鈕時,我們依次從棧Y中取出數據,放入棧X中。當棧X中沒有數據時,說明沒有頁面可以繼續后退瀏覽了。當Y棧沒有數據,那就說明沒有頁面可以點擊前進瀏覽了。
(圖片來自于王爭)
五、兩個問題
1. 我們在講棧的應用時,講到用函數調用棧來保存臨時變量,為什么函數調用要用“?!眮肀4媾R時變量呢?用其他數據結構不行嗎?
答:因為函數調用的執行順序符合后進者先出,先進者后出的特點。
函數調用中經常嵌套,栗子:A調用B,B又調用C,那么就需要先把C執行完,結果賦值給B中的臨時變量,B的執行結果再賦值給A的臨時變量,嵌套越深的函數越需要被先執行,這樣剛好符合棧的特點,因此每次遇到函數調用,只需要壓棧,最后依次從棧頂彈出依次執行即可,根據數據結構是特定應用場景的抽象的原則,我們優先考慮棧結構。
2.我們都知道,JVM 內存管理中有個“堆?!钡母拍?。棧內存用來存儲局部變量和方法調用,堆內存用來存儲 Java 中的對象。那 JVM 里面的“?!备覀冞@里說的“?!笔遣皇且换厥履兀咳绻皇?,那它為什么又叫作“?!蹦??
答:內存中的堆棧和數據結構堆棧不是一個概念,可以說內存中的堆棧是真實存在的物理區,數據結構中的堆棧是抽象的數據存儲結構。
內存空間在邏輯上分為三部分:代碼區、靜態數據區和動態數據區,動態數據區又分為棧區和堆區。
代碼區:存儲方法體的二進制代碼。高級調度(作業調度)、中級調度(內存調度)、低級調度(進程調度)控制代碼區執行代碼的切換。
靜態數據區:存儲全局變量、靜態變量、常量,常量包括final修飾的常量和String常量。系統自動分配和回收。
棧區:存儲運行方法的形參、局部變量、返回值。由系統自動分配和回收。
堆區:new一個對象的引用或地址存儲在棧區,指向該對象存儲在堆區中的真實數據。
相關文章
數據結構與算法學習筆記之寫鏈表代碼的正確姿勢(下)
數據結構與算法學習筆記之 提高讀取性能的鏈表(上)
數據結構與算法學習筆記之 從0編號的數組
以上內容為個人的學習筆記,僅作為學習交流之用。
歡迎大家關注公眾號,不定時干貨,只做有價值的輸出
作者:Dawnzhang
出處:https://www.cnblogs.com/clwydjgs/p/9792256.html
版權:本文版權歸作者
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責任
總結
以上是生活随笔為你收集整理的数据结构与算法学习笔记之后进先出的“桶”的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【high-speed-download
- 下一篇: 漫画|你还记得原生的JDBC怎么连接数据