02-栈和队列

栈和队列

一、栈和队列的简介

  • 操作受限的线性表:将基本操作中的插入和删除加以“限制”。
  • 栈:限定仅在表尾进行插入或删除操作的线性表
  • 队列:只允许在表的一端进行插入,而在另一端删除元素的线性表

二、栈

  • 定义与特点:允许插入和删除的一端称为栈顶,另一端称为栈底,LIFO。
  • 栈的ADT
    ADT Stack{
        数据对象
        数据关系
        基本操作
        InitStack(stack &S);
        DestroyStack(&S);
        ClearStack(&S);
        StackEmpty(S);
        StackLength(S);
        GetTop(S, &e);
        Push(&S, e);
        Pop(&S, &e);
        StackTraverse(S, visit());
    }
    
  • 顺序栈
    • 栈的顺序存储结构,利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。
    • 指针top指向栈顶元素的下一个位置,base位栈底指针,指向栈底的位置。
  • 链式栈
    • 链式栈无栈满问题,空间可扩充
    • 插入与删除仅在栈顶处执行
    • 链栈的栈顶在链头
    • 适合于多栈操作
  • 代码参考01-线性结构,链表的尾插与尾删即为栈的pop和push
  • 栈的应用
    • 数制转换
    void conversion(){
        InitStack(S);
        scanf("%d", N);
        while(N){
            Push(S, N%8);
            N = N / 8;
        }
        while(!StackEmpty(S)){
            Pop(S, e);
            printf("%d", e);
        }
    }
    
    • 行编辑器
    void LineEdit()
    {
        InitStack();
        ch = getchar();
        while(ch != EOF)
        {
            while(ch != EOF && ch != '\n')
            {
                switch(ch)
                {
                    case '#': Pop(S, c);
                    break;
                    case '@': ClearStack(S);
                    break;
                    default: Push(S, ch);
                }
                ch = getchar();
            }//行结束
            //将栈内所有字符传送至用户数据区
            ClearStack(S);
            if(ch != EOF)
              ch = getchar();
        }//编辑结束
        DestroyStack(S);
    }
    
    • 检测括号匹配
    int allBrackets_test (char* str){
        InitStack(S);
        for(p = str; *p; p++){
            if(*p == '(' || *p == '[' || *p == '{')
              push(S, *p);
            else if(*p == ')' || *p == ']' || *p == '}'){
                if(StackEmpty(S)) return FALSE;
                pop(S, c);
                if(*p == ')' && c != '(') return FALSE;
                if(*p == ']' && c != '[') return FALSE;
                if(*p == '}' && c != '{') return FALSE;
            }
        }
        if(!StackEmpty(S))    return FALSE;
        return TRUE;
    }
    
    • 表达式求值
//输入的表达式要以'#'结尾,如‘5+6*3/(3-1)#’
 //opter为运算符栈
 //opval为操作数栈
 
int getIndex(char theta)   //获取theta所对应的索引
{
	int index = 0;
	switch (theta)
	{
	case '+':
		index = 0;
		break;
	case '-':
		index = 1;
		break;
	case '*':
		index = 2;
		break;
	case '/':
		index = 3;
		break;
	case '(':
		index = 4;
		break;
	case ')':
		index = 5;
		break;
	case '#':
		index = 6;
	default:break;
	}
	return index;
}
 
char getPriority(char theta1, char theta2)   //获取theta1与theta2之间的优先级
{
	const char priority[][7] =     //算符间的优先级关系
	{
		{ '>','>','<','<','<','>','>' },
		{ '>','>','<','<','<','>','>' },
		{ '>','>','>','>','<','>','>' },
		{ '>','>','>','>','<','>','>' },
		{ '<','<','<','<','<','=','0' },
		{ '>','>','>','>','0','>','>' },
		{ '<','<','<','<','<','0','=' },
	};
 
	int index1 = getIndex(theta1);
	int index2 = getIndex(theta2);
	return priority[index1][index2];
}
 
double calculate(double b, char theta, double a)   //计算b theta a
{
	switch (theta)
	{
	case '+':
		return b + a;
	case '-':
		return b - a;
	case '*':
		return b * a;
	case '/':
		return b / a;
	default:
		break;
	}
}
 
