棧
棧模型
棧(stack)是限制對元素的插入(push)和刪除(pop)只能在一個位置上進行的表,該位置是表的末端,叫做棧的棧頂(top)。
棧的基本操作只有兩種,壓入棧(push)和彈出棧頂(pop),且只能作用於棧頂。(只有棧頂元素是可訪問的

你可以把棧結構理解成一個底部封閉,頂部打開的桶。最先進去的元素一定是最后才能取出,最晚進去的元素一定是最先取出。
因此棧又叫做LIFO(后進先出,Last In First Out)表。


棧的優勢
棧的操作是常數時間的,而且是以非常快的常數時間。在某些機器上,push和pop都可以寫成一條機器指令,現代計算機把棧操作作為它指令的一部分。因此棧是在計算機科學中繼數組之后最基本的數據結構。
棧的實現
棧的實現分為數組實現和鏈表實現。
- 鏈表實現
這里我們使用單鏈表來實現,定義一個first指針指向棧頂,棧的鏈表實現實際上是簡化了單鏈表實現,具體實現看以下代碼。
1public class StackImplementByLinklist<AnyType> {
2 public Node<AnyType> first;
3 int size;
4 //內部類定義node
5 public class Node<AnyType>{
6 AnyType data;
7 Node<AnyType> next;
8 }
9 //初始化
10 public void stack(){
11 first=null;
12 size=0;
13 }
14
15 public void push(AnyType a){
16 Node oldNode=first;
17 first=new Node();
18 first.data=a;
19 first.next=oldNode;
20 size++;
21 }
22
23 public AnyType pop(){
24 AnyType a=first.data;
25 first=first.next;
26 size--;
27 return a;
28 }
29
30 public boolean isEmpty(){
31 return size==0;
32 }
33
34 public int size(){
35 return size;
36 }
37}
- 數組實現
相比於鏈表實現,數組實現棧更加的常用。因為數組操作的常數時間極短,而且實現起來更加簡單。
1public class StackImplementByArray<AnyType> {
2 AnyType[] arr;
3 int size;
4 public void stack(int capacity){
5 arr=(AnyType[])new Object[capacity];
6 size=0;
7 }
8 public void push(AnyType a){
9 if(size==arr.length){
10 changeArray(2*size+1);
11 }
12 arr[size]=a;
13 size++;
14 }
15 public AnyType pop(){
16 if(size==0){
17 System.out.println("棧頂為空");
18 System.exit(0);
19 }
20 AnyType a=arr[size-1];
21 arr[size-1]=null;
22 size--;
23 return a;
24 }
25 public boolean isEmpty(){
26 return size==0;
27 }
28 public int size(){
29 return size;
30 }
31
32 //由於數組大小是要先確定的,因此當數組滿了后要擴大數組容量
33 public void changeArray(int newCapacity){
34 AnyType[] newArr=(AnyType[])new Object[newCapacity];
35 for(int i=0;i<arr.length;i++){
36 newArr[i]=arr[i];
37 }
38 arr=newArr;
39 }
40
41}
棧的應用
- 平衡符號的檢測
編譯器檢查程序符號的語法錯誤,常常就是通過棧來實現的。
在編程時,我們經常會用到“ ( ),[ ],{ }," " ”這些符號,當這些符號不是配對出現的,編譯器就會報錯,編譯就無法通過。
那么,編譯器是怎么知道這些符號有沒有配對出現的呢?它通常是這么處理的。
當遇到左符號,如“( [ { " ”這些,就把它壓入一個准備好的棧;否則就彈出棧頂,檢測當前符號是否與棧頂元素配對。一旦不能配對,直接退出報錯。
- 算術表達式的求值
隊列
隊列模型
wiki: 隊列,又稱為佇列(queue),是先進先出(FIFO, First-In-First-Out)的線性表。在具體應用中通常用鏈表或者數組來實現。隊列只允許在后端(稱為rear)進行插入操作,在前端(稱為front)進行刪除操作。隊列的操作方式和堆棧類似,唯一的區別在於隊列只允許新數據在后端進行添加。
隊列模型就相當於我們日常生活的排隊,在隊伍的后面入隊,在隊伍的前端出隊。

多種隊列
隊列一般分為普通的數組隊列,鏈表隊列和循環隊列。
鏈表隊列:長度一般是無限的,一般不存在溢出的可能性,用完就銷毀,不會浪費內存空間。
普通的數組隊列:長度一般是有限的,即數組長度。由於元素出隊后其位置的內存空間並不會釋放,因此會浪費大量的內存空間。

循環隊列:特殊的數組隊列,由於普通的數組的隊列會浪費大量的內存空間,因此出現了循環隊列。當循環隊列的隊尾指針到達數組末尾后,會重新回到數組起始位置,實現了對內存的重復利用。

隊列的實現
1.鏈表隊列
1public class QueueImplementByLinkList<AnyType> {
2 Node first;//隊首
3 Node last;//隊尾
4 int size;
5 public class Node{
6 AnyType data;
7 Node next;
8 public Node(AnyType data,Node next){
9 this.data=data;
10 this.next=next;
11 }
12 }
13
14 //初始化隊列
15 public void initqueue(){
16 first=new Node(null,null);
17 last=first;
18 size=0;
19 }
20
21 //入隊
22 public void enqueue(AnyType a){
23 if(size==0){
24 last.data=a;
25 size++;
26 return;
27 }
28 Node oldlast=last;
29 last=new Node(a,null);
30 oldlast.next=last;
31 size++;
32 }
33
34 //出隊
35 public AnyType dequeue(){
36 if(size==0){
37 System.out.print("隊列為空");
38 System.exit(0);
39 }
40 AnyType a=first.data;
41 first=first.next;
42 size--;
43 return a;
44 }
45 public boolean isEmpty(){
46 return size==0;
47 }
48 public int size(){
49 return size;
50 }
51}
2.數組隊列
1public class QueueImplementByArray<AnyType> {
2 AnyType[] arr;
3 int first;
4 int last;
5 int size;
6 //初始化
7 public void ininqueue(int capacity){
8 arr=(AnyType[])new Object[capacity];
9 first=0;
10 last=0;
11 size=0;
12 }
13 public void enqueue(AnyType a){
14 if(size==arr.length){
15 changeArray(2*size+1);
16 }
17 arr[last++]=a;
18 size++;
19 }
20 public AnyType dequeue(){
21 if(size==0){
22 System.out.println("隊列為空");
23 System.exit(0);
24 }
25 AnyType a=arr[first++];
26 arr[first-1]=null;
27 size--;
28 return a;
29 }
30 public void changeArray(int newCapacity){
31 AnyType[] newArr=(AnyType[])new Object[newCapacity];
32 for(int i=0;i<arr.length;i++){
33 newArr[i]=arr[i];
34 }
35 arr=newArr;
36 }
37 public boolean isEmpty(){
38 return size==0;
39 }
40 public int size(){
41 return size;
42 }
43
44}
- 循環隊列
1public class CycleQueue {
2 int[] arr;
3 int start;//隊首
4 int end;//隊尾
5 int size=0;
6 //初始化
7 public void initqueue(int size){
8 arr=new int[size];
9 size=0;
10 start=0;
11 end=0;
12 }
13
14 //入隊
15 public void enqueue(int num){
16 if(size>arr.length){
17 System.out.println("隊列已滿");
18 return;
19 }
20 if(end==arr.length){
21 end=0;
22 }
23 arr[end++]=num;
24 size++;
25 }
26
27 //出隊
28 public int dequeue(){
29 if(size==0){
30 System.out.println("隊列為空");
31 System.exit(0);
32 }
33 if(start==arr.length){
34 start=0;
35 }
36 size--;
37 return arr[start++];
38 }
39
40 public boolean isEmpty(){
41 return size==0;
42 }
43 public int size(){
44 return size;
45 }
46}
一點點總結
棧和隊列是基本的數據結構,是對數組和鏈表的重新封裝和擴展。由於它們的特性和執行速度,棧和隊列被廣泛的使用。
最后,不要為了使用數據結構而使用使用數據結構,要區分各種數據結構的使用場景,靈活地運用數據結構,可以事半功倍。
如果這篇文章對你有幫助的話,左下角給個推薦鴨,這個對我真的很重要?!