天勤2022数据结构(二)栈和队列

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

顺序栈
typedef struct{
	int top;
	int data[maxSize];	
}SqStack;

链栈结点
typedef struct LNode{
	int data;
	struct LNode *next;
}LNode;

顺序队列
typedef struct{
	int front;
	int rear;
	int data[maxSize];
}SqQueue;

链队列
typedef struct QNode{
	int data;
	struct QNode *next;
}QNode;

typedef struct{
	QNode *front;
	QNode *rear;
}LiQueue

基本操作

一、顺序栈操作

初始化

void initStack(SqStack &st){
	st.top = -1;
}

判断栈空

int isEmpty(SqStack st){
	return top == -1?1 :0;
}

进栈 [上溢]
[注意边界条件!!!] 栈满 top=maxSize-1

int push(SqStack &st, int x){
	if(st.top == maxSize-1)
		return 0;
	st.data[++st.top] = x;
	return 1;
}

出栈 [下溢]
[注意边界条件!!!] 栈空 top=-1

int pop(SqStack &st, int &x){
	if(top == -1)
		return 0;
	x = st.data[st.top--];
	return 1;
}

二、链栈

初始化

void initStack(LNode *&lst){
	lst = (LNode*)malloc(sizeof(LNode));
	lst -> next = NULL;
}

判断栈空

void isEmpty(LNode *lst){
	if(lst->next == NULL)
		return 1;
	return 0;
}

进栈 [头插法]

void push(LNode *&lst, int x){
	LNode *p = (LNode*)malloc(sizeof(LNode));
	p->data = x;
	
	p->next = lst->next;
	lst->next = p;
}

出栈
[释放结点] 栈空 lst->next=NULL

int pop(LNode *&lst, int &x){
	LNode *p;
	if(lst->next == NULL)
		return 0;
	p = lst->next;
	lst = lst->next->next;
	x = p->data;
	free(p);
	return 1;
}

三、顺序队

初始化
队空:front=rear

void initQueue(SqQueue &qu){
	qu.front = qu.rear = 0;
}

判断队空

int isEmpty(SqQueue qu){
	return qu.front == qu.rear;
}

进队
先移动rear,再赋值

int offer(SqQueue &qu, int x){
	if((qu.rear + 1)%maxSize == qu.front){
		return 0;
	}
	********************************
	*qu.rear = (qu.rear+1)%maxSize;*
	********************************
	qu.data[qu.rear] = x;
	return 1;
}

出队
先移动front,再赋值

int remove(SqQueue &qu, int &x){
	if(qu.rear == qu.front){
		return 0;
	}
	*********************************
	*qu.front= (qu.front+1)%maxSize;*
	*********************************
	x = qu.data[qu.front];
	return 1;
}

