15. C#資料結構與演算法 -- 棧

來源:互聯網
上載者:User

標籤:c#

這節我們討論了兩種好玩的資料結構,棧和隊列。

什麼是棧, 所謂的棧是棧(Stack)是操作限定在表的尾端進行的線性表。表尾由於要進行插入、刪除等操作,所以,它具有特殊的含義,把表尾稱為棧頂(Top) ,另一端是固定的,叫棧底(Bottom) 。當棧中沒有資料元素時叫空棧(Empty Stack)。這個類似於送飯的飯盒子,上層放的是紅燒肉,中層放的水煮魚,下層放的雞腿。你要把這些菜取出來,這就引出來了棧的特點先進後出(First in last out)。   具體敘述,加:

650) this.width=650;" width="620" height="221" src="http://files.jb51.net/file_images/article/201211/2012110120523833.png" />

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


650) this.width=650;" src="http://files.jb51.net/file_images/article/201211/2012110120523834.png" />


棧的形式化定義為:棧(Stack)簡記為 S,是一個二元組,顧定義為S = (D, R)
其中:D 是資料元素的有限集合;
R 是資料元素之間關係的有限集合。

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

同樣,我們以 C#語言的泛型介面來表示棧,介面中的方法成員表示基本操作。為表示的方便與簡潔,把泛型棧介面取名為 IStack(實際上,在 C#中沒有泛型介面 IStack<T>, 泛型棧是從 IEnumerable<T>和 ICollection 等介面繼承而來,這一點與線性表有著本質的區別) 。

棧的介面定義原始碼如下所示。

public interface IStack<T> {

//初始條件:棧存在;操作結果:返回棧中資料元素的個數。

int GetLength(); //求棧的長度   虛擬碼 index++

//初始條件:棧存在; 操作結果:如果棧為空白返回 true,否則返回 false。虛擬碼 if(top==null) return true;else return false;

bool IsEmpty(); //判斷棧是否為空白

//初始條件:棧存在; 操作結果:使棧為空白。虛擬碼 top=null;

void Clear(); //清空操作

//初始條件:棧存在; 操作結果:將值為 item 的新的資料元素添加到棧頂,棧發生變化。虛擬碼 top=item;index++;

void Push(T item); //入棧操作

//初始條件:棧存在且不為空白; 操作結果:將棧頂元素從棧中取出,棧發生變化   虛擬碼:return top;index--;

T Pop(); //出棧操作

//初始條件:棧表存在且不為空白; 操作結果:返回棧頂元素的值,棧不發生變化。虛擬碼 get top;

T GetTop(); //取棧頂元素
}

棧也分為兩種的形式,一種是順序棧,一種是鏈棧。

第一種 順序棧(Sequence Stack):

用一片連續的儲存空間來儲存棧中的資料元素,這樣的棧稱為順序棧(Sequence Stack)。類似於順序表,用一維數組來存放順序棧中的資料元素。棧頂指標 top 設在數組下標為 0 的端,top 隨著插入和刪除而變化,當棧為空白時,top=-1。是順序棧的棧頂指標 top與棧中資料元素的關係圖。

650) this.width=650;" width="620" height="315" src="http://files.jb51.net/file_images/article/201211/2012110120523835.png" />


順序棧類 SeqStack<T>原始碼的實現如下所示。

public class SeqStack<T> : IStack<T> {
private int maxsize; //順序棧的容量 最大的儲存空間
private T[] data; //數組,用於儲存順序棧中的資料元素 儲存資料的多少 
private int top; //指示順序棧的棧頂  棧頂指標

//索引器
public T this[int index]
{
get
{
return data[index];
}
set
{
data[index] = value;
}
}

//容量屬性
public int Maxsize
{
get
{
return maxsize;
}

set
{
maxsize = value;
}
}

//棧頂屬性
public int Top
{
get
{
return top;
}
}

//構造器  進行相應初始化的工作    進行賦值
public SeqStack(int size)
{
data = new T[size];
maxsize = size;
top = -1;
}

//求棧的長度   用頭指標來加一
public int GetLength()
{
return top+1;
}


650) this.width=650;" width="620" height="243" src="http://files.jb51.net/file_images/article/201211/2012110120523836.png" />


//判斷順序棧是否為空白

