数据结构笔记 — 栈和队列
栈和队列
1.1 栈基本概念
-
定义:栈
(Stack)
时一个后进先出的线性表,塔要求 只在表尾进行删除和插入操作-
栈的元素必须 “后进先出”
-
栈的操作只能在这个线性表的表尾进行
-
表尾称为栈的栈顶
(top)
,相应的表头称为栈底(bottom)
-
-
出栈和入栈
-
栈的插入操作
(Push)
,叫做进栈(入栈)
-
栈的删除操作
(Pop)
,叫做出栈(弹栈)
-
-
栈的本质是一个线性表,线性表有两种存储形式,那么栈也有分为 栈的顺序存储结构 和 栈的链式存储结构
-
最开始栈中不含有任何数据,叫做 空栈 ,此时 栈顶就是栈底 。
-
数据从栈顶进入,栈顶栈底分高,整个栈的当前容量变大
-
数据出栈时从栈顶弹出,栈顶下移,整个栈的当前容量变小
1.2 栈结构组成
-
栈结构的结构定义
typedef struct{ ElemType *base; ElemType *top; int stackSize; }sqStack; // *base : 指向栈底的指针变量 // *top : 指向栈顶的指针变量 // stackSize : 指示栈当前可使用的最大容量
1.3 栈基本操作
-
初始化一个栈
#define STACK_INIT_SIZE 100 initStack(sqSatck *s){ s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType)); if(!s->base){ exit(0); } s->top = s->base; //最开始,栈顶就是栈底 s->stackSize = STACK_INIT_SIZE; }
-
入栈操作
-
也叫作压栈操作,即香栈中存放数据
-
入栈操作在栈顶进行,每压入一个数据,
top
指针+1
,直到栈满为止#include <stdio.h> #include <stdlib.h> #define STACKINCREMENT 10 void Push(sqStack *s, ElemType e){ //若栈满,追加空间 if (s->top - s->base >= s->stackSize){ s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType)); if (!s->base){ exit(0); } s->top = s->base + s->stackSize; //设置栈顶 s->stackSzie = s->stackSzie + STACKINCREMENT; //设置栈最大容量 } *(s->top) = e; s->top++; }
-
-
出栈操作
-
每从栈内弹出一个数据,栈容量
-1
,栈顶指针下移#include <stdio.h> #include <stdlib.h> void Pop(sqStack *s, ElemType *e){ if (s->top == s->base){ //栈为空 exit(0); } *e = *--(s->top); }
-
-
清空栈
void clearStack(sqStack *s){ s->base = s->top; }
-
销毁栈
void destroyStack(sqStack *s){ int i, len; len = s->satckSize; for(i = 0; i < len, i++){ free(s->base); s->base++; } s->base = s->top = NULL; s->stackSize = 0; }
-
计算栈当前容量
int StackLength(sqStack s){ return (s.top - s.base); } // s.top - s.base 返回的是当前栈的容量(即当前栈的元素个数),该操作原理相当于: // (*top - *base) / sizeof(ElemType)
1.4 栈的链式存储结构
-
链栈结构
typedef struct StackNode{ ElemType data; //存放栈数据 struct StackNode *next; } StackNode, *LinkStackPtr; typedef struct LinkStack{ LinkStackPtr top; // top 指针 int count; // 栈元素计数器 }
-
进栈操作
Status Push(LinkStack *s, ElemType e){ LinkStackPtr p (LinkStackPtr)malloc(sizeof(StackNode)); p->data = e; p->next = s->top; s->top = p; s->count++; return OK; }
-
出栈操作
Status Pop(LinkStack *s,ElemType *e){ LinkStackPtr p; if (StackEmpty(*s)) //判新是否为空栈 return ERROR; *e = s->top->data; p = s->top; s->top = s->top->next; free(p); s->count--; return OK; }
1.5 逆波兰表达式
例如,对于
(1-2)*(4+5)
,表示为1 2 - 4 5 + *
,利用了栈的特性
先将
1、2
入栈,遇到操作符-
后将1、2
弹出栈并计算1-2
结果,把结果-1
入栈再将
4、5
入栈,遇到操作符+
后将4、5
弹出栈并计算4+5
结果,把结果9
入栈最后遇到操作符
*
入栈,将-1
和9
弹出栈,计算得到结果-9
,栈空,计算结束
【练习】
按照以上原理,实现逆波兰计算器,要求实现以下功能:
实现对逆波兰输入的表达式进行计算
支持带小数点的数据输入
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <ctype.h> 4 #define STACK_INIT_SIZE 20 5 #define STACK_INCREMENT 10 6 #define MAX_BUFFER 10 7 8 typedef double ElemType; 9 typedef struct { 10 ElemType *base; 11 ElemType *top; 12 int stackSize; 13 } sqStack; 14 15 //栈初始化 16 void InitStack(sqStack *s){ 17 s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType)); 18 if (!s->base) exit(0); 19 s->top = s->base; 20 s->stackSize = STACK_INIT_SIZE; 21 } 22 23 //入栈操作 24 void Push(sqStack *s, ElemType e){ 25 26 //栈满,追加空间 27 if (s->top - s->base >= s->stackSize){ 28 s->base = (ElemType *)realloc(s->base, (s->stackSize + STACK_INCREMENT) * sizeof(ElemType)); 29 if (!s->base){ 30 exit(0); 31 } 32 s->top = s->base + s->stackSize; 33 s->stackSize = s->stackSize + STACK_INCREMENT; 34 } 35 *(s->top) = e; 36 s->top++; 37 } 38 39 //出栈操作 40 void Pop(sqStack *s, ElemType *e){ 41 if (s->top == s->base) 42 return; 43 *e = *--(s->top); 44 } 45 46 //求栈长 47 int stackLen(sqStack s){ 48 return (s.top - s.base); 49 } 50 51 52 int main() { 53 54 sqStack s; 55 int i = 0; 56 char c; 57 char str[MAX_BUFFER]; 58 double d, e; 59 InitStack(&s); 60 61 printf("请按照逆波兰表达式输入带计算数据,数据之间使用运算符隔开,以 @ 结束:\n"); 62 scanf("%c", &c); 63 while (c != '@'){ 64 while (isdigit(c) || c == '.'){ 65 str[i++] = c; 66 str[i] = '\0'; 67 if (i >= 10){ 68 printf("输入单个数据过大!\n"); 69 return -1; 70 } 71 scanf("%c", &c); 72 if (c == ' '){ 73 d = atof(str); 74 Push(&s, d); 75 i = 0; 76 break; 77 } 78 } 79 80 switch (c) { 81 case '+': 82 Pop(&s, &e); 83 Pop(&s, &d); 84 Push(&s, d+e); 85 break; 86 case '-': 87 Pop(&s, &e); 88 Pop(&s, &d); 89 Push(&s, d-e); 90 break; 91 case '*': 92 Pop(&s, &e); 93 Pop(&s, &d); 94 Push(&s, d*e); 95 break; 96 case '/': 97 Pop(&s, &e); 98 Pop(&s, &d); 99 if (e){ 100 Push(&s, d/e); 101 } else{ 102 printf("除数为零!\n"); 103 return -1; 104 } 105 break; 106 } 107 scanf("%c", &c); 108 } 109 110 Pop(&s, &d); 111 printf("最终的结果为: %f\n", d); 112 113 return 0; 114 }
2 队列
2.1 队列概念
-
队列
(queue)
只允许在一端进行插入操作,在另一端进行删除操作的线性表,是一种 先进先出 的线性表 -
结构定义
typedef struct QNode{ ElemType data; struct QNode *next; } QNode, *QueuePtr; typedef struct { QueuePtr front, rear; } LinkQueue;
2.2 队列基本操作
-
创建队列
void InitQueue(LinkQueue *q){ q->front = q->rear = (QueuePtr)malloc(sizeof(ElemType)); if (!q->front) exit(0); q->front->next = NULL; }
-
入队列操作
void InsertQueue(LinkQueue *q, ElemType e){ QueuePtr p; p = (QueuePtr)malloc(sizeof(QNode)); if (!p) exit(0); p->data = e; p->next = NULL; q->rear->next = p; q->rear = p; }
-
出队列操作
void DeleteQueue(LinkQueue *q, ElemType *e){ QueuePtr p; if (q->front == q->rear) return; p = q->front->next; *e = p->data; q->front->next = p->next; if (q->rear == p) q->rear = q->front; free(p); }
-
销毁队列
void DestroyQueue(LinkQueue *q){ while (q->front){ q->rear = q->front->next; free(q->front); q->front = q->rear; } }
2.3 循环队列
-
结构组成
typedef struct { ElemType *base; int front, rear; } CycleQueue;
-
初始化循环队列
void InitCQueue(CycleQueue *q){ q->base = (ElemType *)malloc(MAXSIZE * sizeof(ElemType)); if (!q->base) exit(0); q->front = q->rear = 0; }
-
入循环队列
void InsertCQueue(CycleQueue *q, ElemType e){ if ((q->rear*1) % MAXSIZE == q->front) return; q->base[q->rear] = e; q->rear = (q->rear+1) % MAXSIZE; }
- 出循环队列
void DeleteCQueue(CycleQueue *q, ElemType *e){ if (q->front == q->rear) return; *e = q->base[q->front]; q->front = (q->front+1) % MAXSIZE; }
>>>>> END <<<<<