zoukankan      html  css  js  c++  java
  • 数据结构-栈,队列

    这个作业属于哪个班级 数据结构--网络2011/2012
    这个作业的地址 DS博客作业02--栈和队列
    这个作业的目标 学习栈和队列的结构设计及运算操作
    姓名 付峻霖

    0.PTA得分截图

    1.本周学习总结

    1.1 栈

    栈结构的定义和特点

    • 栈的顺序存储是由数组来实现的
    • 只允许在栈顶进行插入删除操作,另一端为栈底
    • 栈是后进先出的线性表
    • 线性表的表尾是栈顶,而不是栈底
    • 无论是进栈还是出栈,均在栈顶操作,栈底是固定的

    栈的抽象数据类型

    ADT 栈(stack)
    Data
    	同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
    Operation
    	InitStack(*S):初始化操作,建立一个空栈S。
    	DestroyStack(*S):若栈存在,则销毁它。
    	ClearStack(*S):将栈清空。
    	StackEmpty(S):若栈为空,返回true,否则返回false。
    	GetTop(S,*e):若栈存在且非空,用e返回S的栈顶元素。
    	Push(*S,e):若栈S存在,插入新元素e到栈S中并成为栈顶元素
    	Pop(*S,*e):删除栈S中栈顶元素,并用e返回其值。
    	StackLength(S):返回栈S的元素个数
    endADT
    

    Q:什么时候函数操作要用‘*’号?
    A:调用函数过后,内容有被修改就要用‘*’

    1.1.1 顺序栈

    顺序栈的图形

    顺序栈的结构定义

    typedef int SElemType;
    
    /*顺序栈结构*/
    typedef struct
    {
    	SElemType data[MAXSIZE];
    	int top;        /*用于栈顶指针*/
    }SqStack;
    

    顺序栈的基本操作

    • 初始化栈
    void InitStack(SqStack* S)
    {
       S=new SqStack;      //分配一个顺序栈空间,首地址放在S中
       S->top=-1;          //栈顶指针置为-1
    }
    
    • 进栈操作
    /*插入元素e为新的栈顶元素*/
    bool Push(SqStack* S, SElemType e)
    {
    	if (S->top == MAXSIZE - 1)/*栈满*/
    		return ERROR;
    	S->top++;                 /*栈顶指针增加一*/
    	S->data[S->top] = e;      /*将新插入元素赋值给栈顶空间*/
    	return OK;
    }
    
    • 出栈操作
    /*若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR*/
    bool Pop(SqStack* S, SElemType* e)
    {
    	if (S->top == -1)      /*栈空*/
    		return ERROR;
    	*e = S->data[S->top];  /*将要删除的栈顶元素赋值给e*/
    	S->top--;              /*栈顶指针减一*/
    	return OK;
    }
    
    • 销毁栈
    void DestroyStack(SqStack* S)
    {
    	free(S);
    }
    

    1.1.2 顺序栈--两栈共享空间

    栈的顺序存储还是很方便的,在插入删除时不需要移动元素,不过它有一个缺陷,必须事先确定数组大小,这很可能造成资源浪费
    

    两栈共享空间的图形

    两栈共享空间的结构定义

    /*两栈共享空间结构*/
    typedef struct
    {
    	SElemType data[MAXSIZE];
    	int top1;  /*栈1的栈顶指针*/
    	int top2;  /*栈2的栈顶指针*/
    }SqDoubleStack;
    

    两栈共享空间的基本操作

    • 插入操作
    /*插入元素e为新的栈顶元素*/
    bool Push(SqDoubleStack* S, SElemType e, int stackNumber)
    {
    	if (S->top1 + 1 == S->top2)  /*栈已满,不能再push新元素了*/
    		return ERROR;
    	if (stackNumber == 1)        /*栈1有元素进栈*/
    		S->data[++S->top1] = e;  /*若是栈1则先top1+1后给数组元素赋值*/
    	else if (stackNumber == 2)   /*栈2有元素进栈*/
    		S->data[--S->top2] = e;  /*若是栈2则先top2-1后给数组元素赋值*/
    	return OK;
    }
    
    • 删除操作
    /*若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR*/
    bool Pop(SqDoubleStack* S, SElemType *e, int stackNumber)
    {
    	if (stackNumber == 1)
    	{
    		if (S->top1 == -1)      
    			return ERROR;        /*说明栈1已经是空栈,溢出*/
    		*e = S->data[S->top1--]; /*将栈1的栈顶元素出栈*/
    	}
    	if (stackNumber == 2)
    	{
    		if (S->top2 == -1)
    			return ERROR;        /*说明栈2已经是空栈,溢出*/
    		*e = S->data[S->top2++]; /*将栈2的栈顶元素出栈*/
    	}
    	return OK;
    }
    

    1.1.3 链式栈

    链式栈的图形

    链式栈的结构定义

    /*链栈结构*/
    typedef struct StackNode
    {
    	SElemType data;        //数据域
    	struct StackNode* next;//指针域
    }StackNode,*LinkStackPtr;      //链栈结点类型
    
    typedef struct
    {
    	LinkStackPtr top;
    	int count;
    }LinkStack;
    

    链式栈的基本操作

    • 初始化栈
    void InitStack(StackNode* S)
    {
    	S = new StackNode;
    	S->next = NULL;
    }
    
    • 进栈操作
    /*插入元素e为新的栈顶元素*/
    bool Push(LinkStack* S, SElemType e)
    {
    	LinkStackPtr s = new StackNode;
    	s->data = e;
    	s->next = S->top;/*把当前的栈顶元素赋值给新结点的直接后继*/
    	S->top = s;      /*将新的结点s赋值给栈顶指针*/
    	S->count++;
    	return OK;
    }
    
    • 出栈操作
    bool Pop(LinkStack* S, SElemType* e)
    {
    	LinkStackPtr p;
    	if (StackEmpty(*S))
    		return ERROR;
    	*e = S->top->data;
    	p = S->top;            /*将栈顶结点赋值给p*/
    	S->top = S->top->next; /*使得栈顶指针下移一位,指向后一结点*/
    	free(p);               /*释放结点p*/
    	S->count--;
    	return OK;
    }
    

    1.2 栈的应用

    1.2.1 斐波那契数列的实现--兔子繁衍后代

    题目

    兔子在出生两个月后,就右繁衍能力,一对兔子每个月能生出一对小兔子来。假设所有兔子都不死,那么一年后可以繁衍多少兔子呢?
    

    题目分析
    第一个月小兔子没有繁殖能力,所以还是一对,第二个月生下一对小兔子,总共两对,第三个月,老兔子又生一对,小兔子没有繁衍能力,总共三对······

    • 数学定义
      F(n)=F(n-1)+F(n-2)

    代码实现

    /*斐波那契的递归函数*/
    int Fbi(int i)
    {
    	if (i < 2) /*0月零对兔子,1月一对兔子*/
    		return i == 0 ? 0 : 1; //特殊处理
    	return Fbi(i - 1) + Fbi(i - 2);
    }
    int main()
    {
    	int i;
    	printf("递归显示斐波那契数列:
    ");
    	for (i = 0; i <= 12; i++)  //打印每个月 繁殖的小兔子对数
    		printf("%d", Fbi(i));
    	return 0;
    }
    

    1.2.2 十进制数转八进制数

    十进制数转八进制数码云
    输入样例与输出样例

    1.2.3 逆波兰计算结果

    后缀表达式计算结果码云
    输入样例与输出样例

    1.3 队列

    队列结构的定义和特点

    • 队列是一种先进先出的线性表
    • 允许插入的一端称为队尾,允许删除的一端称为队头
    • 插入数据只能再队尾进行,删除数据只能在队头进行

    队列的抽象数据类型

    ADT 队列(Queue)
    Data
    	同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
    Operation
    	InitQueue(*Q):初始化操作,建立一个空队列Q。
    	DestroyQueue(*Q):若队列Q存在,则销毁它。
    	ClearQueue(*Q):将队列Q清空。
    	QueueEmpty(Q):若队列Q为空,返回true,否则返回false。
    	GetHead(Q,*e):若队列Q存在且非空,用e返回队列Q的队头元素。
    	EnQueue(*Q,e):若队列Q存在,插入新元素e到队列Q中并成为队尾元素。
    	DeQueue(*Q,*e):删除队列Q中队头元素,并用e返回其值
    	QueueLenghth(Q):返回队列Q的元素个数。
    endADT
    

    Q:什么时候函数操作要用‘*’号?
    A:调用函数过后,内容有被修改就要用‘*’

    1.3.1 顺序队列

    顺序队列的图形

    顺序队列的结构定义

    typedef int QElemType;
    /*顺序队列的存储结构*/
    typedef struct
    {
    	QElemType data[MAXSIZE];
    	int front;         /*头指针*/
    	int rear;          /*尾指针,若队列不空,指向队列尾元素的下一个位置*/
    }SqQueue;
    

    顺序队列的基本操作

    • 初始化队列
    /*初始化一个空队列Q*/
    void InitQueue(Queue& Q)
    {
        Q = new Queue;//动态申请内存
        Q->front = 0;
        Q->rear = 0;
    }
    
    • 入队操作
    /*若队列未满,则插入元素e为Q新的队尾元素*/
    bool EnQueue(SqQueue* Q, QElemType e)
    {
        if (Q->rear + 1 == MAXSIZE)/*队列满的判断*/
            return ERROR;
        Q->data[Q->rear] = e;      /*将元素e赋值给队尾*/
        Q->rear = Q->rear + 1;     /*rear指针向后移一位置*/
        return OK;
    }
    
    • 出队操作
    /*若队列不空,则删除Q中队头元素,用e返回其值*/
    bool DeQueue(SqQueue* q, Elemtype* e)
    {
        if (Q->front == Q->rear)       /*队列空的判断*/
            return ERROR;
        *e = Q->data[Q->front];        /*将队头元素赋值给e*/
        Q->front = Q->front + 1;       /*front指针向后移一位置*/
        return OK;
    }
    

    1.3.2 循环队列

    循环队列的图形

    循环队列的结构定义

    typedef int QElemType;
    /*循环队列的顺序存储结构*/
    typedef struct
    {
    	QElemType data[MAXSIZE];
    	int front;         /*头指针*/
    	int rear;          /*尾指针,若队列不空,指向队列尾元素的下一个位置*/
    }SqQueue;
    

    循环队列的基本操作

    • 初始化队列
    /*初始化一个空队列Q*/
    bool InitQueue(Queue& Q)
    {
        Q->front = 0;
        Q->rear = 0;
        return OK;
    }
    
    • 入队操作
    /*若队列未满,则插入元素e为Q新的队尾元素*/
    bool EnQueue(SqQueue* Q, QElemType e)
    {
        if ((Q->rear + 1)%MAXSIZE == Q->front)/*队列满的判断*/
            return ERROR;
        Q->data[Q->rear] = e;      /*将元素e赋值给队尾*/
        Q->rear = (Q->rear + 1) % MAXSIZE;     /*rear指针向后移一位置*/
                                               /*若到最后则转到数组头部*/
        return OK;
    }
    
    • 出队操作
    /*若队列不空,则删除Q中队头元素,用e返回其值*/
    bool DeQueue(SqQueue* q, Elemtype* e)
    {
        if (Q->front == Q->rear)                   /*队列空的判断*/
            return ERROR;
        *e = Q->data[Q->front];                    /*将队头元素赋值给e*/
        Q->front = (Q->front + 1) % MAXSIZE;       /*front指针向后移一位置*/
                                                   /*若到最后则转到数组头部*/
        return OK;
    }
    
    • 求队列长度
    /*返回Q的元素个数,也就是队列的当前长度*/
    int QueueLength(SqQueue Q)
    {
        return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
    }
    

    1.3.3 链式队列

    链式队列的图形

    链式队列的结构定义

    typedef int QElemType;
    
    typedef struct QNode      /*结点结构*/
    {
    	QElemType data;
    	struct QNode* next;
    }QNode, * QueuePtr;
    
    typedef struct            /*队列的链表结构*/
    {
    	QueuePtr front, rear; /*队头、队尾指针*/
    }LinkQueue;
    

    链式队列的基本操作

    • 入队操作
    /*插入元素e为Q的新的队尾元素*/
    bool EnQueue(LinkQueue* Q, QElemType e)
    {
    	QueuePtr s = new QNode;
    	if (!s)            /*存储分配失败*/
    		exit(OVERFLOW);
    	s->data = e;
    	s->next = NULL;
    	/*尾插法*/
    	Q->rear->next = s;/*把拥有元素e的新结点s赋值给原队尾结点的后继*/
    	Q->rear = s;      /*把当前的s设置为队尾结点,rear指向s*/
    	return OK;
    }
    
    • 出队操作
    /*若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR*/
    bool DeQueue(LinkQueue* Q, QElemType* e)
    {
    	QueuePtr p;
    	if (Q->front == Q->rear)
    		return ERROR;
    	p = Q->front->next;        /*将要删除的队头结点暂存给p*/
    	*e = p->data;              /*将要删除的队头结点的值赋值给e*/
    	Q->front->next = p->next;  /*将原队头结点的后继p->next赋值给头结点后继*/
    	if (Q->rear == p)             /*若队头就是队尾,则删除后将rear指向头结点*/
    		Q->rear = Q->front;
    	free(p);
    	return OK;
    }
    

    队列应用

    1.3.1 6-3 jmu-ds-舞伴问题 (20 分)

    题目

    假设在周末舞会上,男士和女士们分别进入舞厅,各自排成一队。跳舞开始,依次从男队和女队队头各出一人配成舞伴,若两队初始人数不同,则较长那一队未配对者等待下一轮舞曲。现要求写一算法模拟上述舞伴配对问题。
    

    思路

    1. 创建俩队列分别放男士和女士
    2. 俩队列同时弹出一个人,组成舞伴

    核心代码

    /*将Person里的人 分别放到Mdancers,Fdancers两个队列中*/
    void DancePartner(Person dancer[], int num)//舞蹈合作伙伴
    {
    	for (int i = 0; i < num; i++)
    	{
    		if (dancer[i].sex == 'M')
    		{  //男生在男栈
    			EnQueue(Mdancers, dancer[i]);
    		}
    		else
    		{  //女生在女栈
    			EnQueue(Fdancers, dancer[i]);
    		}
    	}
    	while (QueueEmpty(Mdancers) != 1 && QueueEmpty(Fdancers) != 1)
    	{   //当  两个栈都不为空
    		Person x, y;
    		DeQueue(Mdancers, x);//弹出一个男生
    		DeQueue(Fdancers, y);//弹出一个女生
    		cout << y.name << " " << x.name << endl;
    	}
    }
    

    2.PTA实验作业(4分)

    符号配对码云地址
    银行业务码云地址

    2.1 符号配对

    2.1.1 解题思路及伪代码

    解题思路

    1. 遍历字符串,把括号之外的东西全部忽略
    2. 只要是左符号就入栈。
    3. 只要当前符号与栈顶符号配对成功,就出栈
    4. 最后进行条件判断,flag的状态栈是否为空

    伪代码

    for (遍历字符串)
    {
    	if (是左括号)
    		入栈
    	else if (栈空且当前位置为右符号)//配对失败
    		flag = 1;
    		break; 结束
    	else if (栈顶元素与当前位置符号刚好配对)//配对成功
    		栈顶元素出栈
    }
    if (flag == 1)
    	右符号剩余
    else if (flag == 0 且 栈空)
    	无剩余,完全配对成功
    else 栈不为空
    	左符号剩余
    return 0;
    

    2.1.2 总结解题所用的知识点

    1. stack模板各类函数pop,push,empty的应用
    2. 分类讨论思想,当它是左符号时入栈,为右符号时出栈,还要考虑特殊情况
    3. 巧用flag与栈的状态表示配对结果

    2.2 银行业务队列简单模拟

    2.2.1 解题思路及伪代码

    解题思路

    1. 首先把这些数字分配到A或B队列中
    2. 如果A是奇数,那就直接删除A队头
    3. 如果A是偶数,那就A先走,B再走

    伪代码

    /*让所有顾客进入队列*/
    for (顾客总数)
    	输入每个数字
    	if (数字为奇数)
    		入A队列
    	else 数字为偶数
    		入B队列
    
    /*头部空格单独处理*/
    if (!A.empty())
    	打印A队头
    	删除A队头
    	i = 1;//处理完的顾客人数
    else
    	打印B队头
    	删除B队头
    
    /*正常处理*/
    while (A不为空 或 B不为空)
    	i++;    //处理完的顾客人数+1
    	if (奇数)
    		if (A队列不为空)
    			打印A队头
    			删除A队头
    	else 偶数
    		/*A先删除,B再删除*/
    		if (A队列不为空)
    			打印A队头
    			删除A队头
    		if (B队列不为空)
    			打印B队头
    			删除B队头
    

    2.2.2 总结解题所用的知识点

    1. queue模板各类函数pop,push,empty的应用
    2. 关于奇数偶数的处理,用i来计数奇数出A,偶数出A再出B

    3.用两个栈实现队列

    3.1 题目及解题代码

    • 题目

    • 代码

    class CQueue {
        Stack<Integer> in;
        Stack<Integer> out;
        public CQueue() {
            in = new Stack<Integer>();
            out = new Stack<Integer>();
        }
        public void appendTail(int value) {
            in.push(value);
        }
        public int deleteHead() {
            if(in.isEmpty()&&out.isEmpty()) return -1;
            if(out.isEmpty())
                while(!in.isEmpty())
                    out.push(in.pop());
            return out.pop();
        }
    }
    作者:Jokar兄
    链接:https://www.bilibili.com/video/BV1E54y127LP?t=56
    来源:哔哩哔哩(bilibili)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

    3.2 该题的设计思路及伪代码

    题意解读
    通过两个先进后出的栈,实现一个先进先出的队列
    设计思路

    1. 创建两个栈,栈in用于存储存入的元素,栈out用于存储用于弹出的元素
    2. 比如123,栈in存放完元素后栈顶为3,将栈in中的元素弹入栈out中,栈out的栈顶元素为1,再弹出栈out的123,实现了输入为123,输出也为123

    伪代码

    class CQueue {
        /*创建两个栈*/
        创建in栈 //栈in用于存储存入的元素
        创建out栈//栈out用于存储用于弹出的元素
        public CQueue() {
            为栈in开辟空间
            为栈out开辟空间
        }
        public int deleteHead() {
            if(两栈都为空) return -1;
            if(若栈out中的元素为空)
                则将栈in中的元素弹出并存入栈out中
            return 弹出元素;
        }
    }
    

    时间复杂度为O(n)
    空间复杂度为O(n)

    3.3 分析该题目解题优势及难点。

    • 需要深刻的理解栈结构先入后出,以及队列结构先入先出的原理
    • 巧妙的运用两个栈结构,将in栈元素弹出到out栈中,实现元素的逆序
  • 相关阅读:
    用python抓取百度指数 以及 用cxfreeze打包的经验
    selenium中send_keys的使用
    python之文件调用
    学习python之selenium
    学习python图像识别
    解决方案: 运行ugarchroll,报错Error in try(.C("c_qstd", p = as.double(p), mu = as.double(mu), sigma = as.double(sigma), : NA/NaN/Inf in foreign function call (arg 3)
    int 转 const char*
    均值,方差,协方差,协方差矩阵,特征值,特征向量
    OpenCv 图片上添加汉字
    OpenCV获取与设置像素点的值的几个方法
  • 原文地址:https://www.cnblogs.com/qq690775749/p/14639290.html
Copyright © 2011-2022 走看看