【C#數據結構系列】棧和隊列


 一:棧

  棧和隊列也是線性結構,線性表、棧和隊列這三種數據結構的數據元素以及數據元素間的邏輯關系完全相同,差別是線性表的操作不受限制,而棧和隊列的操作受到限制。棧的操作只能在表的一端進行,隊列的插入操作在表的一端進行而其它操作在表的另一端進行,所以,把棧和隊列稱為操作受限的線性表。

1:棧的定義及基本運算

  棧(Stack)是操作限定在表的尾端進行的線性表。表尾由於要進行插入、刪除等操作,所以,它具有特殊的含義,把表尾稱為棧頂(Top),另一端是固定的,叫棧底(Bottom)。當棧中沒有數據元素時叫空棧(Empty Stack)。

  棧通常記為:S= (a 1 ,a 2 ,…,a n ),S是英文單詞stack的第 1 個字母。a 1 為棧底元素,a n 為棧頂元素。這n個數據元素按照a 1 ,a 2 ,…,a n 的順序依次入棧,而出棧的次序相反,a n 第一個出棧,a 1 最后一個出棧。所以,棧的操作是按照后進先出(Last In First Out,簡稱LIFO)或先進后出(First In Last Out,簡稱FILO)的原則進行的,因此,棧又稱為LIFO表或FILO表。

  

  由於棧只能在棧頂進行操作,所以棧不能在棧的任意一個元素處插入或刪除元素。因此,棧的操作是線性表操作的一個子集。棧的操作主要包括在棧頂插入元素和刪除元素、取棧頂元素和判斷棧是否為空等。

  棧的接口定義如下所示:

1 public interface IStack<T> 
2 {
3     int GetLength(); //求棧的長度
4     bool IsEmpty(); //判斷棧是否為空
5     void Clear(); //清空操作
6     void Push(T item); //入棧操作
7     T Pop(); //出棧操作
8     T GetTop(); //取棧頂元素
9 }
View Code

  

2:棧的存儲和運算實現

1、順序棧:

  用一片連續的存儲空間來存儲棧中的數據元素,這樣的棧稱為順序棧(Sequence Stack)。類似於順序表,用一維數組來存放順序棧中的數據元素。

  順序棧類 SeqStack<T>的實現說明如下所示。

 

  1 public class SeqStack<T> : IStack<T>
  2     {
  3         private int maxsize; //順序棧的容量
  4         private T[] data; //數組,用於存儲順序棧中的數據元素
  5         private int top; //指示順序棧的棧頂
  6         //索引器
  7         public T this[int index]
  8         {
  9             get
 10             {
 11                 return data[index];
 12             }
 13             set
 14             {
 15                 data[index] = value;
 16             }
 17         }
 18         //容量屬性
 19         public int Maxsize
 20         {
 21             get
 22             {
 23                 return maxsize;
 24             }
 25             set
 26             {
 27                 maxsize = value;
 28             }
 29         }
 30         //棧頂屬性
 31         public int Top
 32         {
 33             get
 34             {
 35                 return top;
 36             }
 37         }
 38         //構造器
 39         public SeqStack(int size)
 40         {
 41             data = new T[size];
 42             maxsize = size;
 43             top = -1;
 44         }
 45         //求棧的長度
 46         public int GetLength()
 47         {
 48             return top + 1;
 49         }
 50         //清空順序棧
 51         public void Clear()
 52         {
 53             top = -1;
 54         }
 55         //判斷順序棧是否為空
 56         public bool IsEmpty()
 57         {
 58             if (top == -1)
 59             {
 60                 return true;
 61             }
 62             else
 63             {
 64                 return false;
 65             }
 66         }
 67         //判斷順序棧是否為滿
 68         public bool IsFull()
 69         {
 70             if (top == maxsize - 1)
 71             {
 72                 return true;
 73             }
 74             else
 75             {
 76                 return false;
 77             }
 78         }
 79         //入棧
 80         public void Push(T item)
 81         {
 82             if (IsFull())
 83             {
 84                 Console.WriteLine("Stack is full");
 85                 return;
 86             }
 87             data[++top] = item;
 88         }
 89         //出棧
 90         public T Pop()
 91         {
 92             T tmp = default(T);
 93             if (IsEmpty())
 94             {
 95                 Console.WriteLine("Stack is empty");
 96                 return tmp;
 97             }
 98             tmp = data[top];
 99             --top;
100             return tmp;
101         }
102         //獲取棧頂數據元素
103         public T GetTop()
104         {
105             if (IsEmpty())
106             {
107                 Console.WriteLine("Stack is empty!");
108                 return default(T);
109             }
110             return data[top];
111         }
112     }
View Code

 

