1.栈的应用
1.括号匹配问题(还有确定他们的符号优先级)
如 2+((3+2) * 3) /3
扫描到左括号入栈,右括号入另外一个栈,如果两个栈数量相同,则是匹配的,不保存,不然去找最少的栈,出来提示报错
2.十进制转2进制 如 23转二进制10111,需要栈辅助 每次除2的余数倒过来写
3.main函数的嵌套调用(函数调用栈) 如
4.idea开发工具的 ctr+z撤回上一步 比如我写了 1 2 3代码然后入栈,出来3 2 1可以回到我原来写1的代码
2.什么是栈? 限定一端 插/删 的线性表 特点:后进先出(LIFO)
3.顺序栈的实现c语言代码
#include <stdio.h>
#include <stdlib.h>
/*将顺序栈的存储结构定义和各个函数定义放到这里*/
#define StackSize 100 /*假定栈元素最多100个*/
typedef int DataType; /*定义栈元素的数据类型,假设为int型*/
typedef struct {
DataType data[StackSize]; /*存放栈元素的数组*/
int top; /*栈顶位置,栈顶元素在数组中的下标*/
} SeqStack;
void InitStack(SeqStack *S) { //初始化栈顶为 -1
S->top = -1;
}
int Push(SeqStack *S, DataType x) { //入栈,就是让top+1非常简单,但是要注意是否超过最大的个数
if (S->top == StackSize - 1) {
printf("上溢错误,插入失败\n");
return 0;
}
S->data[++S->top] = x;
return 1;
}
int Pop(SeqStack *S, DataType *ptr) { //让top-1,如果top<0没有元素了
if (S->top == -1) {
printf("下溢错误,删除失败\n");
return 0;
}
*ptr = S->data[S->top--];
return 1;
}
int GetTop(SeqStack *S, DataType *ptr) {//得到顶部的元素,如果是负数失败
if (S->top == -1) {
printf("下溢错误,取栈顶失败\n");
return 0;
}
*ptr = S->data[S->top];
return 1;
}
int Empty(SeqStack *S) { //判断栈是否为空,top==-1为空
if (S->top == -1) return 1; /*栈空则返回1*/
else return 0;
}
int main( ) {
DataType x;
SeqStack S; /*定义结构体变量S为顺序栈类型*/
InitStack(&S); /*初始化顺序栈S*/
printf("对15和10执行入栈操作,");
Push(&S, 15);
Push(&S, 10);
if (GetTop(&S, &x) == 1)
printf("当前栈顶元素为:%d\n", x); /*输出当前栈顶元素10*/
if (Pop(&S, &x) == 1)
printf("执行一次出栈操作,删除元素:%d\n", x); /*输出出栈元素10*/
if (GetTop(&S, &x) == 1)
printf("当前栈顶元素为:%d\n", x); /*输出当前栈顶元素15*/
printf("请输入待入栈元素:");
scanf("%d", &x);
Push(&S, x);
if (Empty(&S) == 1)
printf("栈为空\n");
else
printf("栈非空\n"); /*栈有2个元素,输出栈非空*/
return 0;
}
4.链栈实现代码
//原理解析,先设计一个空的top 放栈顶元素,然后
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
/*将单链表的结点结构定义和链栈的各个函数定义放到这里*/
typedef int DataType; /*定义线性表的数据类型,假设为int型*/
typedef struct Node { /*定义单链表的结点类型*/
DataType data;
struct Node *next;
//struct Node *top;
} Node;
Node *InitStack( ) { //初始化栈, new一个节点,然后->next指向NULL
Node *top = NULL;
top = (Node*)malloc(sizeof(Node));
top->next = NULL;
return top;
}
void DestroyStack(Node *top) { //临时指针p指向top,top不为null移动到下一个,删除p节点,p又指向top
Node *p = top;
while (top != NULL) { /*依次释放链栈的每一个结点*/
top = top->next;
free(p);
p = top;
}
}
void Push(Node *top, DataType x) { //入栈 new一个节点然后放数据, 新节点指向top->next,top->next指向新节点
Node *s = (Node *)malloc(sizeof(Node)); /*申请一个结点s*/
s->data = x;
s->next = top->next;
top->next = s; /*将结点s插在栈顶*/
}
int Pop(Node *top, DataType *ptr) { //出栈,p节点保存top->next,top->next=top->next->next,free(p)
Node *p = top->next;
if (top->next == NULL) {
printf("下溢错误,删除失败\n");
return 0;
}
*ptr = top->next->data; /*存储栈顶元素*/
top->next = p->next; /*将栈顶结点摘链*/
free(p);
return 1;
}
int GetTop(Node *top, DataType *ptr) { //得到顶部元素,返回 top->next->data即可,注意判空
if (top->next == NULL) {
printf("下溢错误,取栈顶失败\n");
return 0;
}
*ptr = top->next->data;
return 1;
}
int Empty(Node *top) { //判断栈是否为空 top->next为空 栈就为空
if (top->next == NULL) return 1; /*栈空则返回1*/
else return 0;
}
int main( ) {
DataType x;
Node *top = NULL; /*定义链栈的栈顶指针并初始化*/
top = InitStack(); /*初始化链栈*/
printf("对15和10执行入栈操作,");
Push(top, 15);
Push(top, 10);
if (GetTop(top, &x) == 1)
printf("当前栈顶元素为:%d\n", x); /*输出当前栈顶元素10*/
if (Pop(top, &x) == 1)
printf("执行一次出栈操作,删除元素:%d\n ", x); /*输出出栈元素10*/
if (GetTop(top, &x) == 1)
printf("当前栈顶元素为:%d\n", x); /*输出当前栈顶元素15*/
printf("请输入待插入元素:");
scanf("%d", &x); //输入17
Push(top, x);
if (Pop(top, &x) == 1)
printf("执行一次出栈操作,删除元素:%d\n ", x); /*输出出栈元素17*/
if (GetTop(top, &x) == 1)
printf("当前栈顶元素为:%d\n", x); /*输出当前栈顶元素15*/
if (Empty(top) == 1)
printf("栈为空\n");
else
printf("栈非空\n"); /*栈有1个元素,输出栈非空*/
DestroyStack(top);
return 0;
}
5.队列的应用场景
1.银行排队拿号
2.打印机的缓冲区(多台电脑共享一台打印机)
3.网卡
4.rabbitMQ 消息队列,可以解决消息延迟处理,以防高并发宕机的问题,还可以设置优先队列指定哪些消息优先被处理
6.什么是队列?(线性表+限制)
1.只允许一端 插入,另外一端删除的结构 如图: 队头1出队 队列就变成了 5 4 3 2;2变成队头
2.特点先进先出(FIFO) 入队序列=出队序列,而栈 入队序列!=出队序列
3.双端队列(deque) 队首和队尾都可以出(不符合队列定义)
7.顺序队列
1.定义rear插入 O(1)
2.出队 删队头,移动后面的队列元素 O(n) 需要提高到O(1)[改为循环队列!!!工作常用]
3.优化队头 队尾指针假溢出问题(底端有空间没有被使用)(使用循环队列)
顺序队列如图:
循环队列如图:设队列长度为3 队尾下标=队尾下标%3(队列长度) 放入元素 1和2过程
我要出队头元素
//我要入队3元素
//然后 rear=(++rear)%3 即rear=(3)%3 0 ha还要判空,rearfront队列满
8.循环队列c语言代码实现
#include <stdio.h>
#include <stdlib.h>
/*将循环队列的存储结构定义和各个函数定义放到这里*/
#define QueueSize 100 /*定义数组的最大长度*/
typedef int DataType; /*定义队列元素的数据类型,假设为int型*/
typedef struct {
DataType data[QueueSize]; /*存放队列元素的数组*/
int front, rear; /*下标,队头元素和队尾元素的位置*/
} CirQueue;
void InitQueue(CirQueue *Q) {
//队头和队尾都指向最后一个元素 也可以为 -1,为了后续+1后队满做准备
Q->front = Q->rear = QueueSize - 1;
}
int EnQueue(CirQueue *Q, DataType x) { //O(1)
if ((Q->rear + 1) % QueueSize == Q->front) {
//如果rear+1等于队头说明队满,不能写(Q->rear) % QueueSize == Q->front 因为一开始就rear==front
printf("上溢错误,插入失败\n");
return 0;
}
Q->rear = (Q->rear + 1) % QueueSize; /*队尾位置在循环意义下加1*/
Q->data[Q->rear] = x; /*在队尾处插入元素*/
return 1;
}
int DeQueue(CirQueue *Q, DataType *ptr) { //O(1) //这里可以rear==front队空,队满和队空判断时机不一样
if (Q->rear == Q->front) {
printf("下溢错误,删除失败\n");
return 0;
}
//出队也是加1
Q->front = (Q->front + 1) % QueueSize; /*队头位置在循环意义下加1*/
*ptr = Q->data[Q->front]; /*读取出队前的队头元素*/
return 1;
}
int GetHead(CirQueue *Q, DataType *ptr) { //得到队头就是得到front指向的值,和判空一样
int i;
if (Q->rear == Q->front) {
printf("下溢错误,取队头失败\n");
return 0;
}
i = (Q->front + 1) % QueueSize; /*注意不改变队头位置*/
*ptr = Q->data[i];
return 1;
}
int Empty(CirQueue *Q) { //判断队列是否为空,就是判断 rear==front
if (Q->rear == Q->front) return 1; /*队列为空返回1*/
else return 0;
}
int main( ) {
DataType x;
CirQueue Q; /*定义结构体变量Q为循环队列类型*/
InitQueue(&Q); /*初始化循环队列Q*/
printf("对5和8执行入队操作,");
EnQueue(&Q, 5);
EnQueue(&Q, 8);
if (GetHead(&Q, &x) == 1)
printf("当前队头元素为:%d\n", x); /*输出当前队头元素5*/
if (DeQueue(&Q, &x) == 1)
printf("执行一次出队操作,出队元素是:%d\n ", x); /*输出出队元素5*/
if (GetHead(&Q, &x) == 1)
printf("当前队头元素为:%d\n", x); /*输出当前队头元素8*/
printf("请输入入队元素:");
scanf("%d", &x);
EnQueue(&Q, x);
if (Empty(&Q) == 1)
printf("队列为空\n");
else
printf("队列非空\n"); /*队列有2个元素,输出队列非空*/
return 0;
}
9.链队列c语言代码
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
/*将链队列的存储结构定义和各个函数定义放到这里*/
typedef int DataType; /*定义队列元素的数据类型,假设为int型*/
typedef struct Node /*定义链队列的结点结构*/
{
DataType data;
struct Node *next;
} Node;
typedef struct /*定义链队列*/ //定义队头和队尾
{
Node *front, *rear;
} LinkQueue;
void InitQueue(LinkQueue *Q) //初始化队列,队头队尾指向新建立节点
{
Node *s = (Node *)malloc(sizeof(Node)); s->next = NULL;
Q->front = Q->rear = s; /*队头指针和队尾指针均指向头结点*/
}
void DestroyQueue(LinkQueue *Q) //队头一一出队也可以,下面是free
{
Node *p = Q->front,*ptrtemp;
while (p != NULL) /*依次释放链队列的结点*/
{
ptrtemp = p->next;
free(p);
p = ptrtemp;
}
}
void EnQueue(LinkQueue *Q, DataType x)
{
Node *s = (Node *)malloc(sizeof(Node));
s->data = x; s->next = NULL; /*申请一个数据域为x的结点s*/
Q->rear->next = s; Q->rear = s; /*将结点s插入到队尾*/
}
int DeQueue(LinkQueue *Q, DataType *ptr) //
{
Node *p;
//因为我们多了一个不存值的节点,所以如果出队 rear==front的话就出列失败
if (Q->rear == Q->front) {printf("下溢错误,删除失败\n"); return 0; }
p = Q->front->next; *ptr = p->data; /*存储队头元素*/
Q->front->next = p->next; /*将队头元素所在结点摘链*/
if (p->next == NULL) // 改的不是front指针而是他的next /*判断出队前队列长度是否为1*/
Q->rear = Q->front; //主要还是处理 队列有没有元素的时候的情况
free(p); return 1;
}
int GetHead(LinkQueue *Q, DataType *ptr)
{
Node *p = NULL;
if (Q->rear == Q->front) {printf("下溢错误,取队头失败\n"); return 0; }
p = Q->front->next;
*ptr = p->data; return 1;
}
int Empty(LinkQueue *Q) //也可以判断 front->next!=NULL实现
{
if (Q->rear == Q->front) return 1; /*队列为空返回1*/
else return 0;
}
void PrintQueue(LinkQueue * Q){ //定义p指针,先打印后next
printf("遍历输出队列:\n");
Node * p = Q->front->next;
while(p!=NULL){
printf("data: %d, next: %p\n",p->data, p->next);
p = p->next;
}
}
int main( )
{
DataType x;
LinkQueue Q; /*定义结构体变量Q为链队列类型*/
InitQueue(&Q); /*初始化链队列Q*/
PrintQueue(&Q);
printf("对5和8执行入队操作,");
EnQueue(&Q, 5);
PrintQueue(&Q);
EnQueue(&Q, 8);
PrintQueue(&Q);
if (GetHead(&Q, &x) == 1)
printf("当前队头元素为:%d\n", x); /*输出当前队头元素5*/
if (DeQueue(&Q, &x) == 1)
printf("执行一次出队操作,出队元素是:%d\n", x); /*输出出队元素5*/
if (GetHead(&Q, &x) == 1)
printf("当前队头元素为:%d\n", x); /*输出当前队头元素8*/
printf("请输入入队元素:");
scanf("%d", &x);
EnQueue(&Q, x);
if (Empty(&Q) == 1)
printf("队列为空\n");
else
printf("队列非空\n"); /*队列有2个元素,输出队列非空*/
DestroyQueue(&Q);
return 0;
}
总结: 出队(判断有没有下一个元素,front->next出队,改指针) 入队 (new新节点 然后改原rear和后rear指向新节点)