//就是判斷頭指標是否為-1 為就為空白 不為就為假

public bool IsEmpty()
{
if (top == -1)
{
return true;
}
else
{
return false;
}
}

具體如所示:

650) this.width=650;" width="620" height="280" src="http://files.jb51.net/file_images/article/201211/2012110120523837.png" />

//判斷順序棧是否為滿 或最大尺寸相比較 相等 返回真 不相等返回假
public bool IsFull()
{
if (top == maxsize-1)
{
return true;
}
else
{
return false;
}
}

相應情況,一切盡在圖例中。

650) this.width=650;" width="620" height="259" src="http://files.jb51.net/file_images/article/201211/2012110120523838.png" />

//入棧 將其放入頂部 top 加加
public void Push(T item)
{

//如果滿了 就不進行添加

if(IsFull())
{
Console.WriteLine("Stack is full");
return;
}
//進行加入到頂部
data[++top] = item;
}

具體情況,一切盡在圖例中

650) this.width=650;" width="620" height="234" src="http://files.jb51.net/file_images/article/201211/2012110120523839.png" />

//出棧 進行出棧後 頭指標減減
public T Pop()
{
T tmp = default(T);
if (IsEmpty())
{

Console.WriteLine("Stack is empty");
return tmp;
}

tmp = data[top];
--top;
return tmp;

具體情況,一切盡在圖例中。

650) this.width=650;" width="620" height="243" src="http://files.jb51.net/file_images/article/201211/2012110120523840.png" />


//擷取棧頂資料元素 把頭指標指向的元素進行彈出的操作
public T GetTop()
{

//如果是空 就返回一個預設值
if (IsEmpty())
{
Console.WriteLine("Stack is empty!");
return default(T);
}

return data[top];

具體情況,一切盡在圖例中:


}

 


}

}

這就是對順序棧的相應的介紹。

下面,我們就來到了另一種棧——鏈棧的介紹

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

650) this.width=650;" width="620" height="258" src="http://files.jb51.net/file_images/article/201211/2012110120523841.png" />

}

 


}

}

這就是對順序棧的相應的介紹。

下面,我們就來到了另一種棧——鏈棧的介紹

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

650) this.width=650;" src="http://files.jb51.net/file_images/article/201211/2012110120523842.png" />

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

public class Node<T>
{
private T data; //資料域
private Node<T> next; //參考網域

//構造器
public Node(T val, Node<T> p)
{
data = val;
next = p;
}

//構造器

public Node(Node<T> p)
{
next = p;
}

//構造器
public Node(T val)
{
data = val;
next = null;
}

//構造器
public Node()
{
data = default(T);
next = null;
}

//資料域屬性
public T Data
{
get
{
return data;
}
set
{
data = value;
}
}

//參考網域屬性
public Node<T> Next
{
get
{
return next;
}
set
{
next = value;
}
}

}

是鏈棧。 

650) this.width=650;" width="620" height="64" src="http://files.jb51.net/file_images/article/201211/2012110120523843.png" />

把鏈棧看作一個泛型類,類名為 LinkStack<T>。LinkStack<T>類中有一個欄位 top表示棧頂指標。由於棧只能訪問棧頂的資料元素,而鏈棧的棧頂指標又不能指示棧的資料元素的個數。所以,求鏈棧的長度時,必須把棧中的資料元素一個個出棧, 每出棧一個資料元素, 計數器就增加 1, 但這樣會破壞棧的結構。為保留棧中的資料元素, 需把出棧的資料元素先壓入另外一個棧, 計算完長度後,再把資料元素壓入原來的棧。但這種演算法的空間複雜度和時間複雜度都很高,所以, 以上兩種演算法都不是理想的解決方案。 理想的解決方案是 LinkStack<T>類增設一個欄位 num表示鏈棧中結點的個數。