2、鏈棧  

  棧的另外一種存儲方式是鏈式存儲,這樣的棧稱為鏈棧(Linked Stack)。鏈棧通常用單鏈表來表示,它的實現是單鏈表的簡化。所以,鏈棧結點的結構與單鏈表結點的結構一樣,如圖 3.3 所示。由於鏈棧的操作只是在一端進行,為了操作方便,把棧頂設在鏈表的頭部,並且不需要頭結點。

  鏈棧結點類(Node<T>)的實現如下:

  

 1 public class Node<T>
 2     {
 3         private T data; //數據域
 4         private Node<T> next; //引用域
 5         //構造器
 6         public Node(T val, Node<T> p)
 7         {
 8             data = val;
 9             next = p;
10         }
11         //構造器
12         public Node(Node<T> p)
13         {
14             next = p;
15         }
16         //構造器
17         public Node(T val)
18         {
19             data = val;
20             next = null;
21         }
22         //構造器
23         public Node()
24         {
25             data = default(T);
26             next = null;
27         }
28         //數據域屬性
29         public T Data
30         {
31             get
32             {
33                 return data;
34             }
35             set
36             {
37                 data = value;
38             }
39         }
40         //引用域屬性
41         public Node<T> Next
42         {
43             get
44             {
45                 return next;
46             }
47             set
48             {
49                 next = value;
50             }
51         }
52     }
View Code

 

  

 

   鏈棧類 LinkStack<T>的實現說明如下所示。

 1 public class LinkStack<T> : IStack<T>
 2     {
 3         private Node<T> top; //棧頂指示器
 4         private int num; //棧中結點的個數
 5         //棧頂指示器屬性
 6         public Node<T> Top
 7         {
 8             get
 9             {
10                 return top;
11             }
12             set
13             {
14                 top = value;
15             }
16         }
17         //元素個數屬性
18         public int Num
19         {
20             get
21             {
22                 return num;
23             }
24             set
25             {
26                 num = value;
27             }
28         }
29         //構造器
30         public LinkStack()
31         {
32             top = null;
33             num = 0;
34         }
35         //求鏈棧的長度
36         public int GetLength()
37         {
38             return num;
39         }
40         //清空鏈棧
41         public void Clear()
42         {
43             top = null;
44             num = 0;
45         }
46         //判斷鏈棧是否為空
47         public bool IsEmpty()
48         {
49             if ((top == null) && (num == 0))
50             {
51                 return true;
52             }
53             else
54             {
55                 return false;
56             }
57         }
58         //入棧
59         public void Push(T item)
60         {
61             Node<T> q = new Node<T>(item);
62             if (top == null)
63             {
64                 top = q;
65             }
66             else
67             {
68                 q.Next = top;
69                 top = q;
70             }
71             ++num;
72         }
73         //出棧
74         public T Pop()
75         {
76             if (IsEmpty())
77             {
78                 Console.WriteLine("Stack is empty!");
79                 return default(T);
80             }
81             Node<T> p = top;
82             top = top.Next;
83             --num;
84             return p.Data;
85         }
86         //獲取棧頂結點的值
87         public T GetTop()
88         {
89             if (IsEmpty())
90             {
91                 Console.WriteLine("Stack is empty!");
92                 return default(T);
93             }
94             return top.Data;
95         }
96     }
View Code

  

3:C#中的棧

  C#2.0 以下版本只提供了非泛型的 Stack 類,該類繼承了 ICollection、IEnumerable 和 ICloneable 接口。C#2.0 提供了泛型的 Stack<T>類,該類繼承了 IEnumerable<T>、ICollection 和 IEnumerable 接口。

 

