第3章栈和队列
栈
1.知识点提要
1.栈的定义及基本运算
2.栈的混洗
- 问:如果列车按照1,2,3,,,n进站,那么有多少种情况出栈?不可能的情况有多少种?
答:根据catalan公式,可能的共有f(n)=C(2n,n)/(n+1)=(2n)!/(n!*n!*(n+1))
不可能的有n!-f(n)
种。
解析:首先,我们设f(n)=序列个数为n的出栈序列种数。(我们假定,最后出栈的元素为k,显然,k取不同值时的情况是相互独立的,也就是求出每种k最后出栈的情况数后可用加法原则,由于k最后出栈,因此,在k入栈之前,比k小的值均出栈,此处情况有f(k-1)种,而之后比k大的值入栈,且都在k之前出栈,因此有f(n-k)种方式,由于比k小和比k大的值入栈出栈情况是相互独立的,此处可用乘法原则,f(n-k)*f(k-1)种,求和便是Catalan递归式。ps.author.陶百百)
首次出空之前第一个出栈的序数k将1~n
的序列分成两个序列,其中一个是1~k-1
,序列个数为k-1,另外一个是k+1~n,序列个数是n-k。
此时,我们若把k视为确定一个序数,那么根据乘法原理,f(n)的问题就等价于——序列个数为k-1的出栈序列种数乘以序列个数为n - k的出栈序列种数,即选择k这个序数的f(n)=f(k-1)×f(n-k)。而k可以选1到n,所以再根据加法原理,将k取不同值的序列种数相加,得到的总序列种数为:f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0)。
看到此处,再看看卡特兰数的递推式,答案不言而喻,即为f(n)=h(n)= C(2n,n)/(n+1)= c(2n,n)-c(2n,n-1)(n=0,1,2,……)。
最后,令f(0)=1,f(1)=1。 - 栈的混洗不可能的顺序
- 因为,后入先出的元素之前没出的元素出栈顺序就已经固定,所以除去比第i个元素先进栈,先出栈的元素以后,进栈的第i个元素,第j个输出,则进栈序列第i个元素左侧第j个元素开始,至最左侧所有元素,的输出顺序都已固定。由此做题。
- 进栈顺序确定的话:例如:1234入栈,4先出栈的话,则进栈序列第4左侧第1个元素左侧所有元素的输出顺序都固定了,即123都固定了,所以输出结果只能是4321
- 出栈的顺序确定的话:例:已知一个栈的进栈序列为p1,p2,…pn,其输出序列为1,2,…,n。若p3=1,则p1的值(一定不是2)。
解析:如果p3=1,则入栈顺序为,p1,p2,1,p4,…,pn。而且1第一个出栈,所以固定了栈里的p1,p2,所以接下来无论继续出栈,还是再入栈再出栈,都不可能第一个出的时p1,而只有第一个出,才会是2,所以不能了。
- 如果限制不能连续pop几个的话,那么就是不能连着几个逆序,例如1234,不能连续pop三个,则,1432,就不对,因为432,连着逆序了三个
- 与c语言的语法特点相结合
c语言的标识符不允许数字打头,只允许字母和下划线打头,所以又可以删除一批序列
- 因为,后入先出的元素之前没出的元素出栈顺序就已经固定,所以除去比第i个元素先进栈,先出栈的元素以后,进栈的第i个元素,第j个输出,则进栈序列第i个元素左侧第j个元素开始,至最左侧所有元素,的输出顺序都已固定。由此做题。
- 写出所有可能性
假定输出序列首元素的元素为k,显然,k取不同值时的情况是相互独立的,所以按照这样的思路分成各个情况,再递归的使用此方法,再随时依据上面总结的混洗原则排除不可能的选项
例:写出按abc入栈的所有出栈顺序
解1.分三种a打头,b打头,c打头。2.a打头中,没有固定任何元素,所以可以分为,b打头和c打头……以此类推。最终确定,abc,acb,bac,bca,cba
3.栈的顺序存储结构
1.顺序栈的两种存储分配
1.静态存储分配
一旦栈满,无法扩充,所以,入栈之前,要判断是否栈满
#define maxsize 100
typedef int SElemtype
typedef struct {
SElemtype elem[maxsize];
int top;
}
2.动态存储分配及基本操作
typedef struct
{
ElemType *base;
ElemType *top;
int stacksize;
}SqStack;
初始时,先分配空间,然后初始化
S.base = (ElemType*)malloc(STACK_INIT_SIZE * sizeof(ElemType));
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
当扩容时
S.base = (ElemType *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(ElemType));
Status InitStack(SqStack &S)//初始化栈
{
//初始的时候改三个,top,base,stacksize,销毁的时候也是改这三个
S.base = (ElemType*)malloc(STACK_INIT_SIZE * sizeof(ElemType));
if (!S.base) exit(OVERFLOW);//存储分配失败
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return OK;
}
Status AssignmentStack(SqStack &S, int n) {
if (S.top - S.base >= S.stacksize)return FALSE;//因为是先放元素后加加,栈满的时候相当于在a[maxsize]上,就是出去这个数组了已经,所以和栈底一相减,就是maxsize
for (int i = 0; i < n; i++) {
cin >> *S.top++;
}
return OK;
}
Status DestroyStack(SqStack &S)//销毁栈
{
S.top = NULL;
S.stacksize = 0;
free(S.base);
return OK;
}
Status ClearStack(SqStack &S)//清空栈
{
S.top = S.base;
return OK;
}
Status StackEmpty(SqStack S)//判空栈
{
if (S.top == S.base)
return TRUE;
else
return FALSE;
}
Status StackLength(SqStack S)//求栈的长度
{
if (S.top == S.base)
return FALSE;
else
return (S.top - S.base);//也可以直接返回S.top - S.base
}
Status GetTop(SqStack S)//查看栈顶元素
{
if (S.top == S.base) return FALSE;
return *(S.top - 1);
}
Status Pop(SqStack &S)
{
if (S.top == S.base) return ERROR;
return *--S.top;
}
Status Push(SqStack &S, ElemType e)
{
if (S.top - S.base >= STACK_INIT_SIZE)
{
S.base = (ElemType *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(ElemType));
if (!S.base) exit(OVERFLOW);
S.top = S.base + STACK_INIT_SIZE;//栈底地址可能改变,重新定位栈顶元素
S.stacksize = S.stacksize + STACKINCREMENT;
}
*S.top++ = e;
return OK;
}
Status StackTraverse(SqStack S)//栈的遍历
{
if (S.base == NULL)
return ERROR;
if (S.top == S.base)
cout << "栈中没有元素" << endl;
ElemType *p;
p = S.top;
while (p > S.base)
{
p--;
cout << *p << " ";
}
return OK;
}
2.两种进栈和出栈处理
- top=-1,top先自加,再放元素,这样栈所有空间都用上了
//进栈
s[++top]=x;
//出栈
x=s[top--]
- top=0 ,先放元素,top再自加,最后一个位置是空的
3.两个顺序栈共用一个存储空间
让两个栈,一开始在一个存储空间的两端,同时向中间涨
0号栈:栈底指针为b[0]
,栈顶指针为t[0]
;
1号栈:栈底指针为b[1]
,栈顶指针为t[1]
;
两个栈的初始化:b[0]=t[0]=-1,b[1]=t[1]=maxsize
;
判空:b[0]==t[0]和b[1]=t[1]
判栈满:t[0]+1==t[1]
注意绝对不是t[0]+t[1]==maxsize
因为他的序号都是从左向右递加的,也因此右侧栈进栈t[1]--
总和进栈语句:top[i]+=(1-2i)
4.栈的链式存储结构
链表的表头指针,就是头指针,不需要头结点,来一个结点就头插就好了
typedef int Elemtype
typedef struct node{
SElemtype data;
struct node * link;
}LinkNode,*LinkStack;
//普通链表
//进栈
s->link=top;top=s;
//出栈
x=s->data;top=top->link;
//静态链表
//进栈
A[s].link=top;top=s;
//出栈
x=A[top].data;top=A[top].link;
5.理解栈的存储结构的要点
- 顺序栈判满(先自加的)
top==maxsize-1
;判空top==-1
- 进栈时先判栈满,出栈时先判栈空
- 所有的操作都是在栈顶实现的,所以不用设置尾指针
- 不知道栈的空间的时候,设置链式栈更合适
- 动态分配语句之后必须进行相应的判断是否分配成功的语句
- 如何判断S/X的序列正确性:任意时刻的S的数量都大于等于X的数量,如果最终栈还要求为空的话,那么再加一条最后的S和X的数量相同。
- 如何判断栈的最大深度:模拟一遍,并且注意一下逆序的序列例:1,2,3,9,8,10,11,12,7,6,14,13,5,4
所以,栈的最大深度为,6
算法题
1. 顺序栈逆序(用辅助数组)
注意Pop和Push要分开写
void algo1(SqStack &S){
int i,n=0,A[255];
while(!StackEmpty(S))A[n++]=Pop(S);
for(i=0;i<n;i++) Push(S,A[i]);
}
2. 删除栈中的元素e(用辅助栈)
void algo2(SqStack &S,int e){
SqStack T;InitStack(T);
int d;
while(!StackEmpty(S)){
d=Pop(S);
if(d!=e)Push(T,d);
}
while(!StackEmpty(T)){
Push(S,Pop(T));
}
}
3.判断输出序列是否正确
void Decision(int p[], int n) {
//用一个栈去模拟,如果没有栈顶元素,则进栈,如果有,则判断:栈顶元素比出栈序列小,则入栈,栈顶元素和出栈序列相等,则出栈,如果栈顶元素比出栈序列小,则出错,因为继续入栈会越来越大,继续出栈也达不到这个元素
SqStack S; InitStack(S);
int i = 0, k = 0, j; bool s = true;//i做入栈序列循环变量,k做出栈序列循环变量,j做出栈元素接受变量,s做判断变量
while (k < n) {
if (StackEmpty(S) || GetTop(S) < p[k])Push(S, ++i);
else if (GetTop(S) == p[k] && !StackEmpty(S)) {
j = Pop(S); k++; }//多个if的写成else if这样可以减少比较次数
else if (GetTop(S) > p[k]) {
s = false; break; }
}
for (j = 0; j < n; j++)printf("%d ", p[j]);
if (s)printf("是合理的出栈序列!\n");
else printf("是不合理的出栈序列!\n");
}
4.双栈结构即基本操作(先加加后放元素)
#define maxSize 20
typedef int SElemType;
typedef struct {
int top[2], bot[2];
SElemType elem[maxSize];
}DblStack;
void InitStack(DblStack &S) {
//初始化栈
S.top[0] = S.bot[0] = -1; S.top[1] = S.bot[1] = maxSize;
}
int StackEmpty(DblStack &S, int i) {
//判空
return S.top[i] == S.bot[i];
}
int StackFull(DblStack &S) {
//判栈满
return S.top[0] + 1 == S.top[1];
}
int Push(DblStack&S, ElemType x, int i) {
if (StackFull(S))return 0;
if (i == 0)S.elem[++S.top[0]] == x;
else S.elem[--S.top[1]] == x;
return 1;
}
SElemType Pop(DblStack &S, int i) {
if (i == 0)return S.elem[S.top[0]--];
else return S.elem[S.top[1]++];
}
SElemType GetTop(DblStack &S, int i) {
return S.elem[S.top[i]];
}
void printStack(DblStack &S, int i) {
int j;
if(i == 0) for (j = 0; j <= S.top[0]; j++)printf("%d ", S.elem[j]);
else for (j = maxSize-1; j >= S.top[1]; j--)printf("%d ", S.elem[j]);
printf("\n");
}
5.闭环双栈(先放元素后加加)
初始时top[0]=0,top[1]=maxSize-1
,将两个栈的栈顶指针置于数组下标为0和maxSize-1的位置,表示将两个栈置空,两个栈的判空条件(top[1]+1)%maxSize=top[0]
**进栈出栈策略:**对于0号栈,进栈时,先放元素,再加加,退栈时先减减,再取元素。对于1号栈,进栈时,先放元素,再减减,退栈时,先加加再取元素。
判断栈满top[0]==top[1]
#define maxSize 20
typedef int SElemtype;
typedef struct {
int top[2], bot[2];
SElemtype elem[maxSize];
}DblStack;
void InitStack(DblStack &S) {
//这里是先放元素再自加,所以初始化不同
S.top[0] = S.bot[0] = 0; S.top[1] = S.bot[1] = maxSize - 1;
}
int StackEmpty(DblStack &S, int i) {
//判断某个栈空不空
return(S.top[i] == S.bot[i]);
}
int StackAllEmpty(DblStack &S) {
//判断两个栈空不空
return (S.top[1] + 1) % maxSize == S.top[0];
}
int StackFull(DblStack&S) {
return S.top[0] == S.top[1];
}
int Push(DblStack &S, SElemtype x, int i) {
if (StackFull(S)) return 0;
S.elem[S.top[i]] = x;
if (i == 0)S.top[0]++;
else S.top[1]--;
return 1;
}
SElemtype Pop(DblStack&S, int i) {
if (i == 0)S.top[0]--;
else S.top[1]++;
return S.elem[S.top[i]];
}
SElemtype GetTop(DblStack &S, int i) {
return S.elem[S.top[i]];
}
void printStack(DblStack&S, int i) {
int j;
if (i == 0) for (j = 0; j < S.top[0]; j++)printf("%d ", S.elem[j]);
else for (j = maxSize - 1; j > S.top[1]; j--)printf("%d ", S.elem[j]);
printf("\n");
}
6.判断是否为对称序列
//数组的
#define maxSize 20
int isReverse(char A[]) {
//判断序列是否以&为对称轴的对称序列
char S[maxSize]; int top = -1;//顺序栈的本质就是个受限的数组,所以直接定义一个数组,和一个头指针就可以
char ch; int i = 0;
while (A[i] != '&'&&A[i] != '#') {
S[++top] = A[i]; i++; }
if (A[i] == '#')return 0;//没有遇到&
while (A[++i] != '#'&&top >= 0) {
//栈中元素没有走完,或者,序列没有走完
ch = S[top--];
if (A[i] != ch)return 0;
}
if ((A[i] == '#')&&(top == -1))return 1;
else return 0;
}
//链表的
int centreSym(LinkList &L) {
SqStack S; InitStack(S); LinkNode*p;
for (p = L->link; p != NULL && p->data < p->link->data; p = p->link) Push(S, p->data);
//关键是下面这一句能把奇数个数和偶数个数,还有递增序列分开。
if (p == NULL)return 0; else if (p->data == p->link->data) p = p->link->link; else if (p->data > p->link->data)p = p->link;
while (p != NULL && !StackEmpty(S)) {
if (Pop(S) != p->data)return 0; p = p->link; }
if (p == NULL && StackEmpty(S))return 1;
else return 0;
}
7.用栈实现链表逆置
void Reverse(LinkList &L) {
SqStack S; InitStack(S); LinkNode*p;
for (p = L->link; p != NULL ; p = p->link) Push(S, p->data);
p = L->link;
while (!StackEmpty(S)) {
p->data = Pop(S); p = p->link; }//写while的时候循环语句经常会丢掉
}
7.栈排序
void StackSort(SqStack &S) {
//利用栈排一个从栈底到栈顶依次增大的序列,当返回到原来的栈的时候,就是从栈顶到栈底依次增大的了。
//当遇到大的就先进栈,遇到小的就利用k暂存一下,把其他大的都还回去,依次排出一个序列
SqStack T; InitStack(T); int k;
while (!StackEmpty(S)) {
if (StackEmpty(T) || GetTop(S) > GetTop(T)) {
Push(T, Pop(S)); }
else if (!StackEmpty(T) && GetTop(S) < GetTop(T)) {
k = Pop(S);
while (!StackEmpty(T) && k < GetTop(T)) {
Push(S, Pop(T)); }
Push(T, k);
}
}
while (!StackEmpty(T)) Push(S, Pop(T));
}
//改进版
//因为只有当遇到小的的时候,是需要先还回去,剩下的都是把S的顶pop出来push到T中
void StackSort2(SqStack &S) {
SqStack T; InitStack(T); int k;
while (!StackEmpty(S)) {
k = Pop(S);
while (!StackEmpty(T) && k < GetTop(T)) Push(S, Pop(T));
Push(T, k);
}
while (!StackEmpty(T)) Push(S, Pop(T));
}
队列
1.知识点提要
1.队列的定义和基本运算
2.队列的顺序存储结构
- 队列设置两个指针,一个rear,一个front
- 为了消除“假溢出”现象,通常采用以maxsize为模的取模运算,来构成循环栈
- 进栈出栈的两种方式
- 进栈时,先放元素,队尾再加一,这样队首的操作比较方便,比如,查看队首元素或者出队之类的
- 进栈时,先加一再放元素,这样不方便
- 循环队列的组织(为了防止队空和队满没法区分,则牺牲一个存储单元,当front=rear的时候是空,(rear+1)%maxsize=front的时候是满)
//循环队列的结点定义
#define initSize 100
typedef int QElemType;
typedef struct {
QElemType *elem;//用来放元素。
int front, rear;//队首队尾指针
int maxSize;//标记模长,即队列最多可以放多少个元素
}CircQueue;
//循环队列的基本操作
void InitQueue(CircQueue Q) {
Q.elem = (QElemType*)malloc(initSize * sizeof(QElemType));
if (Q.elem == NULL) {
printf("存储分配失败!\n"); exit(1); }
Q.maxSize = initSize;
Q.rear = Q.front=0;
}
int QueueLength(CircQueue Q) {
//返回Q的个数,即队列的长度
return (Q.rear - Q.front + Q.maxSize) % Q.maxSize;
}
Status EnQueue(CircQueue &Q, QElemType e) {
//插入元素e为新的队尾元素
if ((Q.rear + 1) % Q.maxSize == Q.front)return ERROR;
Q.elem[Q.rear] = e;
Q.rear = (Q.rear + 1) % Q.maxSize;//以此实现循环队列
return OK;
}
Status DeQueue(CircQueue &Q, QElemType &e) {
//若队列不为空,则出队
if (Q.front == Q.rear)return ERROR;
e = Q.elem[Q.front];
Q.front = (Q.front + 1) % Q.maxSize;//以此实现循环队列
}
3.队列的链式存储结构
- 带有头结点的单链表形式
有两个结点,一个放链表,一个放头尾指针,链表就是普通定义,一个data,一个link,头尾指针的定义也很简单,就把头尾指针放进去就行 - 没有头指针,只有尾指针的循环单链表形式
入队就是队尾指针后尾插,出队就是弹出队尾指针之后的那个节点
只有头指针没有尾指针的话,也能实现相应的操作,只不过时间复杂度较高 - 链表形式的存储特别适合变化比较大的情况,而且不会溢出,比较安全
Status InitQueue(LinkQueue &Q) {
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));//这里就是为了构建队列的每一个结点,每一个节点,就是链表的一个结点,所以就要用LinkNode的大小和类型
if (!Q.front)exit(OVERFLOW);
Q.front->link = NULL;
return OK;
}
Status DestroyQueue(LinkQueue &Q) {
//因为是一个个的建立来的,所以要一个个的free掉
//其实实际是借用头尾指针作循环指针,先把尾指针跳到头指针的下一个,头指针指向的点free,然后头指针再跳到尾指针上,依次循环
while (Q.front) {
Q.rear = Q.front->link;
free(Q.front);
Q.front = Q.rear;
}
return OK;
}
Status EnQueue(LinkQueue &Q, QElemType e) {
LinkNode*p;
p = (LinkNode*)malloc(sizeof(LinkNode));
if (!p)exit(OVERFLOW);
p->data = e; p->link = NULL;
Q.rear->link = p;
Q.rear = p;
return OK;
}
Status DeQueue(LinkQueue &Q, QElemType &e) {
//链表的删除需要看空不空,插入需要看是否申请成功
if (Q.front == Q.rear)return ERROR;
LinkNode*p;
p = Q.front->link; e = p->data;
Q.front->link = p->link;
if (Q.rear == p)Q.rear = Q.front;//如果把最后一个元素也出队出去了,尾指针就会丢失,所以,要重新赋值,重新指向头结点
free(p);
return OK;
}
4.理解队列的要点
- 队列元素个数:(rear-front+maxSize)%maxSize
- 双端队列的不可能序列
- 原则:后输入先输出的元素,其他元素要全部进去
- 输入受限的:全部进去只有一种情况,只需要考虑怎么通过两端的输出得到相应的序列。
- 输出受限的:因为出来只有一种情况,通过分支法模拟所有在里面的元素序列,找到选项所需的。
- 原则:后输入先输出的元素,其他元素要全部进去
- 链式队列的每个节点还可以是队列。
- 若front=m-1,rear-0,则判空条件为(front+1)%m=rear,判满条件为front=rear
- 同时使用多个队列的时候可以用链式队列,然后,队列结构里面的指针用数组写,例如十个队列的话,就定义front[10],和rear[10]
5.双端队列
有三种类型:1.两端插入,两端删除。2.两端插入,一端删除。3.一端插入,两端删除。
- 可以把双端队列视为底靠底的双栈,但它们相通,成为双向队列。两端都可能是队头,队尾
- 输入受限的话,即只能一端输入,那么对于一个确定的输入序列,输出只能有3种可能:在同一端输出,相当于栈;或者在另一端输出,相当于队列;或者混合进出。
- 输出受限的话,即只能一端输出,那么对于一个确定的输入序列,输出只能有3种可能:在同一端输入和输出,相当于栈;或者一端输入,另一端输出,相当于队列;或者混合进出。
6.双端队列可能情况
例:数列1234可以由输出受限得到,不能有输入受限的得到的是
答:两种情况都是可以看成一个栈和一个队列的结合,栈可以改变序列顺序,队列不可以,而队列的顺序可以由栈得到,所以,先把栈可以输出的顺序排除掉,这是两种办法都可以实现的,所以一共剩下,4!-14=10种(由Caralan公式计算出来的)就剩下了接下来表格里的十种,然后,接下来,只需要在脑中模拟即可。
因为在同一端输入输出的情况已经排除,所以剩下的都是混合进出的。然后,根据后输入的先出来,必定之前的先进去的原则,进行模拟。
模拟的时候分两种情况,输出受限时,把他想成,左右两端输入,右端输出,把已经固定了的输进去,凑成所需序列,没有固定的话,输进去就输出来即可。输入受限是,把他想象成左端输入,左右两端输出,把已经固定了的输进去,凑成所需序列,没有固定的话,输进去输出来即可。
补:凑的技巧:
- 如果只能右端输出的时候,可以把所需序列逆序,构成栈里序列再想比较好像,例如,需要312,那么在栈里就得是213,出来才是312,少一个完就好想多了。
下面的表格就是模拟结果,1表示成功,0表示不成功 - 只需要关注被固定的元素,例如4123,被固定的是123,4最后放进去拿出来就可以,不需要管,再比如,3142,被固定的12,3不用管上面是说过了,4的话其实也没固定,放进去拿出来即可。
类型 | 4123 | 4132 | 4231 | 4312 | 4213 | 3124 | 3142 | 3412 | 2413 | 1423 |
---|---|---|---|---|---|---|---|---|---|---|
输出受限 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
输入受限 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 |
7.优先队列
- 当优先级高的时候,无论队列顺序都应该出去最高优先级的元素。
- 插入同样是在队尾插入,但是插入以后,要调用一个调整算法,把具有最高优先级的元素,调整到队首。当取出队首元素之后,同样要用一个调整算法, 把具有最高优先级的元素排到队首。
- 如果按照优先级进行普通调整,则时间复杂度为O(n),最好的办法是用堆,时间复杂度为O(logn)
算法题
1.查找最小
思路还是,用一个变量记录最小值,然后,遇到最小的更新
int FindMin(CircQueue &Q) {
int i, k, m = 0, n = QueueLength(Q);
Datatype min; DeQueue(Q, min); EnQueue(Q, min);
for (i = 1; i <= n; i++) {
DeQueue(Q, k);EnQueue(Q, k);//队列的遍历方式
if (k < min) {
min = k; m = i; }
}
return m;
}
**补充注意:**如果函数要进行对原始对象进行修改,就要用引用,如果改的话,头文件里也别忘了该。
2.逆序
void reverseQueue(CircQueue &Q) {
Datatype x; SqStack S; InitStack(S);
while (!QueueEmpty(Q))
{
DeQueue(Q, x); Push(S, x);
}
while (!StackEmpty(S))
{
x = Pop(S); EnQueue(Q, x);
}
}
3.以Q.rear和Q.length作为队列的指针
#define maxSize 20
typedef struct {
Datatype elem[maxSize];
int rear, length;
}Circqueue;
void InitQueue(Circqueue &Q) {
Q.length = 0; Q.rear = 0;
}
int EnQueue(Circqueue &Q, Datatype x) {
//改变值的话一定要加引用
//判满:Q.length=maxsize
if (Q.length == maxSize)return 0;
Q.rear = (Q.rear + 1) % maxSize;
Q.elem[Q.rear] = x;
Q.length++; return 1;
}
Datatype DeQueue(Circqueue &Q) {
//关键在找到头指针
int front = Q.rear - Q.length + 1;
if (front < 0)front = front + maxSize;
Datatype x = Q.elem[front];
Q.length--; return x;
}
6.Q.front和Q.length 来作为队列的指针
#define maxSize 20
typedef struct {
Datatype elem[maxSize];
int front, length;
}Circqueue;
void InitQueue(Circqueue &Q) {
Q.length = 0; Q.front = 0;
}
int EnQueue(Circqueue &Q, Datatype x) {
//改变值的话一定要加引用
//判满:Q.length=maxsize
if (Q.length == maxSize)return 0;
int rear = (Q.front + Q.length) % maxSize;
Q.elem[rear] = x;
Q.length++; return 1;
}
Datatype DeQueue(Circqueue &Q) {
//关键在找到头指针
Datatype x = Q.elem[Q.front];
Q.front = (Q.front + 1) % maxSize;
Q.length--; return x;
}
5.以Q.tag来区分是队满还是队空
//进队一个tag就变成1,进队出队别忘了指针也要变化
#define maxSize 20
typedef struct {
Datatype elem[maxSize];
int front, rear;
int tag;
}cirqueue;
void InitQueue(cirqueue &Q) {
Q.rear = Q.front = Q.tag = 0;
}
void EnQueue(cirqueue &Q, Datatype x) {
Q.elem[Q.rear] = x;
Q.rear = (Q.rear + 1) % maxSize;
Q.tag = 1;
}
Datatype DeQueue(cirqueue &Q) {
Datatype x = Q.elem[Q.front];
Q.front = (Q.front + 1) % maxSize;
Q.tag = 0; return x;
}
6.用两个队列模拟栈
//精华在于出栈,出栈的时候,所有的元素都导入到另一个栈中,然后最后一个元素留下不导入,依次出栈。
//实现也很简单,就是出队在入队的前面就行
//入栈还是依次入队,但是是谁不空入谁,保证一个队是空的
#define Size 20
void InitStack(CircQueue &Q1, CircQueue &Q2) {
InitQueue(Q1); InitQueue(Q2);
}
bool StackEmpty(CircQueue &Q1, CircQueue&Q2) {
return QueueEmpty(Q1) && QueueEmpty(Q2);
}
bool StackFull(CircQueue &Q1, CircQueue &Q2) {
return QueueFull(Q1) || QueueFull(Q2);
}
bool Push(CircQueue &Q1, CircQueue &Q2, int x) {
if (StackFull(Q1, Q2))return false;
if (StackEmpty(Q1, Q2))EnQueue(Q1, x);
else if (!QueueEmpty(Q1))EnQueue