真题仿造

  1. 为了充分利用空间,顺序栈s0、s1共享一个存储区elem[0,…, maxSize-1]试设计共享栈s0、s1以及有关入栈和出栈操作的算法,假设中元素为int型。要求:

    1. 给出基本设计思想

      [Hint] 栈顶指针相向移动
      栈底设在存储区的两端,即s0栈底设在下标0处,s1栈底设在下标maxSize-1处
      栈顶在0~maxSize-1的范围内变动,当两栈栈顶相遇时为栈满。

    2. 根据设计思想,采用C或C++语言描述算法(对于共享栈要写出其结构定义),关键之处给出注释

      //共享栈栈结构体定义
      typedef struct{
      	int top[2];			// top[0]为s0栈顶,top[1]为s1栈顶
      	int elem[maxSize];				
      }SqStack;
      
      // 入栈
      int push(SqStack &st, int stNo, int x){	//stNo是栈的编号
      	if(st.top[0] + 1 ==st.top[1])
      		return 0;	// 栈满后不能入栈,返回0
      	if(stNo == 0){
      		++(st.top[0]);
      		st.elem[st.top[0]] = x;
      		return 1;	// 入栈成功,返回1
      	}else if(stNo == 1){
      		--(st.top[1]);
      		st.elem[st.top[1]] = x;
      		return 1;
      	}else
      		return -1;	// 栈编号输入有误,返回-1
      }
      // 出栈
      int pop(SqStack &st, int stNo, int &x){
      	if(stNo == 0 && st.top[0]!=-1){	// st0栈且不空
      		x = st.top[0]--;
      		return 1;	// 出栈成功,返回1
      	}else if(
      		stNo == 1 && st.top[1]!=maxSize){	// st1栈且不空
      		x = st.top[1]++;
      		return 1;	// 出栈成功,返回1
      	}else
      		return 0;	// 出栈错误,返回0;
      }
      
      
  2. 请利用两个栈s1和s2来模拟一个队列,假设中元素为int型,栈中元素最多为 maxSize。已知的3个运算定义如下。

    push(ST,x):元素x入ST栈
    pop(ST,&x):ST栈顶元素出栈,赋给变量x
    isEmpty(ST):判断ST栈是否为空

    如何利用的运算来实现该队列的3个运算: enQueue(元素入队列)、 deQueue(元素出队列) isQueueEmpty(判断队列是否为空,空返回1,不空返回0)。要求:

    (1)给出基本设计思想

    s1为输入栈:逐个元素进栈,模拟入队
    s2为输出栈:将s1中元素退栈后逐个压入s2栈。s1最先入栈的元素在s2中处于栈顶。s2退栈,模拟出队
    当s1,s2 均为空 时,队列为n空

    (2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。

    typedef struct{
    	int data[maxSize];
    	int top = -1;
    }SqStack;
    
    // 入队
    int enQueue(SqStack &s1, SqStack &s2, int x){
    	int y; 
    	if(s1.top == maxSize-1){
    		if(!isEmpty(s2))
    			return 0;	// s1满,s2非空,这时s1不能再入栈,返回0
    		else{
    			while(!isEmpty(s1)){	//s1满,s2空,将s1中元素逐个退栈并压入到s2中
    				pop(s1, y);
    				push(s2, y);
    			}
    			push(s1, x);
    			return 1;
    		}
    	}
    	// s1没有满,直接入栈
    	push(s1, x);	
    	return 1;
    }
    
    //出栈
    int deQueue(SqStack &s2, SqStack &s1, int &x){
    	if(isEmpty(s2)){
    		if(isEmpty(s1))
    			return -1;	// 两个栈中均无元素,队列为空
    		int y;
    		while(!isEmpty(s1)){	//s2空,s1不为空,将s1中元素逐个退栈并压入s2中
    			pop(s1, y);
    			push(s2, y);
    		}
    		pop(s2, x);
    		return 1;
    	}
    	// s2不空,直接出栈
    	pop(s2, x);
    	return 1;
    }
    
    //判断队列是否为空
    int isQueueEmpty(SqStack s1, SqStack s2){
    	return isEmpty(s1) && isEmpty(s2);
    }
    

基础题

  1. 铁路进行列车调度时,常把站台设计成栈式结构,试问:

    • 设有编号为1,2,3,4,5,6的6辆列车,顺序开入栈式结构的站台,则可能的出栈序列有多少种?

      f ( n ) = 1 n + 1 C 2 n n = 1 6 + 1 C 12 6 f(n)=\frac{1}{n+1}C_{2n}^{n}=\frac{1}{6+1}C_{12}^{6} f(n)=n+11C2nn=6+11C126

    • 若进站的6辆列车顺序如上所述,那么是否能够得到 435612 、 326541 、 154623 、 135426 435612、326541、154623、135426 435612326541154623135426的出站序列?如果不能,说明为什么不能如果能,说明如何得到(写出进栈或出栈的序列)

      不能得到:435612,154623
      因为若在4,3,5,6之后再将1,2出栈,则1,2必须一直在栈中,此时1先进栈,2后进栈,2应在1的上面,不可能先于1,2。同理。

  2. 试证明:若借助可由输入序列1,2,3,…,得到一个输出序列p1,p2,p3,…pn(它是输入序列的某一种排列),则在输出序列中不可能出现以下情况:存在i,j,k,使得pj<pk<pi(提示用反证法)

    必要性:
    当i<j<k时,进栈顺序时i,j,k,这3个元素的出栈的相对顺序时pi,pj,pk。当较大的数首先出栈时,那些较小的数都是降序压在栈内的。这些数不可能正序出栈

    充分性:
    如果pj<pk<pi成立,表明当i<j<k时各元素进栈、出栈后的相对顺序 为pj,pk,pi
    当j<k时,pj<pk,表明pj必须在pk进栈之前就出栈,否则pj就被压在pk下面了
    当i<k时,pk<pi,表明pi是先于pk进栈的
    综上所述可知,这与正确的出栈顺序pi<pj<pk相矛盾

  3. 假设以I和O分别表示入栈和出栈操作。若栈的初态和终态均为空,入栈和出栈的操作序列可表示为仅由I和O组成的序列,则称可以操作的序列为合法序列,否则称为非法序列

    • 试指出判别给定序列是否合法的一般规则

      1. 给定序列中,I的个数O的个数相等
      2. 从给定序列的开始到给定序列的任一位置,I的个数大于或等于O的个数
    • 两个不同的合法序列(对两个具有同样元素的输入序列)能否得到相同的输出元素序列?如能得到,请举例说明

      可以得到相同的输出元素序列
      例如:输入元素为A,B,C,则两个输入序列A,B,C和B,A,C均可得到输出元素序列A,B,C。对于输入 序列A,B,C,使用IOIOIO操作序列;对于输入序列B,A,C,使用IIOOIO操作序列

    • 写出一个算法,判定所给的操作序列是否合法。若合法,返回1,否则返回0(假定被判定的操作序列已存入一维char型数组ch[]中,操作序列以“\0”为结束符)

      int judge(char ch[]){
      	int i=0;
      	int I=0,O=0;
      	while(ch[i]!='\0'){
      		if(ch[i]=='I')
      			++I;
      		if(ch[i]=='O')
      			++O;
      		if(O>I)
      			return 0;
      		++i;
      	}
      	if(I!=0)
      		return 0;
      	else
      		return 1;
      }
      
  4. 有5个元素,其入栈次序为A,B,C,D,E,在各种可能的出栈次序中,以元素C,D最先出栈(C第一个且D第二个出栈)的次序有哪几个?

  5. 写出下列中缀表达式的后缀形式(单目运算操作,如!A的后缀表达式为A!)

    1. A*B*C

      A B*C *

    2. -A + B - C + D

      A-B+C-D+

    3. C-A*B

      CAB*-

    4. (A+B) * D + E/(F + A * D) + C

      AB+D * EFAD * +/+C+

    5. (A&&B)||(!(E>F))

      AB&& EF>! ||

    6. (!( A&& ( ! ( ( B<C ) || ( C>D )))))||(C<E)

      这里是引用

总结

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值