栈与队列

> 栈是一种只能在一端进行插入或者删除操作的线性表,其中允许进行插入或删除的一端称为栈顶。 ## 顺序栈 ```cpp typedef struct { int data[MaxSize]; int top; }SqStack; ``` 对于一个顺序栈st,一共有 4个要素,包括两个特殊的状态和两个操作: + 两个状态 - 栈空状态 ```st.top==-1;``` - 栈满状态 ```st.top==MaxSize-1;``` + 两个操作 - 元素x进栈 ```++(st.top); st.data[top]=x;``` - 元素x出栈 ``` x=st.data[top]; --(st.top);``` 顺序栈的几个基本操作(详细版): + 初始化栈 ```cpp void initStack(SqStack &st) { st.top = -1; } ``` + 判断栈空 ```cpp bool isEmpty(SqStack st) { if (st.top == -1) return true; else return false; } ``` + 判断栈满 ```cpp bool isFull(SqStack st) { if (st.top == MaxSize - 1) return true; else return false; } ``` + 进栈算法 ```cpp bool push(SqStack &st,int x) { if(isFull(st)) return false; ++st.top; st.data[st.top]=x; return true; } ``` + 出栈算法 ```cpp bool pop(SqStack &st, int &x) { if (isEmpty(st)) return false; x = st.data[st.top]; --st.top; return true; } ``` > 在实际的题目中初始化栈、进栈以及出栈不必写的上述那么详细 > 初始化栈 ```int stack[MaxSize]; int top==-1;``` > 元素 x 进栈 ```stack[++top]=x;``` > 元素 x 出栈 ```x=stack[top--];``` ## 链栈 ```cpp typedef struct linkLode { int data; struct linkLode*next; }LiStack; ``` 对于一个有带头节点的链栈 ls ,也包括 4 个要素,包括两个状态和两个操作: + 两个状态 - 栈空状态: ```ls->next==NULL;``` - 栈满状态:一般认为没有(但是受内存限制,即内存满的时候就无法申请新的节点,此时为栈满) + 两个操作 - 元素(指针ptr指向)进栈操作,即为头插法 ```cpp ptr->next = ls->next; ls->next = ptr; ``` - 出栈操作 ```cpp ptr = ls->next; x = ptr->data; ls->next = ptr->next; free(ptr); ptr = NULL; ``` ## 共享栈 两个栈共享一个数组A[0..MaxSize-1]的空间,数组 A 两端都是栈底,栈顶向中间生长 左边栈的 4 要素为: - 栈空:```top1==-1``` - 栈满:```top1==top2-1``` - 进栈:```++top1; A[top1]=x;``` - 出栈: ```x=A[top1]; --top1;``` 右边栈的 4 要素为: - 栈空:```top2==MaxSize``` - 栈满:```top2==top1+1``` - 进栈:```--top2; A[top2]=x;``` - 出栈: ```x=A[top2]; ++top2;``` > 如果要解决的问题出现了这样一个状态:单凭现有的条件不能判断当前的状态是否可以解决,此时需要记录一些东西,等待以后出现可以解决当前状态的条件后返回来再解决。这种问题往往可以使用栈来解决。 假设表达式中允许包括 3种括号:圆括号、方括号、和大括号。设计一个算法采用顺序栈判断表达式中括号是否正确匹配。 ```cpp bool match(char exp[], int n) { char st[MaxSize]; int top = -1; bool flag = true; int i = 0; while (i < n && flag == true) { if (exp[i] == '(' || exp[i] = '[' || exp[i] = '{') { ++top; st[top] = exp[i]; } if (exp[i] == ')') { if (st[top] == '(') --top; else flag = false; } if (exp[i] == ']') { if (st[top] == '[') --top; else flag = false; } if (exp[i] == '}') { if (st[top] == '{') --top; else flag = false; } i++; } if (top >= 0) flag = false; return flag; } ``` 有一个带头结点的单链表 L ,用于存放整数序列,设计一个算法判断该序列是否是对称的。 ```cpp bool symtry(LinkList *L) { int st[MaxSize]; int top = -1; LinkList *ptr = L->next; while (ptr != NULL) { ++top; st[top] = ptr->data; ptr = ptr->next; } ptr = L->next; while (ptr != NULL) { if (ptr->data == st[top]) --top; else return flase; ptr = ptr->next; } return true; } ``` 编写一个算法,将一个非负的十进制数 N 转换为一个二进制数。 ```cpp int trans(int N) { int stack[MaxSize]; int top = -1; int i, result = 0; while (N != 0) { i = N % 2; N = N / 2; stack[++top] = i; } while (top != -1)//可以根据需求改变 { i = stack[top--]; result = result * 10 + i; } return result; } ``` ## 将算术表达式 exp 转换为后缀表达式 postexp > 后缀表达式是没有了括号并考虑了运算符优先级的一种表达式。将算术表达式 exp 转换为后缀表达式 postexp 的基本思想是:采用运算符栈比较运算符的优先级,所有运算符必须进栈,只将大于栈顶优先级的运算符直接进栈,否则需要退栈栈顶运算符(先退栈的先计算,同优先级的运算符在栈中的先计算) - 采用运算符栈: ```cpp while(从 exp 中循环读取字符 ch) { ch 为数字: 将后续的所有数字均依次存放到 postexp中。 ch 为左括号"(": 将其放入到运算符栈op中。 ch 为右括号$")"$: 将栈op中左括号"("以前的运算符依次出栈并存放到postexp中,然后将左括号"("出栈。 ch 的优先级大于栈op的栈顶运算符的优先级或栈顶为"(":将ch进栈,否则依次退掉栈顶优先级大于或同级运算符到postexp中,然后将ch进栈。 } ``` 将栈op中的所以运算符出栈并存放到postexp中,最后得到后缀表达式。 - 手工方法:需要人工判断表达式的执行顺序(即加括号),所以无法用程序实现 + 先根据中缀表达式的求值次序加上括号 + 将右括号用括号内的相应运算符替换 + 去除所有的左括号 以```5+2*(1+6)-8/2```为例说明以中缀表达式变成后缀表达式: ![屏幕快照 2016-07-15 下午4.17.23.png](https://img-blog.csdn.net/20170507191535850) - 对后缀表达式postexp求值 ```cpp while(从postexp中循环读取所有的字符ch) { ch 为数字:进到数值栈st中; ch 为"+":从数值栈st中退两个运算数,相加后进数值栈st中; ch 为"-":从数值栈st中退两个运算数,相加后进数值栈st中; ch 为"*":从数值栈st中退两个运算数,相加后进数值栈st中; ch 为"/":从数值栈st中退两个运算数,相加后进数值栈st中(若除数为0,则提示错误信息); } ``` ## 队列 > 队列是一种插入和删除操作受限的线性表,其插入和删除操作在不同端进行,插入元素的一端为队尾,删除元素的一端为队头。 队列中一般使用 font 和 rear 来指示队头和队尾,front 指向队列中队首元素的前一个位置,rear 指向队尾元素,因此: - 进队操作是先将 ```rear++;``` 再将元素放到 rear 位置 - 出队操作是先将队头 ```front++;``` 再取出队头元素 #### 顺序队列 ```cpp typedef struct { int data[MaxSize]; int front,rear; }SqQueue; ``` > 进队操作进队操作是先将 ```rear++;``` 再将元素放到 rear 位置;出队操作是先将队头 ```front++;``` 再取出队头元素。这样导致队头和队尾指针只增加不减少,被出队的空间无法再使用。于是通过逻辑方法改为首尾相连的循环队列可以反复利用已经出队的空间。 - 队空条件: ```qu.front==qu.rear``` - 队满条件: ```(rear+1)%MaxSize==qu.front``` - 进队: ```qu.rear=(qu.rear+1)%MaxSize; qu.data[rear]=x;``` - 出队: ```qu.front=(qu.front+1)%MaxSize; x=qu.data[front];``` - 队中元素个数: ```(qu.rear-qu.front+MaxSize)%MaxSize;``` 队列的基本运算(详细) - 初始化队 ```cpp void initQueue(SqQueue &qu) { qu.front = qu.rear = 0; } ``` - 判断队空 ```cpp bool isEmpty(SqQueue qu) { if (qu.front == qu.rear) return true; else return false; } ``` - 进队 ```cpp bool enQueue(SqQueue &qu, int x) { if ((qu.rear + 1) % MaxSize == qu.front)//是否队满 return false; qu.rear = (qu.rear + 1) % MaxSize; qu.data[qu.rear] = x; return true; } ``` - 出队 ```cpp bool deQueue(SqQueue &qu, int &x) { if (qu.rear == qu.front)//是否队空 return false; qu.front = (qu.front + 1) % MaxSize; x = qu.data[qu.front]; return true; } ``` #### 链队 ```cpp struct qnode { int data; struct qnode *next; } QNode; typedef struct { QNode *front; //队头指针 QNode *rear; //队尾指针 } LiQueue; ``` - 队空条件:```qu->font==NULL;或 qu->rear==NULL``` ```cpp bool isEmpty(LiQueue *lqu) { if (lqu->rear == NULL || lqu->front == NULL) return true; else return false; } ``` - 队满条件:一般认为不存在(实际上受内存大小的限制) - 进队:要考虑原来队空操作 ```cpp void enQueue(LiQueue *lqu, int x) { QNode *ptr; ptr = (QNode *) malloc(sizeof(QNode)); ptr->data = x; ptr->next = NULL; if (lqu->rear == NULL) lqu->front = lqu->rear = ptr; else { lqu->rear->next = ptr; lqu->rear = ptr; } } ``` - 出队:要考虑队空和只有一个节点 ```cpp bool deQueue(LiQueue *lqu, int &x) { QNode *ptr; if (lqu->rear == NULL) //队空 return false; else ptr = lqu->front; if (lqu->rear == lqu->front) //只有一个元素 lqu->front = lqu->rear = NULL; else lqu->front = ptr->next; x = ptr->data; free(ptr); ptr = NULL; return true; } ``` #### 双端队列 > 双端队列是指两端都可以进行进队和出队操作的的队列 ![VISIO_2016-07-15_21-34-12.png](https://img-blog.csdn.net/20170507191526970)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值