队列和栈

从数据结构的角度看,栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表操作的子集

栈和队列的比较
队列
逻辑结构 和线性表一样,数据元素之间存在一对一的关系 和线性表一样,数据元素之间存在一对一的关系
存储结构 顺序存储:
  • 存储空间预先分配,可能会导致空间闲置或者栈满溢出现现象
  • 数据元素个数不能自由扩充
顺序存储(常设计成循环队列):
  • 存储空间预先分配,可能会导致空间闲置或者队列溢出现象
  • 数据元素个数不能自由扩充
链式存储:
  • 动态分配,不会出现闲置或者栈满溢出现象
  • 数据元素个数可以自由扩充
链式存储:
  • 动态分配,不会出现闲置或者队列溢出现象
  • 数据元素个数可以自由扩充
运算规则 插入和删除在表的一端(栈顶)完成,后进先出 插入运算在表的一端(队尾)进行,删除在表的另一端(队头),先进先出

限定仅在表尾进行插入或者删除操作的线性表,栈的修改按照先进后出的原则

顺序栈

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<stdio.h>
#include<stdlib.h>

#define MAXSIZE 100
typedef struct SqStack
{
int *data;
int top; // 栈顶指针
}SqStack;

// InitStack 初始化顺序栈
int InitStack(SqStack *s) {
s->top = -1;
s->data = (int *)malloc(sizeof(int)*MAXSIZE);;
return 0;
}

// Push 入栈
// 1. 判断栈是否满,若满则返回-1
// 2. 将新元素压入栈顶,栈顶指针加1
int Push(SqStack *s, int e) {
// 栈满
if (s->top == MAXSIZE-1) {
return -1;
}
s->data[++s->top] = e;
return 0;
}

// Pop 出栈
// 1. 判断栈是否为空,若空则返回-1
// 2. 栈顶指针减1,栈顶元素出栈
int Pop(SqStack *s, int *e) {
if (s->top == -1) {
return -1;
}
e = &s->data[s->top--];
return 0;
}

// GetTop 取栈顶元素
int GetTop(SqStack *s) {
if (s->top !=-1) {
return s->data[s->top];
}
return 0;
}

链栈

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<stdio.h>
#include<stdlib.h>

typedef struct StackNode
{
int data;
struct StackNode* next;
}StackNode,*LinkStack;

// InitStack 初始化链栈
int InitStack(LinkStack s) {
s = NULL;
return 0;
}

// Push 入栈
// 1. 为入栈元素分配空间,用指针p指向
// 2. 将新节点数据域置为e
// 3. 将新节点插入栈顶
// 4. 修改栈顶指针为p
LinkStack Push(LinkStack s, int e) {
LinkStack p = (LinkStack)malloc(sizeof(StackNode));
p->data = e;
p->next = s;
s = p;
return s;
}

// Pop 出栈
// 1. 判断栈是否为空,若空则返回-1
// 2. 将栈顶元素赋给e
// 3. 临时保存栈顶元素的地址,以备释放
// 4. 修改栈顶指针,指向新的栈顶元素
// 5. 释放原栈顶元素的地址
LinkStack Pop(LinkStack s, int *e) {
if (s == NULL) {
return s;
}
*e = s->data;
LinkStack p = s;
s = s->next;
free(p);
return s;
}

// GetTop 取栈顶元素
int GetTop(LinkStack s) {
if (s != NULL) {
return s->data;
}
return 0;
}

队列

一种先进先出的线性表。它只允许在表的一端进行插入,而在另一端删除元素

循环队列

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<stdio.h>
#include<stdlib.h>

#define MaxQSize 100
typedef struct {
int *base; // 存储空间基地址
int front; // 头结点
int rear; // 尾结点
}SqQueue;

// InitQueue 初始化循环队列
void InitQueue(SqQueue *q)
{
q->base = (int *)malloc(sizeof(int)*MaxQSize);
q->front = q->rear = 0;
}

// QueueLenth 队列长度
int QueueLenth(SqQueue q) {
return (q.rear - q.front + MaxQSize) % MaxQSize;
}

