第三章
栈与队列
栈的定义
(线性表的具有相同数据类型的n个数据元素的有序有限序列,其中n为表长,当n=0时,线性表为空表)
栈 是只允许一端插入或删除的 线性表
n个不同元素进栈,出栈元素不同排列个数为:
1n+1\dfrac{1}{n+1}n+11*C(22n)C\binom{2}{2n}C(2n2)
顺序栈
定义
(用顺序存储方式实现的栈)
#define MaxSize 10
typedef struct {
Elemtype data[MaxSize];
int top;//栈顶指针,存放栈顶元素的下标
}SqStack;
初始化操作
//第一种初始化S.top = -1;
#define MaxSize 10
typedef struct {
Elemtype data[MaxSize];
int top;//栈顶指针,存放栈顶元素的下标
}SqStack;
void InitStack(SqStack &S) {
S.top = -1;//初始化栈顶指针
}
//第二种初始化S.top = 0;
#define MaxSize 10
typedef struct {
Elemtype data[MaxSize];
int top;//栈顶指针,存放栈顶元素的下标
}SqStack;
void InitStack(SqStack &S) {
S.top = 0;//初始化栈顶指针
}
进栈
//第一种的进栈
bool Push(Sqtack &S, Elemtype x) {
if(S.top == MaxSize - 1) {
return false;
}
S.top = S.top + 1;
S.data[S.top] = x;
return true;
}
//第二种的进栈
bool Push(Sqtack &S, Elemtype x) {
if(S.top == MaxSize - 1) {
return false;
}
S.data[S.top] = x;
S.top = S.top + 1;
return true;
}
出栈
//第一种的出栈
bool Pop(Sqtack &S, Elemtype &x) {
if(S.top == -1) {
return false;
}
x = S.data[S.top];
S.top = S.top - 1;
return true;
}
//第二种的出栈
bool Pop(Sqtack &S, Elemtype &x) {
if(S.top == 0) {
return false;
}
x = S.data[S.top-1];
S.top = S.top - 1;
return true;
}
读栈顶元素
//第一种读栈顶元素
bool GetTop(SqStack &S, Elemtype &x) {
if(S.top == -1) {
return false;
}
x = S.data[top];
return true;
}
//第二种读栈顶元素
bool GetTop(SqStack &S, Elemtype &x) {
if(S.top == 0) {
return false;
}
x = S.data[top-1];
return true;
}
共享栈
两个栈共享一个存储空间,有两个栈顶指针,分别存放顺序表的第一个元素前一个的下标比如-1,和存放顺序表最后一个元素下标的后一个比如MaxSize
定义
#define MaxSize 10
typedef struct {
Elemtype data[MaxSize];
int top0;//0号栈顶指针,存放栈顶元素的下标
int top1;//1号栈顶指针,存放栈顶元素的下标
}ShqStack;
初始化操作
#define MaxSize 10
typedef struct {
Elemtype data[MaxSize];
int top0;//0号栈顶指针,存放栈顶元素的下标
int top1;//1号栈顶指针,存放栈顶元素的下标
}ShqStack;
void InitShStack(ShStack &S) {
S.top0 = -1;
S.top1 = Maxsize;
}
栈满的条件:
top0 + 1 == top1
链栈
定义
用链式存储方式实现的栈
创建(初始化)
//不带带头结点的
typedef struct LSNode {
Elemtyoe data;
struct LSNode *next;
}LSNode, *LinkStack;
bool InitLStack(LinkStack &LS) {
LS = NULL;//空表,暂时没有任何节点,防止脏数据
return true;
}
void test() {
LinkStack LS;
initLStack(L);
}
//带头结点的
typedef struct LSNode {
Elemtyoe data;
struct LSNode *next;
}LSNode, *LinkStack;
bool InitLStack(LinkStack &LS) {
LS = (LSNode *)malloc(sizeof(LSNode));//分配一个头结点
if(LS == NULL) {//内存分配不足,分配失败
return false;
}
LS = NULL;//空表,暂时没有任何节点,防止脏数据
return true;
}
void test() {
LinkStack LS;
initLStack(L);
}
增(进栈)
这里可以用到头插法
//带头结点的
bool pop(LinkStack &LS,Elemtype e) {//将值为e的节点插入栈中
LSNode s = (LSNode *)malloc(sizeof(LSNode));
LSNode *p = LS;//p用于指向头结点
s->data = e;
s->next = p = next;
p->next = s;
return true;
}
//不带头结点
bool pop(LinkStack &LS,Elemtype e) {//将值为e的节点插入栈中
LSNode s = (LSNode *)malloc(sizeof(LSNode));
LSNode *p = LS;
s->data = e;
if(LS == NULL) {//空表的时后直接往里头加就行
LS = s;
}else {
s->next = p->next;
p->next = s;
}
return true;
}
删(出栈)
//带头结点的
bool push(LinkStack &LS,Elemtype &e) {//将出栈的元素值放入e中
if(LS->next == NULL) {//判断表空
return false;
}
LSNode *p = LS;//p指向栈头结点
LSNode *q = p->next;//q指向头结点的下一个结点,也就是要出栈的栈顶元素
e = p->next->data;//将栈顶的值放入e中
p->next = q->next;
free(q);
return true;
}
//不带头结点的
bool push(LinkStack &LS,Elemtype &e) {//将出栈的元素值放入e中
if(LS == NULL) {//判断表空
return false;
}
LSNode *p = LS;//指向栈顶结点,就是栈顶元素
e = p->data;
LS = LS->next;//栈顶指针直接指向下一个元素
free(p);//释放之前栈顶的结点
return true;
}
查(获取栈顶元素)
//带头结点的
bool Top(LinkStack &LS,Elemtype &e) {//将出栈的元素值放入e中
if(LS->next == NULL) {//判断表空
return false;
}
LSNode *p = LS;//p指向栈头结点
e = p->next->data;//将栈顶元素的值存放在e中
return true;
}
//不带头结点的
int Top(LinkStack &LS,Elemtype &e) {//将出栈的元素值放入e中
if(LS == NULL) {//判断表空
return false;
}
LSNode *p = LS;//p指向栈头结点
e = p->data;//将栈顶元素的值存放在e中
return true;
}
判断表空
//带头结点的
bool juiceEmpty(LinkStack &LS) {
if(LS->next == NULL) {
return true;
}
}
//不带头结点的
bool juiceEmpty(LinkStack &LS) {
if(LS == NULL) {
return true;
}
}
队列
定义
只允许在一端进行插入,在另一端进行删除的 线性表,先进先出
(线性表时具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,但n=0时相信表为一个空表。
顺序队列
#define MaxSize 10
typedef struct{
ElemType data[MaxSize];//用静态数组存放
int front, rear;//队头指针和队尾指针
}SqQueue;
初始化操作
(队尾指针指向队尾元素后一个元素)
#define MaxSize 10
typedef struct{
ElemType data[MaxSize];//用静态数组存放
int front, rear;//队头指针和队尾指针
}SqQueue;
void InitQueue(SqQueue &Q) {
Q.rear = Q.front = 0;//初始化队头指针指向0
}
void testQueue() {
//申明一个顺序队列
SqQueue Q;
InitQueue(Q);
//...后续操作...
}
(队尾指针指向队尾元素)
#define MaxSize 10
typedef struct{
ElemType data[MaxSize];//用静态数组存放
int front, rear;//队头指针和队尾指针
}SqQueue;
void InitQueue(SqQueue &Q) {
Q.front = 0;//初始化队头指针指向0
Q.rear = MaxSize - 1;
}
void testQueue() {
//申明一个顺序队列
SqQueue Q;
InitQueue(Q);
//...后续操作...
}
入队
(队尾指针指向队尾元素后一个元素)
bool EnQueue(SqQueue &Q,ElemType x) {
if((Q.rear+1)%MaxSize == Q.front){//判断队满,这时牺牲一个出差空间来进行判断队满
return false;
}
Q.data[Q.rear] = x;//将x插入队列
Q.rear = (Q.rear+1)%MaxSize;//队尾指针往后移,并取模
}
(队尾指针指向队尾元素)
bool EnQueue(SqQueue &Q,ElemType x) {
if((Q.rear+2)%MaxSize == Q.front){//判断队满,这时牺牲一个出差空间来进行判断队满
return false;
}
Q.rear = (Q.rear+1) % MaxSize;//队尾指针往后移,并取模
Q.data[Q.rear] = x;//将x插入队列
}
出队
(队尾指针指向队尾元素后一个元素)
//删除一个队头元素,并用x返回
bool DeQueue(SqQueue &Q,ElemType &x) {
if(Q.rear == Q.front) {//判断队空
return false;
}
x = Q.data[Q.front];
Q.front = (Q.front+1)%MaxSize;//队头指针往后移动,并取模
return true;
}
(队尾指针指向队尾元素)
//删除一个队头元素,并用x返回
bool DeQueue(SqQueue &Q,ElemType &x) {
if((Q.rear+1)%MaxSize == Q.front) {//判断队空
return false;
}
x = Q.data[Q.front];
Q.front = (Q.front+1)%MaxSize;//队头指针往后移动,并取模
return true;
}
查
(获得队头元素)
(队尾指针指向队尾元素后一个元素)
bool GetHead(SqQueue Q,ElemType &x) {
if(Q.rear == Q.front){//判断队空
return false;
}
x = Q.data[Q.front];
return true;
}
(队尾指针指向队尾元素)
bool GetHead(SqQueue Q,ElemType &x) {
if((Q.rear+1)%MaxSize == Q.front){//判断队空
return false;
}
x = Q.data[Q.front];
return true;
}
队中元素数量
(rear+MaxSize-front) % MaxSize//稍微修改也可以进行判断空与满
判断队满/空方案二
(不浪费任何元素)
#define MaxSize 10
typedef struct {
ElemType data[MaxSize];
int front,rear;
int size;//队列当前长度
}SqQueue;
(rear+MaxSize-front) % MaxSize//稍微修改也可以进行判断空与满
判断队满/空方案三
#define MaxSize 10
typedef struct {
ElemType data[MaxSize];
int front,rear;
int tag;//最近进行的时删除/插入
}SqQueue;
链队列
定义
typedef struct LinkNode{//链式队列结点
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct{//链式队列
LinkNode *front,*rear;//队列的头指针和尾指针
}LinkQueue;
初始化
//带头结点的
void InitQueue(LinkQueue &Q) {
//初始化front,rear都指向头结点
Q.front = Q.rear = (LinkNode *)malloc(sizeof(LinkNode));//申请一个头结点
Q.front->next = NULL;
}
void testLinkQueue() {
LinkQueue Q;//声明一个队列
InitQueue(Q);//初始化队列
//...后续操作...
}
//不带头结点
void InitQueue(LinkQueue &Q) {
//初始时front,rear都指向NULL
Q.front = NULL;
Q.rear = NULL;
}
判空
//带头结点的
bool IsEmpty(LinkQueue Q) {
if(Q.front == Q.rear) {
return true;
}else {
return false;
}
}
//不带头结点的
bool IsEmpty(LinkQueue Q) {
if(Q.front == NULL) {
return true;
}else {
return false;
}
}
入队
//带头结点的
void EnQueue(LinkQueue &Q,ElemType x) {
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;//不要忘了
Q.rear->next = s;//新结点插入到rear之后
Q.rear = s;//修改表尾指针
}
//不带头结点的
void EnQueue(LinkQueue &Q,ElemType x) {
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
if(Q.front == NULL) {//在空队列中插入第一个元素
Q.front = s;//修改队头指针
Q.rear = s;//修改队尾指针
}else {
Q.rear->next = s;//新结点插入到rear之后
Q.rear = s;//修改表尾指针
}
}
出队
//带头结点的
bool DeQueue(LinkQueue &Q,Elemtype &x) {
if(Q.front == Q.rear) {//表空判断
return false;
}
LinkNode *p = Q.front->next;
x = p->data;
if(Q.rear == p){//此次时最后一个结点出队
Q.rear = Q.front;//修改rear的指针
}
free(p);//释放空间
return true;
}
//不带头结点的
bool DeQueue(LinkQueue &Q,Elemtype &x) {
if(Q.front == NULL) {//表空判断
return false;
}
LinkNode *p = Q.front;
x = p->data;
if(Q.rear == Q.front){//此次时最后一个结点出队
Q.rear = Q.front = NULL;//修改front和rear的指针
}
Q.front = p->next;
free(p);//释放空间
return true;
}
双端队列
考点:判断元素输出序列合法性(eg:4个有序序列)
-
双端队列:
-
栈的出栈序列:
-
输入受限制的双端队列:
- 输出受限的双端队列:
括号匹配问题
bool bracketCheck(char str[], int length) {
SqStack S;
InitStack(S);//初始化一个栈
for(int i = 0;i < length; i++) {//左括号入栈
if(str[i] == '(' || str[i] == '[' || str[i] == '{') {
push(S, str[i]);
}else {
if(StackEmpty(S)){//扫描右括号,且当前栈空
renturn fasle;//匹配失败
}
char topElem;
Pop(S, topElem);//栈顶元素弹出
if(str[i] == ')' && topElem != '('){
return false;
}
if(str[i] == ']' && topElem != '['){
return false;
}
if(str[i] == '}' && topElem != '{'){
return false;
}
}
}
return StackEmpty(S);//检索全部括号后,栈空说明匹配成功
}
表达式求值问题
-
后缀表达式
左优先原则
注意:操作数是有左右顺序
转化为算法主要思想:
-
前缀表达式
右优先原则
注意:操作数是有左右顺序
二维数组的存储结构
- 行优先
-
列优先
-
对称矩阵
-
三角矩阵
- 对角矩阵的压缩存储
- 稀疏矩阵