鏈棧類 LinkStack<T>的實現說明如下所示。
public class LinkStack<T> : IStack<T> {
private Node<T> top; //棧頂指標
private int num; //棧中結點的個數

//棧頂指標屬性
public Node<T> Top
{
get
{
return top;
}
set
{
top = value;
}
}

//元素個數屬性 進行了計數 
public int Num
{
get
{
return num;
}
set
{
num = value;
}
}

//構造器 進行了函數的初始化
public LinkStack()
{
top = null;
num = 0;
}

//求鏈棧的長度   返回計算的複雜度  此演算法的複雜度是O(1)
public int GetLength()
{
return num;
}

//清空鏈棧 進行清空的操作 此演算法的複雜度是O(1)
public void Clear()
{
top = null;
num = 0;
}

//判斷鏈棧是否為空白   判斷 計數的變數和頭指標是否是空  返回為真  否則 為假  此演算法的複雜度是O(n)
public bool IsEmpty()
{
if ((top == null) && (num == 0))
{
return true;
}
else
{
return false;
}
}

//入棧 進行棧內 入棧的操作
public void Push(T item)
{
Node<T> q = new Node<T>(item);

if (top == null)
{
top = q;
}
else

{
q.Next = top;
top = q;
}

++num;
}

//出棧 進行出棧的操作 頭指標相減。此演算法的複雜度為1
public T Pop()
{
if (IsEmpty())
{
Console.WriteLine("Stack is empty!");
return default(T);
}

Node<T> p = top;
top = top.Next;
--num;

return p.Data;
}

//擷取棧頂結點的值  返回頭指標的值 此演算法的複雜度為一。
public T GetTop()
{
if (IsEmpty())
{
Console.WriteLine("Stack is empty!");
return default(T);
}
return top.Data;
}

 

}

這就是鏈棧的介紹的。還介紹一個棧的明顯的應用,這就是簡易萬能計算機的應用。

我們都知道在使用算符優先文法時必須使用兩個基本棧,數棧(operand stack)和運算子棧(operator stack),來完成計算工作,然而單單使用這兩個棧有一定的局限性,因此在設計時,我引入了第三個棧(op stack),下面我們就來分析一下。

在使用兩個棧時,如果遇到運算式 2-3*/6#,會發生什麼呢?

步驟號

數字棧

運算子棧

當前輸入

剩餘字串

說明

1

#

 

2-3*/6#

 

2

#

2

-3*/6#

 

3

2

#

-

3*/6#

 

4

2

# -

3

*/6#

 

5

2 3

# -

*

/6#

 

6

2 3

# - *

/

6#

*>/,運算2*3,-</,push(/)

7

6

# - /

6

#

 

8

6 6

# - /

#

 

/>#,運算6/6,

->#,試圖運算,由於缺少數符,報錯,錯誤定位在減號

9

# -

 

 

 

此時,錯誤資訊為:在minus附近可能存在錯誤。但實際上問題出在*或/號附近,這種報錯的定位結果是不能令人滿意的。

於是讓我們看看如果引入第三個棧作符號棧會如何?符號棧的功能是儲存所有分析過程中的符號,包括數符和運算子兩種。

步驟號

數字棧

運算子棧

符號棧

當前輸入

剩餘字串

說明

1

#

#

 

2-3*/6#

 

2

#

#

2

-3*/6#

 

3

2

#

# 2

-

3*/6#

 

4

2

# -

# 2 - 

3

*/6#

 

5

2 3

# -

# 2 – 3

*

/6#

 

6

2 3

# - *

# 2 – 3 *

/

6#

*>/,運算2*3,-</,push(/)

7

6

# - /

# 2 6 /

6

#

 

8

6 6

# - /

# 2 6 / 6

#

 

/>#, 拋出6後,先對/和棧中的6做絕對鄰近檢查,再對6和2做絕對鄰近檢查,但卻發現6和2不能相鄰,所以報錯,此時錯誤定位於除號

錯誤定位在/號,錯誤資訊為:在divide附近存在錯誤。這樣將使使用者更有可能找到運算式中的問題所在。我們通過每次運算時(對應於SemanticAnalyzer.FakeCalculate()方法),利用了絕對相鄰優先順序表對符號棧的彈出符號進行相鄰性檢查,只要發現棧頂的兩個符號不能相鄰,則馬上報錯。具體情況,:


650) this.width=650;" width="620" height="431" src="http://files.jb51.net/file_images/article/201211/2012110120523844.png" />



參考: http://www.jb51.net/article/31699.htm

 

本文出自 “Ricky's Blog” 部落格,請務必保留此出處http://57388.blog.51cto.com/47388/1663515

15. C#資料結構與演算法 -- 棧

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.