• DS博客作业02--栈和队列


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

    0.PTA得分截图

    1.本周学习总结

    1.1 栈

    ★栈中的数据元素遵守”后进先出(先进后出),时进时出”的原则,
    栈顶与栈底:允许元素插入与删除的一端称为栈顶,另一端称为栈底(只能在栈顶进行插入和删除操作哦!)

    顺序栈的结构、操作函数


    顺序栈结构定义

    #define MAXSIZE 10
    struct StackNode {
        int data[MAXSIZE];
        int top;
    };
    

    顺序栈操作函数

    // 创建空栈
    StackNode* createStack() {
        StackNode* stack = (StackNode*)malloc(sizeof(StackNode));
        if (stack == NULL) {
            cout << "Memory allocate failed." << endl;
            return NULL;
        }
        for (int i = 0; i < MAXSIZE; i++) {
            stack->data[i] = 0;
        }
        stack->top = -1;
        return stack;
    }
    // 入栈 
    void Push(StackNode* stack, int item) {
        if (stack == NULL) {
            cout << "The stack is not created." << endl;
            return;
        }
        if (stack->top == MAXSIZE - 1) {
            cout << "The stack is full." << endl;
            return;
        }
        else {
            stack->data[++(stack->top)] = item;
            return;
        }
    }
    //出栈,并返回出栈数据
    int Pop(StackNode* stack) {
        if (stack == NULL) {
            cout << "The stack is not created." << endl;
            return 0;
        }
        if (stack->top == -1) {
            cout << "The stack is empty." << endl;
            return 0;
        }
        else {
            return (stack->data[(stack->top)--]);
        }
    }
     // 获取栈元素个数
    int getStackLength(StackNode* stack) {
        if (stack == NULL) {
            cout << "The stack is not created." << endl;
            return -1;
        }
        return (stack->top + 1);
    }
    

    链栈的结构、操作函数

    ★与顺序栈相比,链栈的结点空间可以动态申请,因此,不存在栈满上溢的情况。

    链栈的基本操作包括:

    • 链栈的初始化操作。链栈的初始化操作就是把链栈初始化为空,设栈顶指针为top,初始化时,不带头结点top==NULL;带头结点top->next ==NULL;
    • 判断链栈是否为空。判断链栈是否为空,就是判断链栈的头结点指针域是否为空,即:top->next NULL;带头结点,不带头结点的链栈栈空条件为:topNULL;
    • 进栈操作。进栈操作就是将数据元素data插入到链栈的栈顶,就是将新结点插入到链表的第一个结点之前,将新结点插入到链表中分为3个步骤p->data = data; p->next = top->next; top->next = p;p为指向新结点的指针;
    • 出栈操作。出栈操作就是将栈的第一个结点删除,并将结点元素赋值给data,在元素出栈前要判断栈是是否为空;
    • 取栈顶元素。取栈顶元素就是把栈顶的元素取出,并返回。在去栈顶元素之前,同样要判断栈是否为空;
    • 求链栈的长度。求链表的长度就是返回链栈中的元素个数,从栈顶指针开始,通过指针域找到下一个结点,并使用变量计数,直到栈底为止;
    • 销毁链栈操作。链栈的空间是动态申请的,在程序结束时要把这些结点空间通过free函数释放;
    • 打印栈中元素。打印栈中元素即:将栈中元素输出。

    链栈结构定义

    typedef int DataType;
    
    typedef struct Node
    {
        DataType data;
        struct Node* next;
    }LStackNode,*LinkStack;
    

    链栈操作函数

    //链栈的初始化
    
    void InitStack(LinkStack* top)
    {
        if ((*top = (LinkStack)malloc(sizeof(LStackNode))) == NULL)//为头结点开辟一个存储空间
        {
            exit(-1);
        }
        (*top)->next = NULL; //将链栈的头结点指针域置为空
    }
    
    //判断链栈是否为空
    int StackEmpty(LinkStack top)
    {
        if (top->next==NULL)       
        {
            return 1;
        }
        return 0;
    }
    
    //进栈操作
    
    void PushStack(LinkStack top, DataType data)
    {
        LStackNode*  p;
        p = (LStackNode*)(malloc(sizeof(LStackNode))); 
        if (p == NULL)
        {
            printf("内存分配失败!
    ");
        }
        else
        {
            p->data = data;
            p->next = top->next;
            top->next = p;
        }
    }
    
    //出栈操作
    
    void PopStack(LinkStack top,DataType* data)
    {
        LStackNode* p;
        p = top->next;
        if (p==NULL)
        {
            printf("栈为空!
    ");
        }
        else
        {
            top->next = p->next;
            *data = p->data;
            free(p);   //释放p指向的结点
        }
    }
    
    //取栈顶元素
    
    int GetTop(LinkStack top, DataType *data)
    {
        LStackNode* p;
        p = top->next;
        if (StackEmpty(top))
        {
            printf("栈为空!
    ");
        }
        else
        {
            *data = p->data;
        }
        return *data;
    }
    
    //求表长操作
    
    int StackLength(LinkStack top)
    {
        int count = 0;
        LStackNode *p;
        p = top;
        while (p->next != NULL)
        {
            count++;
            p = p->next;
        }
        return count;
    }
    
    //销毁链栈
    void DestoryStack(LinkStack top)
    {
        LStackNode *p;
        LStackNode *q;
        p = top;
        while (!p)
        {
            q = p;
            p = p->next;
            free(q);
        }
    
    }
    
    //打印栈中元素
    void StackPrint(LinkStack top)
    {
        LStackNode* p;
        if (StackEmpty(top))
        {
            printf("栈为空!
    ");
        }
        printf("栈中元素为:
    ");
        p = top;
        while (p->next != NULL)
        {
            p = p->next;
            printf("%-3d", p->data);
    
        }
    
        printf("
    ");
    }
    

    1.2 栈的应用

    表达式

    表达式求和:

    
    #include <iostream>
    #include <stack>
    #include <stdio.h>
    #include <string.h>
     
    using namespace std;
     
    //符号数组
    char symbol[7] = {'+', '-', '*', '/', '(', ')', '#'};
    //栈内元素的优先级
    int in[7] = {3, 3, 5, 5, 1, 6, 0};
    //栈外元素的优先级
    int out[7] = {2, 2, 4, 4, 6, 1, 0};
     
    ///通过符号字符获取它的数组下标
    int get(char c)
    {
        switch(c)
        {
        case '+':
            return 0;
        case '-':
            return 1;
        case '*':
            return 2;
        case  '/':
            return 3;
        case '(':
            return 4;
        case ')':
            return 5;
        case '#':
            return 6;
        default:
            return 6;
        }
    }
     
    ///比较栈内运算符c1和栈外运算符c2的优先级
    char precede(char c1, char c2)
    {
        int i1 = get(c1);
        int i2 = get(c2);
     
        if(in[i1] > out[i2])
        {
            return '>';
        }
        else if(in[i1] < out[i2])
        {
            return '<';
        }
        else
        {
            return '=';
        }
    }
     
     
    ///计算基本表达式的值
    int figure(int a, int theta, int b)
    {
        switch(theta)
        {
        case 0:
            return a + b;
        case 1:
            return a - b;
        case 2:
            return a * b;
        default:
            return a / b;
        }
    }
     
    ///计算表达式的值
    int EvaluateExpression(const char *exp)
    {
        stack<int> data; //数据栈
        stack<int> oper; //符号栈
     
        oper.push(get('#'));
        int sum = 0;
        int flag = 1; //表示正负号 1,表示正 0,表示负
        int a, theta, b;
     
        if(!('-' == *exp || '(' == *exp || isdigit(*exp)))
        {
            cout << "表达式出错1" << endl;
            return -1;
        }
     
        if('-' == *exp)
        {
            flag = 0;
            exp++;//指向下一个字符
        }
     
        int index = oper.top();
        while(*exp || symbol[index] != '#')
        {
            if(isdigit(*exp))
            {
                sum = 0;
                if(flag)
                {
                    while(isdigit(*exp))
                    {
                        sum = sum * 10 + *exp - '0';
                        exp++;
                    }
                }
                else
                {
                    while(isdigit(*exp))
                    {
                        sum = sum * 10 - *exp - '0';
                        exp++;
                    }
                }
                data.push(sum);
                flag = 1;
            }
            else
            {
                switch(precede(symbol[oper.top()], *exp))
                {
                case '>' :
                    b = data.top();
                    data.pop();
                    a = data.top();
                    data.pop();
                    theta = oper.top();
                    oper.pop();
                    data.push(figure(a, theta, b));
                    break;
                case '<' :
                    oper.push(get(*exp));
                    if(*exp)
                    {
                        exp++;
                    }
                    break;
                case '=' :
                    oper.pop();
                    if(*exp)
                    {
                        exp++;
                    }
                    break;
                }
            }
            index = oper.top();
        }
        return data.top();
    }
     
    int main()
    {
        char s[105];
        ///只能算整形,浮点会错
        printf("请务必保证输入的表达式正确的,时间有限,本人未加表达式查重功能
    例如:(2+4)/2+(3-1)
    ");
        scanf("%s",s);
        cout << EvaluateExpression(s) << endl;
        return 0;
    }
    

    1.3 队列

    队列是一种先进先出的线性表,是一种常用的数据结构。
    它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作

    顺序队列的结构、操作函数

    // 定义队列
    typedef struct {
    	int data[MaxSize];  //定义数组,存放队列元素
    	int front, rear;  // 队头和对尾指针(存放数组下标)
    }SeqQueue;
    //初始化
    void InitSeqQueue(SeqQueue& Q) {
    	Q.front = Q.rear = 0;
    }
    //入队
    void EnSeqQueue(SeqQueue& Q, int x) {
    	if (Q.rear == MaxSize)
    	{
    		cout << "队列已满,不能入队";
    	}
    	else {
    		// 先插入值到队尾,再将rear+1
    		Q.data[Q.rear] = x;
    		Q.rear++;
    	}
    }
    //出队
    void DeSeqQueue(SeqQueue& Q, int& x) {
    	if (Q.front == Q.rear) {
    		cout << "队列已空,不能出队";
    	}
    	else {
    		// 先取出队头元素,再将front+1
    		x = Q.data[Q.front];
    		Q.front++;
    	}
    }
    

    环形队列的结构、操作函数

    //初始化
    typedef int Elemtype;
    typedef struct
    {
    	Elemtype data[MAXN];
    	int front, rear;
    }Cseque;
    void Init(Cseque &q)
    {
    	q.front = q.rear = 0;
    }
    int EnQueue(Cseque &q,Elemtype x)/* 进队操作, 返回1:队满 */
    {
    	if((q.rear+1)%MAXN==q.front)
    	{
    		cout << "队满" << endl; return 0;
    	}
    	else
    	{
    		q.rear = (q.rear + 1) % MAXN;
    		q.data[q.rear] = x; 
    		return 1;
    	}
    }
    int DeQueue(Cseque &q, Elemtype *x)/* 出队操作 返回1:队空 */
    {
    	if(q.front==q.rear)
    	{
    		cout << "队空" << endl; return 0;
    	}
    	else
    	{
    		q.front = (q.front + 1) % MAXN;
    		*x = q.data[q.front ];
    		return 1;
    	}
    }
    void OutputQueue(Cseque &q)	/* 输出队列中元素 */
    {
    	if (q.front==q.rear)  cout << "队空" << endl;
    	int i = q.front + 1;
    	while (i<=q.rear)
    	{
    		cout << q.data[i] << "    ";
    		i++;
    	}
    	cout << endl;
    }
    

    链队列的结构、操作函数

    typedef int Elemtype;
    //数据结点
    typedef struct node
    {		/* 定义队列结构 */
    	int data;		/* 队列元素类型为int */
    	struct node *next;
    }QNode;
    //链队结点
    typedef struct qptr
    {
    	QNode *front;
    	QNode *rear;
    }LQueue;
    void EnQueue(LQueue *q,Elemtype x)	/* 进队操作 */
    {
    	QNode *p;
    	p =(QNode*)malloc(sizeof(QNode));
    	p->data = x;  p->next = NULL;
    	q->rear->next = p;
    	q->rear = p;
    
    }
    int EmptyQue(LQueue *q)//队列是否为空
    {
    	if (q->front == q->rear) return 0;
    	else return 1;
    }
    int DeQueue(LQueue *q, Elemtype *x)	/* 出队操作 1:对空 */
    {
    	QNode *p;
    	if(!EmptyQue(q))
    	{
    		cout << "队空" << endl; return 0;
    	}
    	else
    	{
    		p = q->front->next;
    		q->front->next = p->next;
    		*x = p->data; free(p);
    		if (q->front->next == NULL)
    			q->rear = q->front;     //队中只有一个元素的情况下,出队后修改队尾指针
    		return 1;
    	}
    }
    
    void OutputQueue(LQueue *q)		/* 输出队列中元素 */
    {
    	QNode *p;
    	p = q->front->next;
    	if (!EmptyQue(q))   cout << "队空";
    	while (p)
    	{
    		cout << p->data<< "     ";
    		p = p->next;
    	}
    	cout << endl;
    }
    
    void  GetBack(LQueue *q)//获取对尾元素
    {
    	cout <<"队尾元素为:"<< q->rear->data;
    	cout << endl;
    }
    void  GetHead(LQueue *q)//获取对头元素
    {
    	cout << "队头元素为:" << q->front->next->data;
    	cout << endl;
    }
    

    队列应用

    舞伴问题

    int QueueLen(SqQueue Q)//队列长度
    {
        return Q->rear - Q->front;//初始化时front和rear都在0处
    }
    int EnQueue(SqQueue& Q, Person e)//加入队列 
    {
        if (Q->rear + 1 == MAXQSIZE)//队满
        {
            return false;
        }
        Q->rear++;
        Q->data[Q->rear] = e;
        //cout << e.name<<e.sex<<endl;
        return true;
    }
    int QueueEmpty(SqQueue& Q)//队列是否为空 
    {
        return (Q->rear == Q->front);
    }
    int DeQueue(SqQueue& Q, Person& e)//出队列 
    {
        if (Q->front == Q->rear)
        {
            return false;
        }
        /*以下两句的位置不可调换*/
        Q->front++;
        e = Q->data[Q->front];
        return true;
    }
    void DancePartner(Person dancer[], int num) //配对舞伴 
    {
        int i;
        Person e;
        for (i = 0; i < num; i++)
        {
            e = dancer[i];
            if (e.sex == 'F')
            {
                EnQueue(Fdancers, e);
            }
            if (e.sex == 'M')
            {
                EnQueue(Mdancers, e);
            }
        }
        while (!QueueEmpty(Fdancers) && !QueueEmpty(Mdancers))
        {
            DeQueue(Fdancers, e);
            cout << e.name;
            DeQueue(Mdancers, e);
            cout <<' '<<' '<<e.name;
            cout << endl;
        }
        
    }
    

    2.PTA实验作业

    2.1 符号配对

    2.1.1 解题思路及伪代码

    基本思路就是一个一个字符处理,发现一个要处理的左符号(/,[,(,{)就入栈,发现一个需要处理的右符号(/,],),})就与栈顶对比,如果是对应的左符号,那么把栈顶弹出,否则说明这个右符号不能配对,输出NO等信息并退出。如果一直处理到程序结尾都没有退出就是YES。

    定义字符串 c 存放待匹配符号
    定义字符栈 s 做匹配处理
    定义 i,k 控制循环
    将字符串输入并存入a,逐个判断a中的字符是否为
    while(1){
            if 输入的是结束标志
                then 结束循环
            输入字符串a
            for(i=0;i<a.size();i++) 
                if a[i]是符号
                    then 存入b
                else if a[i]是/*
                    then 将<存入b且i+1
                else if a[i]是*/
                    将>存入b且i+1
            end
    定义 n=k;
    for i=0 to nif b[i]等于[、{、(或<
            then 将b[i]入栈
        else if 栈不为空
            if 栈顶元素与此时的b[i]匹配
                then 将栈顶出栈
            else
                将b[i]入栈
        else
            将b[i]入栈
    end for
    
    if 栈空
        then输出YES
    else{
        输出NO
        重置k=0
        while 栈不空
            将栈中元素逐个放入c中
            将c数组中的元素逐个比较
        for i=0 to k-1
    for j=i+1 to kif 匹配
                    then c[i]c[j]都置为0
        for i=0 to kif c[i]等于(、[、{
                then输出c[i]-?
                结束循环
            else if c[i]等于'<'
                then输出/*-?
                结束循环
            else if c[i]等于'>'
                then输出?-*/
                结束循环
            else if c[i]等于)、]、}
                then输出?-c[i]
                结束循环
    end  
    

    2.1.2 总结解题所用的知识点

    字符栈的运用

    2.2 银行业务队列简单模拟


    2.2.1 解题思路及伪代码

    for i=0 to n-1
        if 客户编号是奇数
           then 入A队
           else 入B队
    end
    for i=0 to n-1
          if A队非空
             then 取出2个A队元素并输出
          if B队非空
              then 取出1个B队元素并输出
    end
    

    2.2.2 总结解题所用的知识点

    queue用法

    3.阅读代码

    3.1 题目及解题代码


    class MyStack {
    public:
        queue<int> queue1;
        queue<int> queue2;
    
        /** Initialize your data structure here. */
        MyStack() {
    
        }
    
        /** Push element x onto stack. */
        void push(int x) {
            queue2.push(x);
            while (!queue1.empty()) {
                queue2.push(queue1.front());
                queue1.pop();
            }
            swap(queue1, queue2);
        }
        
        /** Removes the element on top of the stack and returns that element. */
        int pop() {
            int r = queue1.front();
            queue1.pop();
            return r;
        }
        
        /** Get the top element. */
        int top() {
            int r = queue1.front();
            return r;
        }
        
        /** Returns whether the stack is empty. */
        bool empty() {
            return queue1.empty();
        }
    };
    

    3.2 该题的设计思路

    为了满足栈的特性,即最后入栈的元素最先出栈,在使用队列实现栈时,应满足队列前端的元素是最后入栈的元素。可以使用两个队列实现栈的操作,其中queue1
    用于存储栈内的元素queue2作为入栈操作的辅助队列。
    入栈操作时,首先将元素入队到queue2,然后将queue1的全部元素依次出队并入队到queue2,此时queue2的前端的元素即为新入栈的元素,再将queue1和queue2
    互换,则queue1的元素即为栈内的元素,queue1的前端和后端分别对应栈顶和栈底。
    由于每次入栈操作都确保queue1的前端元素为栈顶元素,因此出栈操作和获得栈顶元素操作都可以简单实现。出栈操作只需要移除queue1的前端元素并返回即可,获得栈顶元素操作只需要获得queue1的前端元素并返回即可(不移除元素)。
    由于queue1用于存储栈内的元素,判断栈是否为空时,只需要判断queue1是否为空即可。

    • 时间复杂度:入栈操作 O(n),其余操作都是 O(1)。

    入栈操作需要将queue1 中的 n 个元素出队,并入队 n+1 个元素到queue2 ,共有 2n+1次操作,每次出队和入队操作的时间复杂度都是 O(1),因此入栈操作的时间复杂度是O(n)。
    出栈操作对应将queue1的前端元素出队,时间复杂度是 O(1)。
    获得栈顶元素操作对应获得queue1的前端元素,时间复杂度是O(1)。
    判断栈是否为空操作只需要判断queue1是否为空,时间复杂度是 O(1)。

    • 空间复杂度:O(n),其中n是栈内的元素。需要使用两个队列存储栈内的元素。

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

    1.题目较灵活,一个队列或者两个队列的方法都能做
    2.使用两个队列的方法较复杂容易出错
    3.题目涉及到栈和队列两种数据结构,需掌握好两种结构才能更好做题。
    栈是一种后进先出的数据结构,元素从顶端入栈,然后从顶端出栈。
    队列是一种先进先出的数据结构,元素从后端入队,然后从前端出队。

  • 相关阅读:
    Telnet远程测试
    数据库笔记
    gcc 链接不到 函数实现, undefined reference to xxx
    usb2ttl 引脚定义
    ip v4 地址中 局域网地址范围
    vdi 磁盘文件转换为 vmdk文件的命令
    tftp 命令使用
    无法通过vnc连接到局域网内的树莓派
    镜像服务网站
    C语言 scanf 输入浮点数的用法
  • 原文地址:https://www.cnblogs.com/CHINATYY/p/14619867.html
Copyright © 2020-2023  润新知