1. 順序存儲(chǔ)
優(yōu)點(diǎn):
- 實(shí)現(xiàn)簡(jiǎn)單
缺點(diǎn):
- 長(zhǎng)度有限
1.1 結(jié)構(gòu)定義
#define MAXSIZE 20
typedef int Status;
typedef int SElemType;
typedef struct
{
SElemType data[MAXSIZE];
int top;
} SqStack;
1.2 函數(shù)實(shí)現(xiàn)
// 1.構(gòu)建一個(gè)空棧S
Status InitStack(SqStack *S) {
if (!S) return ERROR;
S->top = -1;
return OK;
}
// 2.將棧置空
Status ClearStack(SqStack *S) {
if (!S) return ERROR;
S->top = -1;
return OK;
}
// 3.判斷順序棧是否為空;
Status StackEmpty(SqStack S) {
if (S.top == -1)
return TRUE;
else
return FALSE;
}
// 4.返回棧的長(zhǎng)度
int StackLength(SqStack S) {
return S.top + 1;
}
// 5.獲取棧頂元素
Status GetTop(SqStack S, SElemType *e) {
if (S.top == -1)
return ERROR;
else
*e = S.data[S.top];
return OK;
}
// 6.壓棧
Status Push(SqStack *S, SElemType e) {
if (!S || S->top == MAXSIZE -1) return ERROR;
S->data[++S->top] = e;
return OK;
}
// 7.出棧
Status Pop(SqStack *S,SElemType *e) {
if (!S || S->top == -1) return ERROR;
*e = S->data[S->top--];
return OK;
}
// 8.從棧底到棧頂依次對(duì)棧中的每個(gè)元素打印
Status StackTraverse(SqStack S) {
int i = S.top;
while (i >= 0) {
printf("%d ",S.data[i--]);
}
printf("\n");
return OK;
}
// 輸出
順序棧的表示與實(shí)現(xiàn)!
順序棧中元素為:
9 8 7 6 5 4 3 2 1
彈出棧頂元素為: 9
8 7 6 5 4 3 2 1
是否為空棧: 0
棧頂元素: 8
棧長(zhǎng)度: 8
是否已經(jīng)清空棧 1, 棧長(zhǎng)度為: 0
2. 鏈?zhǔn)酱鎯?chǔ)
優(yōu)點(diǎn):
- 長(zhǎng)度無限(只要內(nèi)存夠)
缺點(diǎn):
- 實(shí)現(xiàn)復(fù)雜
- 節(jié)點(diǎn)的內(nèi)存管理
2.1 結(jié)構(gòu)定義
typedef int Status;
typedef int SElemType;
/* 鏈棧結(jié)構(gòu) */
typedef struct StackNode
{
SElemType data;
struct StackNode *next;
} StackNode, *StackNodePtr;
typedef struct
{
StackNodePtr top;
int count;
} LinkStack;
2.2 函數(shù)實(shí)現(xiàn)
// 1.構(gòu)造一個(gè)空棧S
Status InitStack(LinkStack *S) {
if (!S) return ERROR;
S->top = NULL;
S->count = 0;
return OK;
}
// 2.棧S置為空棧
Status ClearStack(LinkStack *S){
if (!S) return ERROR;
StackNodePtr p, q;
p = S->top;
while (p) {
q = p;
p = p->next;
free(q);
}
S->top = NULL;
S->count = 0;
return OK;
}
// 3.棧S是否為空棧
Status StackEmpty(LinkStack S) {
if (!S.top)
return TRUE;
else
return FALSE;
/*
if (S.count == 0)
return TRUE;
else
return FALSE;
*/
}
// 4.棧S的長(zhǎng)度
int StackLength(LinkStack S) {
return S.count;
}
// 5.返回棧頂元素
Status GetTop(LinkStack S, SElemType *e) {
if (!S.top)
return ERROR;
else
*e = S.top->data;
return OK;
}
// 6.壓棧
Status Push(LinkStack *S, SElemType e) {
if (!S) return ERROR;
StackNodePtr node = (StackNodePtr)malloc(sizeof(StackNode));
node->data = e;
node->next = S->top; // 頭插法
S->top = node; // 更換頭結(jié)點(diǎn)
S->count++;
return OK;
}
// 7.出棧
Status Pop(LinkStack *S, SElemType *e) {
if (!S || !S->top) return ERROR;
*e = S->top->data;
StackNodePtr node = S->top;
S->top = S->top->next; // 更換頭結(jié)點(diǎn)
free(node);
S->count--;
return OK;
}
// 8.遍歷棧
Status StackTraverse(LinkStack S) {
StackNodePtr node;
node = S.top;
while (node) {
printf("%d ",node->data);
node = node->next;
}
printf("\n");
return OK;
}
int main(int argc, const char * argv[]) {
printf("鏈棧定義與實(shí)現(xiàn)\n");
int j;
LinkStack S;
int e;
if (InitStack(&S) == OK)
for (j=1; j <= 10; j++) Push(&S,j);
printf("棧中元素依次為:\n");
StackTraverse(S);
Pop(&S, &e);
printf("彈出的棧頂元素: %d\n",e);
StackTraverse(S);
printf("??辗? %d (1:空 0:否)\n",StackEmpty(S));
GetTop(S,&e);
printf("棧頂元素: %d 棧的長(zhǎng)度為%d\n",e,StackLength(S));
ClearStack(&S);
printf("清空棧后,棧空否: %d (1:空 0:否)\n",StackEmpty(S));
return 0;
}
// 輸出
鏈棧定義與實(shí)現(xiàn)
棧中元素依次為:
10 9 8 7 6 5 4 3 2 1
彈出的棧頂元素: 10
9 8 7 6 5 4 3 2 1
??辗? 0 (1:空 0:否)
棧頂元素: 9 棧的長(zhǎng)度為9
清空棧后,棧空否: 1 (1:空 0:否)
3. 遞歸
遞歸方法就是直接或者間接的調(diào)用自己,它可以將一些發(fā)雜問題簡(jiǎn)化。
遞歸在下列方法中經(jīng)常會(huì)用到:
-
定義是遞歸的。
如斐波拉契數(shù)列、階乘等。
-
數(shù)據(jù)結(jié)構(gòu)是遞歸的。
數(shù)據(jù)結(jié)構(gòu)本身具有遞歸性,如鏈表、樹等。
-
問題的解法是遞歸的。
有一類問題,雖然問題本身沒有明顯的遞歸結(jié)構(gòu),但采用遞歸求解比迭代求解更簡(jiǎn)單。如漢諾塔問題、八皇后問題、迷宮問題。
在求解時(shí),我們會(huì)先求解
,然后再進(jìn)一步分解進(jìn)行求解,這種求解叫做分治法。
使用分治法需要滿足3個(gè)條件:
-
能將一個(gè)問題轉(zhuǎn)換成一個(gè)小問題,新問題和原問題的解法相同或類同。
不同的只是被處理的對(duì)象,并且這些處理更小且變化是有規(guī)律的。
可以通過上述轉(zhuǎn)換而使得問題簡(jiǎn)化。
必須有一個(gè)明確的遞歸出口,或成為遞歸邊界。
4. 漢諾塔問題
在經(jīng)典漢諾塔問題中,有 3 根柱子及 N 個(gè)不同大小的穿孔圓盤,盤子可以滑入任意一根柱子。一開始,所有盤子自上而下按升序依次套在第一根柱子上(即每一個(gè)盤子只能放在更大的盤子上面)。
移動(dòng)圓盤時(shí)受到以下限制:
- 每次只能移動(dòng)一個(gè)盤子;
- 盤子只能從柱子頂端滑出移到下一根柱子;
- 盤子只能疊在比它大的盤子上。
4.1 解決思路
我們使用遞歸來解決:
-
時(shí),直接把盤子從A移動(dòng)到C就行了。(遞歸邊界)
-
時(shí):
- 先把
個(gè)盤子從A移動(dòng)到B;(子問題,遞歸)
- 將最大的盤子從A移動(dòng)到C;
- 再將B上
個(gè)盤子移動(dòng)到C。(子問題,遞歸)
- 先把
4.2 用棧解決
static void move(LinkStack *A, LinkStack *B, LinkStack *C, int n) {
if (n == 1) {
int elem;
Pop(A, &elem);
Push(C, elem);
} else {
// 把棧A中n-1個(gè)盤子放到棧B
move(A, C, B, n - 1);
// A棧出棧放入C棧
int elem;
Pop(A, &elem);
Push(C, elem);
// 把棧B中n-1個(gè)盤子放到棧C
move(B, A, C, n - 1);
}
}
void hanoi(LinkStack *A, LinkStack *B, LinkStack *C) {
move(A, B, C, StackLength(*A));
}
int main(int argc, const char * argv[]) {
int n = 5;
LinkStack A, B, C;
InitStack(&A);
InitStack(&B);
InitStack(&C);
for (int i = n; i > 0; i--) {
Push(&A, i);
}
printf("原始棧A:");
StackTraverse(A);
printf("原始棧C:");
StackTraverse(C);
hanoi(&A, &B, &C);
printf("移動(dòng)后棧A:");
StackTraverse(A);
printf("移動(dòng)后棧C:");
StackTraverse(C);
return 0;
}
// 輸出
原始棧A:1 2 3 4 5
原始棧C:
移動(dòng)后棧A:
移動(dòng)后棧C:1 2 3 4 5
4.3 移動(dòng)過程
void hanoi2(char *A, char *B, char *C, int n) {
if (n == 1) {
printf("move %s to %s\n", A, C);
} else {
hanoi2(A, C, B, n - 1);
printf("move %s to %s\n", A, C);
hanoi2(B, A, C, n - 1);
}
}
int main(int argc, const char * argv[]) {
hanoi2("a", "b", "c", 3);
return 0;
}
// 輸出
move a to c
move a to b
move c to b
move a to c
move b to a
move b to c
move a to c

漢諾塔問題