1. 程式人生 > >[學習筆記]鏈棧及棧的應用

[學習筆記]鏈棧及棧的應用

本文基於"姓名標識-非商業性-相同方式分享 4.0國際"協議創作或轉載,轉載原創文章請註明來源於瘋子的自留地,否則請勿轉載或再轉載,謝謝合作:)

既然棧就是線性表,那麼同樣的也可以用連結串列的形式來表示,當然用連結串列表示的棧有一個學名,曰:鏈棧.

不過跟連結串列還是有一些不同,比如說頭結點不用了,比如說,咱們得單獨鼓搗一個結構體來儲存棧頂的地址.好了,其它沒什麼,用程式碼說話吧

資料結構:

typedef struct Node{
	char Data;
	struct Node *next;
}*TopStack;

typedef struct {
	int Count;
	TopStack Top;
}TopPointer;

1.初始化:

BOOL InitializeStack(TopPointer *TP)
{
	TP=(TopPointer*)malloc(sizeof(TopPointer));//malloc the space to the TopPointer

	if(!TP)
		return false;

	TP->Count=0;
	TP->Top=NULL;

	return true;
}

2.入棧:

BOOL PushStack(TopPointer *TP,char TopValue)
{
	TopStack NewTop;

	NewTop=(TopStack)malloc(sizeof(TopStack));//malloc the new top value space
	if(!NewTop)
		return false;

	NewTop->Data=TopValue;//assign the value
	NewTop->next=TP->Top;

	TP->Top=NewTop;//point to the new top value address
	TP->Count++;

	return true;
}

3.出棧:

BOOL PopStack(TopPointer *TP,char *OldTopValue)
{
	TopStack OldTop;

	if(true==isStackEmpty(*TP))
		return false;

	OldTop=TP->Top;//get the old top address
	*OldTopValue=OldTop->Data;//get the old top value
	TP->Top=OldTop->next;//top pointer point to new top address
	TP->Count--;
	free(OldTop);//free the old top space

	return true;
}

4.檢視棧頂資料:

BOOL GetTopValue(TopPointer TP,char *TopValue)
{
	if(true==isStackEmpty(TP))
		return false;

	*TopValue=(TP.Top)->Data;

	return true;
}

5.是否為空棧:

BOOL isStackEmpty(TopPointer TP)
{
	if(0==TP.Count)
		return true;
	return false;
}

6.棧的資料個數:

int StackLength(TopPointer TP)
{
	return TP.Count;
}

老規矩,寫個程式跑一邊,實踐得真知不是?上程式碼:

#define true 1
#define false 0
#define error -1
#define BOOL int

#include<stdlib.h>
#include<stdio.h>

int main(void)
{
	TopPointer *TP;
	char PopValue;

	printf("Initialize the Stack...");
	if(false==InitializeStack(TP))
	{
		printf("Initialize Stack fail!The program will  be quited right now!\n");
		return -1;
	}
	printf("Initialize success!The length of the stack is %d\n",StackLength(*TP));
	printf("Push value 'a' to the stack!\n");
	if(false==PushStack(TP,'a'))
	{
		printf("Fail to push the value 'a'!");
	}
	printf("Success to push!Now the Length of the Stack is %d\n",StackLength(*TP));
	printf("Pop value...\n");
	if(false==PopStack(TP,&PopValue))
	{
		printf("Fail to pop the value!\n");
	}
	printf("Success to pop value '%c',the length of the stack is %d\n",PopValue,StackLength(*TP));

	return 0;
}

執行截圖:

