數據結構和算法(二)單向循環鏈表的建立插入刪除實現github
數據結構和算法(五)棧和隊列的操做和實現shell
@[TOC]swift
棧是一種後進先出的結構,有一個棧底指針,一個棧頂指針,入棧只能從棧頂入棧,出棧也只能從棧頂出棧。它的結構示意圖以下: 數據結構
咱們能夠對比一下隊列:隊列是一種先進先出的數據結構,只能從隊尾入隊,從對頭出隊,隊列的結構圖以下圖: 數據結構和算法
//順序棧結構
typedef struct KSqueueStack{
KStackElementType data[MAXSIZE];
int top; //用於棧頂指針
}SqStack;
複製代碼
//1. 構建一個空棧S
KStatus initStack(SqStack *S) {
S->top = -1;
return OK;
}
複製代碼
//2. 將棧置空
KStatus clearStack(SqStack *S) {
S->top = -1;
return OK;
}```
#### 2.1.4 順序棧判空
```swift
//3. 判斷順序棧是否爲空
KStatus isEmpty(SqStack S) {
return S.top == -1 ;
}
複製代碼
//4. 獲取棧長度
int getLength(SqStack S) {
return S.top + 1;
}
複製代碼
//5. 獲取棧頂
KStatus getTop(SqStack S, KStackElementType *e) {
//棧空,則返回錯誤
if (S.top == -1) return ERROR;
*e = S.data[S.top];
return OK;
}
複製代碼
入棧前以下圖所示: 函數
入棧後以下圖所示: post
//6. 壓棧
KStatus push(SqStack *S, KStackElementType e) {
//判斷是否 棧滿
if (S->top == MAXSIZE -1) return ERROR;
//1. 棧頂指針+1;
//2. 將新插入的元素賦值給棧頂空間
//S->top ++;
//S->data[S->top] = e;
S->data[++(S->top)] = e;
return OK;
}
複製代碼
//7. 出棧
KStatus pop(SqStack *S, KStackElementType *e) {
//判斷是否棧空
if(S->top == -1) return ERROR;
//1. 將要刪除的棧頂元素賦值給e
//2. 棧頂指針--;
//*e = S->data[S->top];
//S->top--;
*e = S->data[S->top--];
return OK;
}
複製代碼
//8. 棧遍歷
KStatus traverse(SqStack S) {
int i = 0;
printf("棧全部元素:");
while (i < S.top) {
printf("%d ",S.data[i++]);
}
printf("\n");
return OK;
}
複製代碼
//9. 測試
void test() {
SqStack S;
int e;
if (initStack(&S) == OK) {
for (int j = 1 ; j < 10; j++) {
push(&S, j);
}
}
printf("順序棧中元素爲:\n");
traverse(S);
pop(&S, &e);
printf("彈出棧頂元素爲: %d\n",e);
traverse(S);
printf("是否爲空棧:%d\n",isEmpty(S));
getTop(S, &e);
printf("棧頂元素:%d \n棧長度:%d\n",e,getLength(S));
clearStack(&S);
printf("是否已經清空棧 %d, 棧長度爲:%d\n",isEmpty(S),getLength(S));
}
複製代碼
Hello, World!
順序棧中元素爲:
棧全部元素:1 2 3 4 5 6 7 8
彈出棧頂元素爲: 9
棧全部元素:1 2 3 4 5 6 7
是否爲空棧:0
棧頂元素:8
棧長度:8
是否已經清空棧 1, 棧長度爲:0
Program ended with exit code: 0
複製代碼
鏈式棧是有鏈表來實現的一種棧結構,它的結構示意圖以下圖:
//鏈棧結點
typedef struct KStackNode {
KStackElementType data; //結點數據
struct KStackNode *next; //指向下一個結點的指針 }StackNode, *LinkStackPtr; //鏈棧結構 typedef struct KLinkStack {
LinkStackPtr top; //棧頂結點
int count; //棧大小
}LinkStack;
複製代碼
//1. 構造一個空棧S
KStatus initStack(LinkStack *S) {
S->top = NULL;
S->count = 0;
return OK;
}
複製代碼
//2. 鏈棧置空
KStatus clearStack(LinkStack *S) {
LinkStackPtr p,q;
//p指向棧頂結點
p = S->top;
while (p) {
//保存要刪除的結點p
q = p;
//然p指向它的下一個結點
p = p->next;
//刪除 p結點
free(q);
}
return OK;
}
複製代碼
//3. 判斷棧是否爲空
KStatus isEmpty(LinkStack S) {
return S.count == 0;
}
複製代碼
//4. 獲取棧長度
int getLength(LinkStack S) {
return S.count;
}
複製代碼
//5. 獲取棧頂元素
KStatus getTop(LinkStack S, KStackElementType *e) {
//判斷是否棧空
if (S.top == NULL) return ERROR;
*e = S.top->data;
return OK;
}
複製代碼
鏈式棧結構,入棧示意圖以下:
//6. 壓棧
KStatus push(LinkStack *S, KStackElementType e) {
//1. 建立一個新結點,
LinkStackPtr newNode = (LinkStackPtr)malloc(sizeof(StackNode));
//2. 賦值給新結點
newNode->data = e;
//3. 插入新結點到棧頂結點後面
//3.1 把當前的棧頂元素的結點指針指向直接後繼結點
newNode->next = S->top;
//3.2 將新結點賦值給棧頂指針
S->top = newNode;
//棧大小+1
S->count++;
return OK;
}
複製代碼
鏈式棧結構,出棧示意圖以下圖:
//7. 出棧
KStatus pop(LinkStack *S, KStackElementType *e) {
LinkStackPtr p;
if (isEmpty(*S)) return ERROR;
//1. 將棧頂元素賦值給*e
*e = S->top->data;
//2. 將棧頂結點賦值給p
p = S->top;
//3. 使得棧頂指針下移一位, 指向後一結點
S->top = S->top->next;
//4. 釋放p結點
free(p);
//棧大小減1
S->count--;
return OK;
}
複製代碼
//8. 遍歷棧
KStatus traverse(LinkStack S) {
LinkStackPtr p = S.top;
printf("遍歷棧元素:");
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return OK;
}
複製代碼
//9. 單元測試
void test() {
int j;
LinkStack s;
int e;
if(initStack(&s)==OK)
for(j=1;j<=10;j++)
push(&s,j);
printf("棧中元素依次爲:");
traverse(s);
pop(&s,&e);
printf("彈出的棧頂元素 e=%d\n",e);
traverse(s);
printf("棧空否:%d(1:空 0:否)\n",isEmpty(s));
getTop(s,&e);
printf("棧頂元素 e=%d 棧的長度爲%d\n",e,getLength(s));
clearStack(&s);
printf("清空棧後,棧空否:%d(1:空 0:否)\n",isEmpty(s));
}
複製代碼
Hello, World!
棧中元素依次爲:遍歷棧元素:10 9 8 7 6 5 4 3 2 1
彈出的棧頂元素 e=10
遍歷棧元素:9 8 7 6 5 4 3 2 1
棧空否:0(1:空 0:否)
棧頂元素 e=9 棧的長度爲9
清空棧後,棧空否:0(1:空 0:否)
Program ended with exit code: 0
複製代碼
下⾯面3種狀況下,咱們會使⽤用到遞歸來解決問題
- 定義是遞歸的
- 數據結構是遞歸的
- 問題的解法是遞歸的
問題描述: 假若有3個分別命名爲A,B,C的塔座,在塔座A上插有n個直接⼤大⼩小各不不相同的,從⼩小到⼤大的 編號爲1,2,3...n的圓盤. 如今要求將塔座A上的n個圓盤移動到塔座C上. 並仍然按照一樣的順序疊 排. 圓盤移動時必須按照如下的規則:1. 每次只能移動⼀一個圓盤;2. 圓盤能夠插在A,B,C的任⼀一塔座 上;3. 任什麼時候刻都不不能將⼀一個較⼤大的圓盤壓在⼩小的圓盤之上.
求解過程圖以下:
在編譯系統中,算術表達式能夠分爲三類:算術表達式,關係表達式,邏輯表達式。
任何一個算術表達式都是由:操做數,運算符和分界符組成。咱們把操做數,運算符和分界符(分界符標誌了一個算術表達式的結束)稱爲一個算術表達式的單詞。
A+(B-C/D)*E
ABCD/-E*+
後綴表達式的特色:
- 後綴表達式的操做數和中綴表達式的操做數前後次序徹底相同(上面ABCDE),只是運算符的前後次序改變了(+-/*);
- 後綴表達式中沒有括號,後綴表達式的運算次序就是其執行次序
應用堆棧實現後綴表達式求值的基本過程:
從左到右讀入後綴表達式的各項(運算符或運算數):
- 運算數:入棧
- 運算符:從堆棧中彈出適當數量的運算數,計算並結果入棧
- 最後,堆棧頂上的元素就是表達式的結果值
基本策略:將中綴表達式轉換爲後綴表達式,而後求值。
2+9/3-5
-> 2 9 3 / +5 -
過程:
- 運算數相對順序不變
- 運算符號順序發生改變
- 須要存儲「等待中」的運算符號
- 要將當前運算符號與「等待中」的最後一個運算符號比較
從頭至尾讀取中綴表達式的每一個對象,對不一樣對象按不一樣的狀況處理。
- 運算數:直接輸出
- 左括號:壓入堆棧
- 右括號:將棧頂的運算符彈出並輸出,直到遇到左括號(出棧,不輸出)
- 運算符: (1) 若優先級大於棧頂運算符時,則把它壓棧 (2) 若優先級小於等於棧頂運算符時,將棧頂運算符彈出並輸出;再比較新的棧頂運算符,直到該運算符大於棧頂運算符優先級爲止,而後將該運算符壓棧
- 若各對象處理完畢,則把堆棧中存留的運算符一併輸出
編譯系統設置一個存放運算符的堆棧,初始時棧頂置一個分界符「#」。編譯系統從左到右依次掃描中綴表達式,每讀到一個操做數就把它做爲後綴表達式的一部分輸出,每讀到一個運算符(分界符也看做運算符)就將其優先級與棧頂運算符優先級運算符進行比較,以決定是就所讀到的運算符進棧,仍是將棧頂運算符做爲最爲後綴算術表達式的一部分輸出。
- 當O1爲「+」或「-」,O2爲「*」或「/」時,O1的優先級 < O2的優先級(知足先乘除,後加減)
- 當O1爲「+」「-」「*」或「/」,O2爲「(」時,O1的優先級 < O2的優先級(知足先括號內,後括號外的規則)
- 當O1的運算符和O2的運算符同級別時,O1的優先級 > O2的優先級別(同級別先左後右規則)
- 因爲後綴表達式無括號,當O1爲「(」,O2爲「)」時,用標記「=」使算法在此時去掉該對算法;
- 當O1爲「#」時,O2爲「#」時,用標記「=」使算法在此時結束處理
- 若表中的值爲空,則不容許出現這種狀況,一旦出現即爲中綴算術表達式語法出錯,如O1爲「)」,而O2爲「(」狀況,即爲中綴表達式語法錯誤!)。
- 設置一個堆棧,初始時將棧頂元素置爲#
- 順序讀入中綴算術表達式,當讀到的單詞爲操做數是就將其輸出,並接着讀下一個單詞
- 單讀到的單詞爲運算符時,令a爲當前棧頂運算符的變量,b爲當前掃描讀到運算符的變量,把當前讀到的運算符賦給b,而後比較變量a的優先級和b的優先級。若a的優先級高於b的優先級,則將a退棧並做爲後綴表達式的一個單詞輸出,,而後比較新的棧頂元素運算符a的優先級與b的優先級。
若優先級 a<b,則將b的值進棧,而後接着讀下一個單詞
若優先級 a>b,則將a退棧並做爲後綴表達式的一個單詞輸出,而後比較新的棧頂元素運算符a的優先級與b的優先級。
若優先級 a=b且a爲「(」,b爲「)」。則將a退棧,接着讀下一個單詞
若優先級 a=b且a爲「#」,b爲「#」。算法結束。
int PostExp(char str[]) //藉助堆棧計算後綴表達式str的值
{
KStackElementType x,x1,x2;
int i;
KNode *head; //定義頭指針變量head
initStack(&head); //初始化鏈式堆棧head
for(i-0;str[i]!=#;i++) //循環直到輸入爲#
{
if(isdigit(str[i])) //當str[i]爲操做數時
{
x=(int)(str[i]-48); //轉換成int類型數據存於變量x中
push(head,x); //x入棧
}
else //當str[i]爲運算符時
{
pop(head,&x2); //退棧的操做數,存於變量x2中
pop(head,&x1); //退棧的被操做數,存於變量x1中
switch(str[i]) //執行str[i]所表示的運算
{
case '+':
{
x1+=x2; break;
}
case '-':
{
x1-=x2; break;
}
case '*':
{
x1*=x2; break;
}
case '/':
{
if(x2==0.0)
{
printf("除數爲0錯誤!\n");
exit(0);
}
else
{
x1/=x2;
break;
}
}
}
push(head,x1); //運算結果入棧
}
}
pop(head,&x); //獲得計算結果存於x
return x; //返回計算結果
}
複製代碼
隊列:具備必定操做約束的線性表 有如下特色:
- 插入和刪除操做:只能在一端插入,而在另外一端刪除
- 數據插入:入隊(AddQ)
- 數據刪除:出隊列(DeleteQ)
- 先進先出:FIFO