// Push 入队
// 1、判断队列是否满
// 2、将新元素插入队尾
// 3、队尾指针加1
int Push(SqQueue *q, int e)
{
// 队满
if ((q->rear+1)%MaxQSize == q->front) {
return -1;
}

q->base[q->rear] = e;
q->rear = (q->rear + 1) % MaxQSize;
return 0;
}

// Pop 出队
// 1、判断队列是否为空
// 2、保存队头元素
// 3、队头指针加1
int Pop(SqQueue *q, int *e)
{
// 队空
if (q->rear == q->front) {
return -1;
}
*e = q->base[q->front];
q->front = (q->front + 1) % MaxQSize;
return 0;
}

// GetTop 取队头元素
int GetTop(SqQueue *q, int *e)
{
if (q->front == q->rear) {
return -1;
}
*e = q->base[q->front];
return 0;
}

链队

链队是指采用链式存储结构实现的队列。通常链队用单链表表示。一个链队显然需要两个分别指示队头和队尾的指针(分别称为头指针和尾指针)才能唯一确定。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include<stdio.h>
#include<stdlib.h>

typedef struct QNode QNode;
typedef struct QNode* QPtr;

struct QNode
{
int data;
struct QNode* next;
};

typedef struct LinkQueue LinkQueue;
struct LinkQueue
{
QPtr front; // 队头指针
QPtr rear; // 队尾指针
};

// InitQueue 链队初始化
// 1、生成新结点,队头和队尾都指向此结点
// 2、头结点的指针域置为空
void InitQueue(LinkQueue *q)
{
QPtr node = (QPtr)malloc(sizeof(QNode));
node->next = NULL;
q->front = node;
q->rear = node;
}

// EnQueue 元素入队
// 1、为入队元素分配结点空间,用指针p指向
// 2、将新结点数据域置为e
// 3、将新结点插入到队尾
// 4、修改队尾指针为p
void EnQueue(LinkQueue *q, int e)
{
QPtr p = (QPtr)malloc(sizeof(QNode));
p->data = e;
p->next = NULL;
q->rear->next = p;
q->rear = p;
}

// DeQueue 元素出队
// 1、判断队列是否为空,若空则返回-1
// 2、临时保存队头元素的空间,以备释放
// 3、修改头结点的指针域,指向下一个结点
// 4、判断出队是否为最后一个元素,若是,则将队尾指针重新赋值,指向头结点
// 5、释放原队头元素的空间
void DeQueue(LinkQueue *q, int *e)
{
if (q->rear == q->front)
{
return;
}
QPtr p = q->front->next;
*e = p->data;
q->front->next = p->next;
if (q->rear == p)
{
q->rear = q->front;
}
free(p);
}

// GetTop 取队头元素
void GetTop(LinkQueue *q, int *e)
{
if (q->front != q->rear) {
*e = q->front->next->data;
}
}

应用

n阶汉诺塔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>

void move(char A, char B) {
printf("%c ---> %c\n", A, B);
}

void hanoi(int n, char A, char B, char C) {
if (n == 1) {
move(A, C);
return;
}

// 将A上编号为1至n-1的圆盘移动到B,C做辅助塔
hanoi(n-1, A, C, B);
// 将编号为n的圆盘从A移动到C
move(A, C);
// 将B上编号为1至n-1的圆盘移动到C,A做辅助塔
hanoi(n-1, B, A, C);
}

表达式求值

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// 表达式求值
// 1、初始化栈OPTR和OPND
// 2、扫描表达式,读入第一个字符ch,如果表达式没有扫描完成
// a、若ch不是运算符,则压入OPND栈,读入下一个字符ch;
// b、若ch是运算符,则根据OPTR的栈顶元素和ch的优先级比较结果,做不同的处理:
// 1、若是小于,则ch压入OPTR栈,读入下一个字符ch
// 2、若是大于,则弹出OPTR栈顶的运算符,从OPND栈弹出两个数,进行相应运算,结果压入OPND栈
// 3、若是等于,则OPTR的栈顶元素是"("且ch是")",这是弹出OPTR栈顶的"(",相当于括号匹配成功,然后读入下一个字符ch
// 3、OPEN栈顶元素即为表达式求值结果,返回此元素
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXSIZE 100