二:隊列

1:隊列的定義及基本運算

  隊列(Queue)是插入操作限定在表的尾部而其它操作限定在表的頭部進行的線性表。把進行插入操作的表尾稱為隊尾(Rear),把進行其它操作的頭部稱為頭(Front)。當對列中沒有數據元素時稱為空對列(Empty Queue)

  隊列通常記為:Q= (a 1 ,a 2 ,…,a n ),Q是英文單詞queue的第 1 個字母。a 1 為隊頭元素,a n 為隊尾元素。這n個元素是按照a 1 ,a 2 ,…,a n 的次序依次入隊的,出對的次序與入隊相同,a 1 第一個出隊,a n 最后一個出隊。所以,對列的操作是按照先進先出(First In First Out)或后進后出( Last In Last Out)的原則進行的,因此,隊列又稱為FIFO表或LILO表。隊列Q的操作如下圖。

  

  隊列的操作是線性表操作的一個子集。隊列的操作主要包括在隊尾插入元素、在隊頭刪除元素、取隊頭元素和判斷隊列是否為空等。與棧一樣,隊列的運算是定義在邏輯結構層次上的,而運算的具體實現是建立在物理存儲結構層次上的。因此,把隊列的操作作為邏輯結構的一部分,每個操作的具體實現只有在確定了隊列的存儲結構之后才能完成。隊列的基本運算不是它的全部運算,而是一些常用的基本運算。

  隊列接口 IQueue<T>的定義如下所示。

1 public interface IQueue<T>
2     {
3         int GetLength(); //求隊列的長度
4         bool IsEmpty(); //判斷對列是否為空
5         void Clear(); //清空隊列
6         void In(T item); //入隊
7         T Out(); //出隊
8         T GetFront(); //取對頭元素
9     }
View Code

 

2:隊列的存儲和運算實現

