一、栈
1.栈的概念
(1)只允许在一端进行插入和删除的线性表。允许插入和删除的一端叫做栈顶,而不允许插入和删除的另一端叫栈底。当栈中没有任何元素的时候叫空栈。
2.顺序栈
(1)定义:
- 顺序栈是栈的顺序储存表示。
- 实际上,顺序栈是指利用一块连续的储存单元作为栈元素的储存空间,只不过在C语言中是借用一维数组实现而已。
- 因为一维数组的下标从0开始,栈空的时候 S.top < 0,因此空栈时栈顶指针 S.top == 1。
(2)实现
#pragma once
//SeqStack.h
#include<cstdio>
#include<cstdlib>
#define initSize 20
#define increments 10
typedef int SElemType;
typedef struct {
SElemType* elem;
int maxSize, top;
}SeqStack;
void overflowProcess(SeqStack& S) {
S.maxSize += increments;
SElemType* newArray = (SElemType*)malloc(sizeof(S.maxSize));
for (int i = 0; i <= S.top; i++) newArray[i] = S.elem[i];
free(S.elem);
S.elem = newArray;
}
bool getTop(SeqStack& S, SElemType& x) {
if (S.top == -1) return false;
x = S.elem[S.top];
return true;
}
bool stackEmpty(SeqStack& S) {
return S.top == -1;
}
bool stackFull(SeqStack& S) {
return S.top == S.maxSize;
}
int stackSize(SeqStack& S) {
return S.top + 1;
}
bool Push(SeqStack& S, SElemType x) {
if (stackFull(S)) overflowProcess(S);
S.elem[++S.top] = x;
return true;
}
bool Pop(SeqStack& S, SElemType& x) {
if (S.top == -1) return false;
x = S.elem[S.top--];
return true;
}
//SeqStack.cpp
#include "SeqStack.h"
void initStack(SeqStack& S) {
S.elem = (SElemType*)malloc(initSize * sizeof(SElemType));
S.maxSize = initSize;
S.top = -1;
}
3.双栈共享同一栈空间
(1)特点,
- 需要两个栈的时候,可以定义一个足够大的栈空间V[m],并将两个栈设为0号栈和1号栈。
- 该空间的两端的外侧分别设为两个栈的栈底,用b[0],b[1]表示,让两个栈的栈顶b[0],b[1] 都往中间伸展,直到两个栈的栈顶相遇,才认为发生了溢出。
(2)实现
//DblStack.h
#pragma once
#include<cstdio>
#include<cstdlib>
#define m 20
typedef int SElemType;
typedef struct {
int top[2], bot[2];
SElemType V[m];
}DblStack;
//DblStack.cpp
#include "DblStack.h"
void initStack(DblStack& DS, SElemType x, int d) {
DS.bot[0] = DS.top[0] = -1;
DS.bot[1] = DS.top[1] = m;
}
bool Push(DblStack& DS, SElemType x, int d) {
if (DS.top[0] + 1 == DS.top[1] || d != 0 && d != 1) return false;
if (d == 0) DS.top[0]++;
else DS.top[1]--;
DS.V[DS.top[d]] = x;
return true;
}
bool Pop(DblStack& DS, SElemType& x, int d) {
if (d != 0 && d != 1 || DS.top[d] == DS.bot[d]) return false;
x = DS.V[DS.top[d]];
if (d == 0) DS.top[0]--;
else DS.top[1]++;
return true;
}
(3)问题:
- 如果要求更多的栈共享同一个栈空间,使用顺序存储方式将使得处理十分复杂。
- 解决的办法是采用链接方式作为栈的储存表示。
3.链式栈
(1)特点
- 链式栈是栈的链接储存表示。采用链式栈来表示一个栈,表示结点的插入与删除。
- 在程序中同时使用多个栈的情况下,采用链接表示不仅能够提高效率,还可以达到共享存储空间的目的。
//LinkStack.h
#pragma once
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef int SElemType;
typedef struct node {
SElemType data;
struct node* link;
}LinkNode, *LinkStack;
//LinkStack.cpp
#include "LinkStack.h"
void initStack(LinkStack& S) {
S = NULL;
}
void clearStack(LinkStack& S) {
LinkNode* p;
while (S != NULL) {
p = S; S = S->link; free(p);
}
}
bool stackEmpty(LinkStack& S) {
return S == NULL;
}
bool Push(LinkStack& S, SElemType x) {
LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
p->data = x;
p->link = S; S = p; return true;
}
bool Pop(LinkStack& S, SElemType& x) {
if (S == NULL) return false;
x = S->data;
LinkNode* p = S;
S = S->link;
free(p);
return true;
}
bool getTop(LinkStack& S, SElemType& x) {
if (S == NULL) return false;
x = S->data;
return true;
}
int StackSize(LinkStack& S) {
int sz = 0;
LinkNode* p = S;
while (p != NULL) {
p = p->link;
sz++;
}
return sz;
}
4.利用栈和队列判断一个字符序列是否回文
其实应该先把数据类型定义为 char 的。
#include<cstring>
#include "SeqStack.cpp"
#include "CircQueue.cpp"
bool ok(char* str) {
SeqStack S;
initStack(S);
CircQueue Q;
initQueue(Q);
int ch1, ch2;
for (int i = 0; i < strlen(str); i++) {
enQueue(Q, str[i]);
Push(S, str[i]);
}
while (!stackEmpty(S) && !queueEmpty(Q)) {
Pop(S, ch1);
deQueue(Q, ch2);
if (ch1 != ch2) return false;
}
return true;
}
二、队列
1.循环队列
//CircQueue.h
#pragma once
#include<cstdio>
#include<cstdlib>
#define queSize 30
typedef int QElemType;
typedef struct {
QElemType elem[queSize];
int front, rear;
}CircQueue;
//CircQueue.cpp
#include "CircQueue.h"
void initQueue(CircQueue& Q) {
Q.front = 0;
Q.rear = 0;
}
bool queueEmpty(CircQueue& Q) {
return Q.front == Q.rear;
}
bool queueFull(CircQueue& Q) {
return (Q.rear + 1) % queSize == Q.front;
}
int queueSize(CircQueue& Q) {
return (Q.rear - Q.front + queSize) % queSize;
}
bool enQueue(CircQueue& Q, QElemType x) {
if (queueFull(Q)) return false;
Q.elem[Q.rear] = x;
Q.rear = (Q.rear + 1) % queSize;
return true;
}
bool deQueue(CircQueue& Q, QElemType& x) {
if (queueEmpty(Q)) return false;
x = Q.elem[Q.front];
Q.front = (Q.front + 1) % queSize;
return true;
}
bool getFront(CircQueue& Q, QElemType& x) {
if (queueEmpty(Q)) return false;
x = Q.elem[Q.front];
return true;
}
2.链式队列
(1)这个我真的想吐槽,首先,这个链式队列设计得和链表几乎没什么区别。其次,这个Q.rear,可以说没有任何作用。因为队尾的判断是Q.front == NULL。
//LinkQueue.h
#pragma once
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef int QElemType;
typedef struct Node {
QElemType data;
struct Node* link;
}LinkNode;
typedef struct {
LinkNode* front, * rear;
}LinkQueue;
//LinkQueue.cpp
#include "LinkQueue.h"
void initQueue(LinkQueue& Q) {
Q.front = Q.rear = NULL;
}
void ClearQueue(LinkQueue& Q) {
LinkNode* p;
while (Q.front != NULL) {
p = Q.front;
Q.front = p->link;
free(p);
}
Q.rear = NULL;
}
bool QueueEmpty(LinkQueue& Q) {
return Q.front == NULL;
}
bool enQueue(LinkQueue& Q, QElemType x) {
LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
p->data = x;
if (Q.front == NULL) { Q.front = Q.rear = p; }
else { Q.rear = p; }
return true;
}
bool deQueue(LinkQueue& Q, QElemType &x) {
if (Q.front == NULL) return false;
LinkNode* p = Q.front;
x = p->data;
Q.front = Q.front->link;
free(p);
if (Q.front = NULL) Q.rear = NULL;
return true;
}
bool getFront(LinkQueue& Q, QElemType& x) {
if (Q.front == NULL) return false;
x = Q.front->data; return true;
}
int QueueSize(LinkQueue& Q) {
LinkNode* p = Q.front;
int sz = 0;
while (p != NULL) {
p = p->link;
sz++;
}
return sz;
}
三、栈的应用
1.数制转换
#include "LinkStack.cpp"
int BaseTrans(int N, int k) {
int i;
long result = 0;
LinkStack S;
initStack(S);
while (N != 0) {
i = N % k;
N /= k;
Push(S, i);
}
while (!stackEmpty(S)) {
Pop(S, i);
result = result * 10 + (long)i;
}
return result;
}
void main(void) {
int N, k;
printf("请输入一个十进制非负整数:\n");
scanf("%d", &N);
printf("要转换为多少进制的数:\n");
scanf("%d", &k);
printf("%d转换为%d进制数为%d\n", N, k, BaseTrans(N, k));
}
2.括号匹配
#include "LinkStack.cpp"
void brachetMatch(char expr[]) {
LinkStack S;
initStack(S);
int j, i = 0;
char ch = expr[i];
while (ch != '\0') {
if (ch == '(') Push(S, i);
else if (ch == ')') {
if (!stackEmpty(S)) {
Pop(S, j);
printf("位置%d的'('与位置%d的')'匹配\n", j, i);
}
else printf("没有与位置%d的')'匹配的'('\n", i);
}
ch = expr[++i];
}
while (!stackEmpty(S)) {
Pop(S, j);
printf("没有与位置%d的'('匹配的')'!\n", j);
}
}
int main(void) {
char A[] = "(a*(b+c)-d)\0";
char B[] = "(a+b))(\0";
printf("%s\n", A);
brachetMatch(A);
printf("%s", B);
brachetMatch(B);
return 0;
}
3.后缀表示计算表达式的值
#include<cmath>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define stkSize 20
bool DoOperator(double OPEN[], int& top, char op) {
double left, right;
if (top == -1) {
printf("缺少右操作数!\n");
return false;
}
right = OPEN[top--];
if (top == -1) {
printf("缺少左操作数!\n");
return false;
}
left = OPEN[top--];
switch (op) {
case'+': OPEN[++top] = left + right; break;
case'-': OPEN[++top] = left - right; break;
case'*': OPEN[++top] = left * right; break;
case'/':
if (fabs(right) < 1e-6) {
printf("Divide by zero!\n");
return false;
}
else {
OPEN[++top] = left / right;
break;
}
default: return false;
}
return true;
}
int main() {
char A[] = "5964-*+93/-#";
double OPEN[stkSize], result;
int i = 0, top = -1;
char ch = A[i++];
while (ch != '#') {
if (ch >= '0' && ch <= '9') OPEN[++top] = (double)(ch - 48);
else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
if (!DoOperator(OPEN, top, ch)) {
printf("运行出错!\n");
exit(1);
}
}
else printf("输入了非法字符,请重新输入!\n");
ch = A[i++];
}
result = OPEN[top--];
printf("计算结果是:%g\n", result);
return 0;
}
4.中缀表示转换为后缀表示
五、递归
六、双端队列
//SeqDeque.h
#pragma once
#include<cstdio>
#define maxSize 100
typedef int DQElemType;
typedef struct {
DQElemType elem[maxSize];
int end1, end2;
}SeqDeque;
//SeqDeque.cpp
#include "SeqDeque.h"
bool enQueueHead(SeqDeque& Q, DQElemType x) {
if ((Q.end2 + 1) % maxSize == Q.end1) return false;
Q.end1 = (Q.end1 - 1 + maxSize) % maxSize;
Q.elem[Q.end1] = x;
return true;
}
bool deQueueHead(SeqDeque& Q, DQElemType& x) {
if (Q.end1 == Q.end2) return false;
x = Q.elem[Q.end1];
Q.end1 = (Q.end1 + 1) % maxSize;
return true;
}
bool getHead(SeqDeque& Q, DQElemType& x) {
if (Q.end1 == Q.end2) return false;
x = Q.elem[Q.end1];
return true;
}
bool enQueueTail(SeqDeque& Q, DQElemType x) {
if ((Q.end1 + 1) % maxSize == Q.end1) return false;
Q.elem[Q.end2] = x;
Q.end2 = (Q.end2 + 1) % maxSize;
return true;
}
bool deQueueTail(SeqDeque& Q, DQElemType& x) {
if (Q.end1 == Q.end2) return false;
Q.end2 = (Q.end2 - 1 + maxSize) % maxSize;
x = Q.elem[Q.end2];
return true;
}
七、优先队列
//PriorityQueue.h
#pragma once
#include<cstdio>
#include<cstdlib>
using namespace std;
#define maxPQSize 50
typedef int PQElemType;
typedef struct {
PQElemType elem[maxPQSize];
int n;
}PQueue;
//PriorityQueue.cpp
#include "PriorityQueue.h"
bool PQInsert(PQueue& PQ, PQElemType x) {
if (PQ.n == maxPQSize) return false;
PQ.elem[PQ.n++] = x;
return true;
}
bool PQRemove(PQueue& PQ, PQElemType& x) {
//这里作者又在搞事情,规定数值最小的优先级最大。但是在STL中默认情况下明明是数值越大优先级越高。
if (PQ.n == 0) return false;
PQElemType min = PQ.elem[0];
int k = 0;
for (int i = 1; i < PQ.n; i++) {
if (min < PQ.elem[i]) {
k = i;
min = PQ.elem[i];
}
}
x = PQ.elem[k];
PQ.n--;
PQ.elem[k] = PQ.elem[PQ.n];
return true;
}