栈
栈是限定仅在表尾进行插入和删除操作的线性表。
栈的抽象数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| ADT Stack { Data 元素具有相同的类型,相邻元素具有前驱后继关系。
Operation InitStack (&S) (构造空栈 ) 操作结果:构造一个空栈S。 DestroyStack(&S) (销毁栈结构) 初始条件:栈S已存在。 操作结果:栈S被销毁。 ClearStack (&S) (栈清空) 初始条件:栈S已存在。 操作结果:栈S清为空栈。 StackEmpty (S) (判空) 初始条件:栈S已存在。 操作结果:若栈S为空栈,则返回true, 否则false。 StackLength (S) (求栈长) 初始条件:栈S已存在。 操作结果:返回S的元素个数,即栈的长度。 StackTraverse (S) (遍历栈) 初始条件:栈S已存在且非空。 操作结果:从栈底到栈顶依次对S的每个数据元素访问。 GetTop (S, &e) (求栈顶元素) 初始条件:栈S已存在且非空。 操作结果:用e返回S的栈顶元素。 Push (&S, e) (入栈) 初始条件:栈S已存在。 操作结果:插入元素e为新的栈顶元素。 Pop (&S, &e) (出栈) 初始条件:栈S已存在且非空。 操作结果:删除S的栈顶元素,并用e返回其值。 }ADT Stack
|
栈的顺序存储结构
1 2 3 4 5 6 7 8
| #define MAXSIZE 50
typedef int SElemType; typedef struct { SElemType* data; int top; } SqStack;
|
进栈操作
1 2 3 4 5 6 7 8 9 10
| void Push(SqStack& S, SElemType e) { if (S.top == MAXSIZE - 1) cout << "栈满" << endl; else { S.top++; S.data[S.top] = e; } }
|
出栈操作
1 2 3 4 5 6 7 8 9 10
| void Pop(SqStack& S, SElemType& e) { if (S.top == -1) cout << "栈空" << endl; else { e = S.data[S.top]; S.top--; } }
|
两栈共享空间
略。。。
栈的链式存储结构
1 2 3 4 5 6 7 8 9 10
| typedef struct StackNode { SElemType data; struct StackNode* next; }StackNode, * LinkStackPtr; typedef struct LinkStack { LinkStackPtr top; int count; }LinkStack;
|
进栈操作
1 2 3 4 5 6 7 8
| void Push(LinkStack& S, SElemType e) { LinkStackPtr s = new StackNode; s->data = e; s->next = S.top; S.top = s; S.count++; }
|
出栈出栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void Pop(LinkStack& S, SElemType& e) { LinkStackPtr p; if (IsEmpty(S)) cout << "栈空" << endl; else { e = S.top->data; p = S.top; S.top = S.top->next; delete p; S.count--; } }
|
栈的作用
栈的引入简化了程序设计问题,划分了不同关注层次,使得思考范围缩小,更加聚焦于我们要解决的问题核心。反之,像数组等,因为要分散精力去考虑数组的下标增减等细节问题,反而掩盖了问题的本质。
现在许多的高级语言,如java,C#等都有对栈结构的封装,可以不用关注它的实现细节,就可以直接使用Stack的push和pop方法,非常方便。
栈的应用-递归
我们把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称做递归函数。
斐波那契数列
F(n)=F(n-1)+F(n-2),(F(1)=F(2)=1)
1 2 3 4 5 6
| int Fbi(int i) { if(i==1||i==2) return 1; return Fbi(i-1)+Fbi(i-2); }
|
等...
栈的应用-四则运算表达式求值
可参考
队列
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列的抽象数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| ADT Queue { Data 元素具有相同的类型,相邻元素具有前驱后继关系。
Operation InitQueue (&Q) (构造空队列 ) 操作结果:构造一个空队列Q。 DestroyQueue (&Q) (销毁队列结构) 初始条件:队列Q已存在。 操作结果:队列Q被销毁。 ClearQueue (&Q) (队列清空) 初始条件:队列Q已存在。 操作结果:队列Q清为空队列。 QueueEmpty (Q) (判空) 初始条件:队列Q已存在。 操作结果:若队列Q为空,则返回true, 否则false。 QueueLength (Q) (求队列长) 初始条件:队列Q已存在。 操作结果:返回Q的元素个数,即队列的长度。 QueueTraverse (Q) (遍历队列) 初始条件:队列Q已存在且非空。 操作结果:从队头到队尾,依次对Q的每个数据元素 访问。 GetHead (Q, &e) (求队头元素) 初始条件:队列Q已存在且非空。 操作结果:用e返回Q的队头元素。 EnQueue (&Q, e) (入队) 初始条件:队列Q已存在。 操作结果:插入元素e为新的队列元素。 DeQueue (&Q, &e) (出队) 初始条件:队列Q已存在且非空。 操作结果:删除Q的队头元素,并用e返回其值。 }ADT Queue
|
循环队列
我们把队列的这种头尾相连的顺序存储结构称为循环队列。(可解决顺序存储空间不足)
定义
1 2 3 4 5 6 7 8 9
| #define MAXSIZE 50 typedef int QElemType;
typedef struct { QElemType* data; int front; int rear; }SqQueue;
|
初始化
1 2 3 4 5
| void InitQueue(SqQueue& Q) { Q.front = 0; Q.rear = 0; }
|
队列长度
1 2 3 4
| int GetLength(SqQueue Q) { return (Q.rear - Q.front + MAXSIZE) % MAXSIZE; }
|
入队操作
1 2 3 4 5 6 7 8 9 10
| void EnQueue(SqQueue& Q, QElemType e) { if ((Q.rear + 1) % MAXSIZE == Q.front) cout << "队列满" << endl; else { Q.data[Q.rear] = e; Q.rear = (Q.rear + 1) % MAXSIZE; } }
|
出队操作
1 2 3 4 5 6 7 8 9 10
| void DeQueue(SqQueue& Q, QElemType& e) { if (Q.front == Q.rear) cout << "队列空" << endl; else { e = Q.data[Q.front]; Q.front = (Q.front + 1) % MAXSIZE; } }
|
队列的链式存储结构
1 2 3 4 5 6 7 8 9 10 11
| typedef int QElemType;
typedef struct QNode { QElemType data; struct QNode* next; }QNode, * QueuePtr; typedef struct { QueuePtr front, rear; }LinkQueue;
|
入队操作
1 2 3 4 5 6 7 8 9 10 11 12 13
| void EnQueue(LinkQueue& Q, QElemType e) { QueuePtr s = new QNode; if (!s) cout << "存储分配失败" << endl; else { s->data = e; s->next = NULL; Q.rear->next = s; Q.rear = s; } }
|
出队操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void DeQueue(LinkQueue& Q, QElemType& e) { QueuePtr p; if (Q.front == Q.rear) cout << "队列空" << endl; else { p = Q.front->next; e = p->data; Q.front->next = p->next; if (Q.rear == p) Q.rear = Q.front; delete p; } }
|
栈与队列对比
逻辑结构 |
和线性表一样, 数据元素之间存在一对一的关系 |
和线性表一样, 数据元素之间存在一对一的关系 |
顺序存储 |
存储空间预先分配,可能会导致空间闲置或栈满溢出现象;数据元素个数不能自由扩充 |
(常设计成循环队列形式);存储空间预先分配,可能会导致空间闲置或队满溢出现象;数据元素个数不能自由扩充 |
链式存储 |
动态分配,不会出现闲置或栈满溢出现象;数据元素个数可以自由扩充 |
动态分配,不会出现闲置或栈满溢出现象;数据元素个数可以自由扩充 |
运算规则 |
插人和删除在表的一端(栈顶)完成,后进先出 |
插入运算在表的一端(队尾)进行,删除运算在表的另一端(队头),先进先出 |