1、順序隊列  

  用一片連續的存儲空間來存儲隊列中的數據元素,這樣的隊列稱為順序隊列(Sequence Queue)。

    類似於順序棧,用一維數組來存放順序隊列中的數據元素。隊頭位置設在數組下標為 0 的端,用 front 表示;隊尾位置設在數組的另一端,用 rear 表示。front 和 rear 隨着插入和刪除而變化。當隊列為空時,front=rear=-1。下圖是順序隊列的兩個指示器與隊列中數據元素的關系圖。

  當有數據元素入隊時,隊尾指示器 rear 加 1,當有數據元素出隊時,隊頭指示器 front 加 1。當 front=rear 時,表示隊列為空,隊尾指示器 rear 到達數組的上限處而 front 為-1 時,隊列為滿,如圖(c)所示。隊尾指示器 rear 的值大於隊頭指示器 front 的值,隊列中元素的個數可以由 rear-front 求得

  由圖 (d)可知,如果再有一個數據元素入隊就會出現溢出。但事實上隊列中並未滿,還有空閑空間,把這種現象稱為“假溢出”。這是由於隊列“隊尾入隊頭出”的操作原則造成的。解決假溢出的方法是將順序隊列看成是首尾相接的循環結構,頭尾指示器的關系不變,這種隊列叫循環順序隊列( Circular sequenceQueue )。循環隊列如下圖。

 

  當隊尾指示器 rear 到達數組的上限時,如果還有數據元素入隊並且數組的第0 個空間空閑時,隊尾指示器 rear 指向數組的 0 端。所以,隊尾指示器的加 1 操作修改為:

          rear = (rear + 1) % maxsize  

  隊頭指示器的操作也是如此。當隊頭指示器 front 到達數組的上限時,如果還有數據元素出隊,隊頭指示器 front 指向數組的 0 端。所以,隊頭指示器的加1 操作修改為:

          front = (front + 1) % maxsize

 2:循環順序隊列操作示意圖如下

  

  由圖可知,隊尾指示器 rear 的值不一定大於隊頭指示器 front 的值,並且隊滿和隊空時都有 rear=front。也就是說,隊滿和隊空的條件都是相同的。解決這個問題的方法一般是少用一個空間,如圖 (d)所示,把這種情況視為隊滿。所以,判斷隊空的條件是:rear==front,判斷隊滿的條件是:(rear + 1)% maxsize==front。求循環隊列中數據元素的個數可由(rear-front+maxsize)%maxsize公式求得。

  1 public class CSeqQueue<T> : IQueue<T>
  2     {
  3         private int maxsize; //循環順序隊列的容量
  4         private T[] data; //數組,用於存儲循環順序隊列中的數據元素
  5         private int front; //指示循環順序隊列的隊頭
  6         private int rear; //指示循環順序隊列的隊尾
  7         //索引器
  8         public T this[int index]
  9         {
 10             get
 11             {
 12                 return data[index];
 13             }
 14             set
 15             {
 16                 data[index] = value;
 17             }
 18         }
 19         //容量屬性
 20         public int Maxsize
 21         {
 22             get
 23             {
 24                 return maxsize;
 25             }
 26             set
 27             {
 28                 maxsize = value;
 29             }
 30         }
 31         //隊頭屬性
 32         public int Front
 33         {
 34             get
 35             {
 36                 return front;
 37             }
 38             set
 39             {
 40                 front = value;
 41             }
 42         }
 43         //隊尾屬性
 44         public int Rear
 45         {
 46             get
 47             {
 48                 return rear;
 49             }
 50             set
 51             {
 52                 rear = value;
 53             }
 54 
 55         }
 56         //構造器
 57         public CSeqQueue(int size)
 58         {
 59             data = new T[size];
 60             maxsize = size;
 61             front = rear = -1;
 62         }
 63         //求循環順序隊列的長度
 64         public int GetLength()
 65         {
 66             return (rear - front + maxsize) % maxsize;
 67         }
 68         //清空循環順序隊列
 69         public void Clear()
 70         {
 71             front = rear = -1;
 72         }
 73         //判斷循環順序隊列是否為空
 74         public bool IsEmpty()
 75         {
 76             if (front == rear)
 77             {
 78                 return true;
 79             }
 80             else
 81             {
 82                 return false;
 83             }
 84         }
 85         //判斷循環順序隊列是否為滿
 86         public bool IsFull()
 87         {
 88             if ((rear + 1) % maxsize == front)
 89             {
 90                 return true;
 91             }
 92             else
 93             {
 94                 return false;
 95             }
 96         }
 97         //入隊
 98         public void In(T item)
 99         {
100             if (IsFull())
101             {
102                 Console.WriteLine("Queue is full");
103                 return;
104             }
105             data[++rear] = item;
106         }
107         //出隊
108         public T Out()
109         {
110             T tmp = default(T);
111             if (IsEmpty())
112             {
113                 Console.WriteLine("Queue is empty");
114                 return tmp;
115             }
116             tmp = data[++front];
117             return tmp;
118         }
119         //獲取隊頭數據元素
120         public T GetFront()
121         {
122             if (IsEmpty())
123             {
124                 Console.WriteLine("Queue is empty!");
125                 return default(T);
126             }
127             return data[front + 1];
128         }
129     }
View Code

  

 3:鏈隊列

  隊列的另外一種存儲方式是鏈式存儲,這樣的隊列稱為鏈隊列(LinkedQueue)。同鏈棧一樣,鏈隊列通常用單鏈表來表示,它的實現是單鏈表的簡化。所以,鏈隊列的結點的結構與單鏈表一樣,由於鏈隊列的操作只是在一端進行,為了操作方便,把隊頭設在鏈表的頭部,並且不需要頭結點。

節點示意圖:

        

 

 