// 运算符栈
typedef struct OPTRStack OPTRStack;
struct OPTRStack {
char data;
OPTRStack* next;
};

// 操作数栈
typedef struct OPNDStack OPNDStack;
struct OPNDStack
{
double data;
OPNDStack* next;
};

char getOptr(OPTRStack *optr)
{
char ch = '-';
if (optr == NULL)
{
return ch;
}
return optr->data;
}

OPTRStack* popOptr(OPTRStack *optr, char *ch)
{
if (optr == NULL)
{
return optr;
}
*ch = optr->data;
OPTRStack *p = optr;
optr = optr->next;
free(p);
return optr;
}

OPNDStack* popOpnd(OPNDStack *opnd, double *data)
{
if (opnd == NULL)
{
return opnd;
}
*data = opnd->data;
OPNDStack *p = opnd;
opnd = opnd->next;
free(p);
return opnd;
}

OPNDStack* pushOpnd(OPNDStack *opnd, double data)
{
OPNDStack *p = (OPNDStack*)malloc(sizeof(OPNDStack));
p->data = data;
p->next = opnd;
opnd = p;
return opnd;
}

int isOPTR(char ch)
{
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')' || ch == '#')
{
return 1;
}
return 0;
}

char precede(char a, char b)
{
if (a == '+')
{
if (b == '*' || b == '/' || b == '(')
{
return '<';
}
return '>';
}
else if (a == '-')
{
if (b == '*' || b == '/' || b == '(')
{
return '<';
}
return '>';
}
else if (a == '*')
{
if (b == '(')
{
return '<';
}
return '>';
}
else if (a == '/')
{
if (b == '(')
{
return '<';
}
return '>';
}
else if (a == '(')
{
if (b == ')')
{
return '=';
}
else if (b == '#')
{
return '!';
}
return '<';
}
else if (a == ')')
{
if (b == '(')
{
return '!';
}
return '>';
}
else if (a == '#')
{
if (b == ')')
{
return '!';
}
else if (b == '#')
{
return '=';
}
return '<';
}
else
{
return '!';
}
}

double operate(double left, double right, char operaor)
{
double result;
switch (operaor)
{
case '+':
result = left + right;
break;
case '-':
result = left - right;
break;
case '*':
result = left * right;
break;
case '/':
result = left / right;
break;
default:
result = 0.0;
break;
}
return result;
}

void EvaluateExpression() {
// 初始化栈
OPTRStack *optr = (OPTRStack*)malloc(sizeof(OPTRStack));
optr->data = '#';
optr->next = NULL;
OPNDStack *opnd = NULL;

char *str = (char *)malloc(sizeof(char)*MAXSIZE);
// scanf("%s", str);
str = "3*(7-2)#";
int index = 0;
char operator;
int error = 0;
while(str[index] != '\0')
{
char ch = str[index];
if (error == 1) {
break;
}
if (isOPTR(ch) != 1)
{
// 操作数入栈
OPNDStack *opndNode = (OPNDStack*)malloc(sizeof(OPNDStack));
opndNode->data = ch - '0';
opndNode->next = opnd;
opnd = opndNode;
index++;
continue;
}

char cede = precede(getOptr(optr), ch);
switch (cede)
{
case '<':
{
OPTRStack *optrNode = (OPTRStack*)malloc(sizeof(OPTRStack));
optrNode->data = ch;
optrNode->next = optr;
optr = optrNode;
index++;
break;
}
case '>':
{
optr = popOptr(optr, &operator);
double left, right;
opnd = popOpnd(opnd, &right);
opnd = popOpnd(opnd, &left);
opnd = pushOpnd(opnd, operate(left, right, operator));
break;
}
case '=':
{
optr = popOptr(optr, &operator);
index++;
break;
}
default:
{
printf("Syntax Error!");
error = 1;
break;
}
}
}
if (error != 1)
{
printf("结果为:%lf\n", opnd->data);
} else {
printf("表达式错误!!!\n");
}
}