第3章 栈和队列

第3章栈和队列

1.知识点提要

1.栈的定义及基本运算

2.栈的混洗

  1. 问:如果列车按照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。
  2. 栈的混洗不可能的顺序
    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,所以不能了。
    2. 如果限制不能连续pop几个的话,那么就是不能连着几个逆序,例如1234,不能连续pop三个,则,1432,就不对,因为432,连着逆序了三个
    3. 与c语言的语法特点相结合
      c语言的标识符不允许数字打头,只允许字母和下划线打头,所以又可以删除一批序列
  3. 写出所有可能性
    假定输出序列首元素的元素为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.两种进栈和出栈处理
  1. top=-1,top先自加,再放元素,这样栈所有空间都用上了
//进栈
s[++top]=x;
//出栈
x=s[top--]
  1. 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.理解栈的存储结构的要点
  1. 顺序栈判满(先自加的)top==maxsize-1;判空top==-1
  2. 进栈时先判栈满,出栈时先判栈空
  3. 所有的操作都是在栈顶实现的,所以不用设置尾指针
  4. 不知道栈的空间的时候,设置链式栈更合适
  5. 动态分配语句之后必须进行相应的判断是否分配成功的语句
  6. 如何判断S/X的序列正确性:任意时刻的S的数量都大于等于X的数量,如果最终栈还要求为空的话,那么再加一条最后的S和X的数量相同。
  7. 如何判断栈的最大深度:模拟一遍,并且注意一下逆序的序列例: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公式计算出来的)就剩下了接下来表格里的十种,然后,接下来,只需要在脑中模拟即可。
因为在同一端输入输出的情况已经排除,所以剩下的都是混合进出的。然后,根据后输入的先出来,必定之前的先进去的原则,进行模拟。
模拟的时候分两种情况,输出受限时,把他想成,左右两端输入,右端输出,把已经固定了的输进去,凑成所需序列,没有固定的话,输进去就输出来即可。输入受限是,把他想象成左端输入,左右两端输出,把已经固定了的输进去,凑成所需序列,没有固定的话,输进去输出来即可。
补:凑的技巧:

  1. 如果只能右端输出的时候,可以把所需序列逆序,构成栈里序列再想比较好像,例如,需要312,那么在栈里就得是213,出来才是312,少一个完就好想多了。
    下面的表格就是模拟结果,1表示成功,0表示不成功
  2. 只需要关注被固定的元素,例如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
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值