鏈隊列示意圖:

 

          

  把鏈隊列看作一個泛型類,類名為 LinkQueue<T>。LinkQueue<T>類中有兩個字段 front 和 rear,表示隊頭指示器和隊尾指示器。由於隊列只能訪問隊頭的數據元素,而鏈隊列的隊頭指示器和隊尾指示器又不能指示隊列的元素個數,所以,與鏈棧一樣,在 LinkQueue<T>類增設一個字段 num 表示鏈隊列中結點的個。鏈隊列類 LinkQueue<T>的實現說明如下所示。

  

  1 public class LinkQueue<T> : IQueue<T>
  2     {
  3         private Node<T> front; //隊列頭指示器
  4         private Node<T> rear; //隊列尾指示器
  5         private int num; //隊列結點個數
  6         //隊頭屬性
  7         public Node<T> Front
  8         {
  9             get
 10             {
 11                 return front;
 12             }
 13             set
 14             {
 15                 front = value;
 16             }
 17         }
 18         //隊尾屬性
 19         public Node<T> Rear
 20         {
 21             get
 22             {
 23                 return rear;
 24             }
 25             set
 26             {
 27                 rear = value;
 28             }
 29         }
 30         //隊列結點個數屬性
 31         public int Num
 32         {
 33             get
 34             {
 35                 return num;
 36             }
 37             set
 38             {
 39                 num = value;
 40             }
 41         }
 42         //構造器
 43         public LinkQueue()
 44         {
 45             front = rear = null;
 46             num = 0;
 47         }
 48         //求鏈隊列的長度
 49         public int GetLength()
 50         {
 51             return num;
 52         }
 53         //清空鏈隊列
 54         public void Clear()
 55         {
 56             front = rear = null;
 57             num = 0;
 58         }
 59         //判斷鏈隊列是否為空
 60         public bool IsEmpty()
 61         {
 62             if ((front == rear) && (num == 0))
 63             {
 64                 return true;
 65             }
 66             else
 67             {
 68                 return false;
 69             }
 70         }
 71         //入隊
 72         public void In(T item)
 73         {
 74             Node<T> q = new Node<T>(item);
 75             if (rear == null)
 76             {
 77                 rear = q;
 78             }
 79             else
 80             {
 81                 rear.Next = q;
 82                 rear = q;
 83             }
 84             ++num;
 85         }
 86         //出隊
 87         public T Out()
 88         {
 89             if (IsEmpty())
 90             {
 91                 Console.WriteLine("Queue is empty!");
 92                 return default(T);
 93             }
 94             Node<T> p = front;
 95             front = front.Next;
 96             if (front == null)
 97             {
 98                 rear = null;
 99             }
100             --num;
101             return p.Data;
102         }
103         //獲取鏈隊列頭結點的值
104         public T GetFront()
105         {
106             if (IsEmpty())
107             {
108                 Console.WriteLine("Queue is empty!");
109                 return default(T);
110             }
111             return front.Data;
112         }
113     }
View Code

 

  3.1:C# 中的隊列

  C#2.0 以下版本只提供了非泛型的 Queue 類,該類繼承了 ICollection、IEnumerable 和 ICloneable 接口。C#2.0 提供了泛型的 Queue<T>類,該類繼承了 IEnumerable<T>、ICollection 和 IEnumerable 接口。以下程序說明了泛型Queue<T>類的主要方法,並對在我們自定義的隊列類中沒有出現的成員方法進行了注釋,關於泛型 Queue<T>類的更具體的信息,讀者可參考.NET Framework的有關書籍。  

 

三 : 小結

  棧和隊列是計算機中常用的兩種數據結構,是操作受限的線性表。棧的插入和刪除等操作都在棧頂進行,它是先進后出的線性表。隊列的刪除操作在隊頭進行,而插入、查找等操作在隊尾進行,它是先進先出的線性表。與線性表一樣,棧和隊列有兩種存儲結構,順序存儲的棧稱為順序棧,鏈式存儲的棧稱為鏈棧。順序存儲的隊列稱為順序對列,鏈式存儲的隊列稱為鏈隊列。

  為解決順序隊列中的假溢出問題,采用循環順序隊列,但出現隊空和隊滿的判斷條件相同的問題,判斷條件都是:front==rear。采用少用一個存儲單元來解決該問題。此時,隊滿的判斷條件是:(rear+1)%maxsize==front,判斷隊空的條件是:rear==front。

  棧適合於具有先進后出特性的問題,如括號匹配、表達式求值等問題;隊列適合於具有先進先出特性的問題,如排隊等問題。

 


免責聲明!

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



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