double getAnswer()   //表达式求值
{
	opter.push('#');      //首先将'#'入栈opter
	int counter = 0;      //添加变量counter表示有多少个数字相继入栈,实现多位数的四则运算
	char c = getchar();
	while (c != '#' || opter.top() != '#')   //终止条件
	{
		if (isdigit(c))   //如果c在'0'~'9'之间
		{
			if (counter == 1)   //counter==1表示上一字符也是数字,所以要合并,比如12*12,要算12,而不是单独的1和2
			{
				double t = opval.top();
				opval.pop();
				opval.push(t * 10 + (c - '0'));
				counter = 1;
			}
			else
			{
				opval.push(c - '0');     //将c对应的数值入栈opval
				counter++;
			}
			c = getchar();
		}
		else
		{
			counter = 0;   //counter置零
			switch (getPriority(opter.top(), c))   //获取运算符栈opter栈顶元素与c之间的优先级,用'>','<','='表示
			{
			case '<':               //<则将c入栈opter
				opter.push(c);
				c = getchar();
				break;
			case '=':               //=将opter栈顶元素弹出,用于括号的处理
				opter.pop();
				c = getchar();
				break;
			case '>':               //>则计算
				char theta = opter.top();
				opter.pop();
				double a = opval.top();
				opval.pop();
				double b = opval.top();
				opval.pop();
				opval.push(calculate(b, theta, a));
			}
		}
	}
	return opval.top();   //返回opval栈顶元素的值
}
  • 函数调用与递归
    • 函数调用
      • 保存主调用函数的运行状态和返回地址,将其栈帧入栈内;包括将被调函数实参“原件”放在栈帧中入栈,制作副本以便顶替形参来参加被调函数的运行;同时将各寄存器内容也放在栈帧中入栈。
      • 执行被调用函数的函数体和语句
      • 将各寄存器内容弹出栈,恢复主调用函数的程序的运行状态,从而释放占空间
      • 按照返回地址将控制权交还给主调用函数
    • 特例;递归
      • 一个对象部分地包含它自己,或用它自己给自己定义,则称这个对象是递归的。
      • 一个函数直接或间接的调用自身,则称这各函数是递归的。
      • 各种不同类型的递归
        • 定义是递归的(阶乘函数)
        • 数据结构是递归的(单链表)
        • 算法是递归的(汉诺塔)
      • 递归求解所要具备的三个条件
        • 将一个问题转变成一个新问题,而新问题与原问题的解法相同或类似,所不同的仅是所处理的对象,且这些处理对象的变化是有规律的
        • 可以通过上述转化使问题逐步简单化
        • 必须有一个明确的递归出口(递归的边界)
      • 优点和缺点
        • 优点:递归方法是程序设计的重要方法,它使得程序的结构清晰明了,形式简洁,易于阅读,正确性容易证明
        • 缺点:通常性能较非递归算法差

三、队列

  • 定义与特点
    • 只允许在表的一端进行插入,而在另一端删除元素,在队列中,允许插入的一端叫队尾,允许删除的叫队头
    • FIFO
  • 队列的ADT
ADT Queue{
    数据对象
    数据关系
    基本操作
    InitQueue(Queue &Q);
    DestroyQueue(&Q);
    ClearQueue(&Q);
    QueueEmpty(Q);
    QueueLength(Q);
    GetHead(Q, &e);
    EnQueue(&Q, e);
    DeQueue(&Q, &e);
    QueueTraverse(S, visit());
}
  • 链式队列
    • 无头链表
  • 顺序队列
    • 插入新的队尾元素,尾指针增1,rear = rear + 1;删除对头元素,头指针增1,front = front + 1
    • 非空队列,头指针指向队列头元素,尾指针指向队列尾元素的下一个位置
    • 假溢出
  • 循环队列
    • 队头队尾指针加1,可用取模运算实现
    • 队头指针进1:front = (front + 1) % maxsize
    • 队尾指针进1:rear = (rear + 1)%maxsize
    • 队列初始化: front = rear = 0;
    • 队空条件:front == rear
    • 队满条件:(rear + 1) % maxsize = front
  • 代码参考01-线性结构,链表的头删,尾插就是队列,循环队列参考循环链表
  • 队列的应用
    • 离散事件模拟
      • 超市收银台模拟程序,银行业务模拟程序
      • 模拟“等待-服务”这类业务的活动
      • 事前驱动模拟:时间从0开始,每个时刻以p_a的概率生成一个到达事件;第y个服务窗口以p_y的该概率生成一个离开事件,客户应如何排队,才能降低逗留时间。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值