好了,說下棧的應用,最明顯的遞迴了.話說當初學C的時候遞迴讓我迷惑了好一會來著,尤其是老譚上面的那個碟子問題,還好,最後秒殺掉了.最著名的遞迴就是斐波那契數列.這裡就不講了.

      第二個應用就是字尾表示式,神馬是字尾表示式?那個,就是...就是偉大滴波蘭邏輯學家Jan Lukasiewicz老人家發明的逆波蘭表示法.尼瑪!什麼叫做逆波蘭!那個...其實就是利用棧的先入後出原理來讓計算機求四則運算的一種演算法.用大話上的話講就是"所有的符號都要在運算數字的後面出現".比如說書上的這個例子,咱們的運算"9+(3-1)*3+10/2"用字尾表示式表示就是"9 3 1 - 3 * + 10 2 / +",忘記說了,我們以前學的都是中綴表示式.

      好了,怎麼個轉換法呢?中綴法轉字尾法就是這麼個規則:從左到右遍歷中綴表示式的每個數字和符號,若是數字就輸出,即成為字尾表示式的一部分;若是符號,則判斷其與棧頂符號的優先順序,是右括號或優先順序不高於棧頂符號(乘除優先於加減)則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最終輸出字尾表示式為止.額,看不懂?太刻板?好吧,用例子說話吧!還是那個"9+(3-1)*3+10/2".看好了啊,咱們搞倆個棧,棧1放最後的最後的結果,棧2用來臨時房運算子號.首先9進棧1,然後是+號進棧2,接著左括號)進棧2,接下來是3進棧1,然後-進棧2,然後數字1進棧1,接下來是右括號,咱們從小學就知道括號裡面得先運算是吧,這裡也一樣,棧2裡面的-符號進棧1,如果你跟我一起在紙上演算,那麼現在可以看到棧1從棧底到棧頂依次是元素9 3 1 -,好了,咱們繼續,*號進棧2,然後3進棧1,接著+號進棧2,咳咳,注意了,按照規則"是右括號或優先順序不高於棧頂符號(乘除優先於加減)則棧頂元素依次出棧並輸出",*號從棧2輸出然後壓入棧1,因為現在棧2是倆個+號,相同符號啊,注意相同符號的話就當做比現在的這個符號高階,所以原來的那個棧2的棧底元素+運算子號也從棧1出棧壓入棧2,然後把咱們從我們的中綴表示式裡面拿出來的這個+號壓入棧2,現在棧1的元素從棧底到棧頂依次是9 3 1 - 3 * +,棧2只有一個元素+,好了繼續,接下來是10,進棧1,然後是/符號,進棧2,好了,到了最後一個元素2了,廢話當然進棧1了,按照規則,當最後一個元素進棧1後,把棧2的元素分別出棧然後壓入棧1,於是就是棧2的/和+分別壓入棧1,好了,現在就得到我們的字尾表示式"9 3 1 - 3 * + 10 2 / +",灰常簡單吧,嘿嘿嘿

      再來看下字尾表示式的話計算機這廝是如何進行運算的呢?也先來規則吧:從左遍歷表示式的每個數字和符號,遇到是數字就進棧,遇到事符號就將棧頂兩個數字出棧,進行運算,一直到最終獲得結果.還是用上面的例子吧,上面我們不是得出了字尾表示式嗎,就用它吧,"9 3 1 - 3 * + 10 2 / +",首先初始化一個棧,然後9進棧,3進棧,1進棧,移到-符號了,按照規則​"遇到事符號就將棧頂兩個數字出棧,進行運算",把1和3分別出棧,然後3-1的2,再把這個2進棧,然後3進棧,*號來也,3和2出棧進行乘法運算,得6,6進棧,然後是+,好了,把棧底的9和棧頂的6出棧進行+法運算,的15,15進棧,然後10進棧,2進棧,到了/法了,2和10依次出來,皇上有請呢,除法後的5,5再進棧,哎呀,+號又來了,有完沒完,棧頂的15和剛剛才進棧的5只好穿好衣服出來進行運算,這大晚上的不折騰人嗎不是,好了,最後得20,額,咱們一看,咦,遍歷到最後了,木資料了,好了,這個20就是這個表示式的值了.是不是很神奇,灰常滴神奇?不管你覺得神不神奇,反正我當初學會這招時對這個已經入土為安的Jan老祖宗佩服的五體投地,你說會不會也是在蘋果樹下思考如何鼓搗出字尾表示式然後蘋果砸到了他就頓悟的呢,嘿嘿嘿

      本來想用Jan老祖宗的字尾表示式寫一個簡易的計算器的,時候不早了,明天鼓搗吧