zoukankan      html  css  js  c++  java
  • 博客作业02--栈和队列

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

    0.PTA得分截图

    1.本周学习总结

    1.1栈

    顺序栈的结构、操作函数

    顺序栈的结构
    栈中数据元素的逻辑关系呈线性关系,所以栈可以像线性表一样采用顺序存储结构进行存储,即分配一块连续的存储空间来存放栈中元素,并用一个变量指向当前的栈顶元素以反映栈中元素的变化。采用顺序 视频讲解存储结构的栈称为顺序栈。

    typedef struct
    {
      ElemTyqe data[MaxSize];
      int top;
    }
    

    顺序栈的操作函数
    1.初始化栈initStack(&s)
    该运算创建一个空栈,由s指向它。实际上就是分配一个顺序栈空间,并将栈顶指针设置为-1。算法如下:

    void InitStack(SqStack *&s)
    {
      s=(SqStack *)malloc(sizeof(SqStack));
      s->top=-1;
    }
    

    2.销毁栈DestroyStack(&s)
    该运算释放顺序栈s占用的储存空间。算法如下:

    void DestroyStack(SqStack *&s)
    {
      free(s);
    }
    

    3.判断栈是否为空StackEmpty(s)
    该运算实际上用于判断条件s->top==-1是否成立。算法如下:

    bool StackEmpty(SqStack *s)
    {  
      return(s->top==-1);
    }
    

    4.进栈Push(&S,e)
    该运算新建一个结点,用于存放元素e(由p指向它),然后将其插入到头结点之后作为新的首结点。算法如下:

    void Push(LinkStNode *&s,ElemType e)
    {
      LinkStNode *p;
      p=(LinkStNode*)malloc(sizeof(LinkStNode));
      p->data=e;
      p->next=s->next;
      s->next=p;
    }
    

    5.出栈Pop(&s,&e)
    该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e,然后将其删除。

    bool Pop(LinkStNode*&s,ElemType &e)
    {
      LinkStNode *p;
      if(s->next==NULL)
        return false;
      p=s->next;
      e=p->data;
      s->next=p->next;
      free(p);
      return true;
    }
    

    6.取栈顶元素GetTop(s, &e)
    该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e。算法如下:

    bool GetTop(LinkStNode *s, ElemType&e)
    {
      if(s->next==NULL)
        return false;
      e=s->next->data;
      return true;
    }
    

    链栈的结构、操作函数

    链栈的结构
    栈中数据元素的逻辑关系呈线性关系,所以栈可以像线性表一样采用链式存储结构。采用链式存储结构的栈称为链栈(linked stack),链表有多种,这里采用带头结点的单链表来实现链栈。
    链栈中节点类型LinkStNode的声明如下:

    typedef struct linknode
    {
      Elem Type data;
      struct linknode *next;
    } LinkStNode;
    

    链栈的操作函数
    1.初始化栈
    该运算创建一个空链栈s。实际上是创建链栈的头结点,并将其next域置为NULL。算法如下:

    void InitStack(LinkStNode*&s)
    {
      s=(LinkStNode*)malloc(sizeof(LinkStNode));
      s->next=NULL;
    }
    

    2.销毁栈
    该运算释放链栈s占用的全部结点空间,和单链表的销毁算法完全相同。算法如下:

    viod DestroyStack(LinkStNode*&s)
    {
      LinkStNode * pre=s,*p=s->next;
      while(p!=NULL)
      {
        free(pre);
        pre=p;
        p=pre->next;
      }
      free(pre);
    }
    

    3.判断栈是否为空
    该运算判断s->next=NULL的条件是否为成立。算法如下:

    bool StackEpty(LinkStNode *s)
    {
      return(s->next==NULL);
    }
    

    4.进栈Push(&s,e)
    该运算新建一个结点,用于存放元素e(由p指向它),然后将其插入到头结点之后作为新的首结点。算法如下:

    void Push(LinkStNode *&s,ElemType e)
    {
      LinkStNode *p;
      p=(LinkStNode*)malloc(sizeof(LinkStNode));
      p->data=e;
      p->next=s->next;
      s->next=p;
    }
    

    5.出栈Pop(&s,&e)
    该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e,然后将其删除。算法如下:

    bool Pop(LinkStNode*&s,ElemType&e)
    {
      LinkStNode*p;
      if(s->next==NULL)
        return false;
      p=s->next;
      e=p->data;
      s->next=p->next;
      free(p):
      return true;
    }
    

    6.取栈顶元素
    该运算在栈不为空的条件下提取首结点的数据域赋给引用型参数e。算法如下:

    bool GetTop(Link*s,ElemType&e)
    {
      if(s->next==NULL)
        return false;
      e=s->nest->data;
      return true;
    }
    

    1.2栈的应用

    表达式的转换
    代码如下

    #include <iostream>
    #include <stack>
    #include <string>
    using namespace std;
    int main()
    {
    	stack<char>st;
    	char s[50];
    	int i = 0, n = 0;
    	char c[22];
    	char e;
    	cin >> c;
    	while (c[i]!='')//进行遍历,开始转换成后缀表达式
    	{
    		switch (c[i])
    		{
    		case'('://判定为左括号
    			st.push('(');//左括号进栈
    			i++;//继续扫描其他字符
    			break;
    		case')'://判定为右括号
    			e = st.top();//出栈元素e
    			st.pop();
    			while (e != '(')//不为‘(’时循环
    			{
    				s[n++] = e;//将e放入s中;
    				s[n++] = ' ';
    				e = st.top();//继续出栈元素e
    				st.pop();
    			}
    			i++;//继续扫描其他字符
    			break;
    		case'+'://判定为加号或减号
    		case'-':
    			while (!st.empty())//栈不空循环
    			{
    				e = st.top();//取栈顶元素e
    				if (e != '(')//e不是‘(’
    				{
    					s[n++] = e;//将e存放到s中
    					s[n++] = ' ';
    					e = st.top();//出栈元素e
    					st.pop();
    				}
    				else//e是‘(’退出循环
    					break;
    			}
    			st.push(c[i]);//将‘+’或‘-’进栈
    			i++;//继续扫描其他字符
    			break;
    		case'*'://判定为‘*’或‘/’号
    		case'/':
    			while (!st.empty())栈不空循环
    			{
    				e = st.top();//取栈顶元素e
    				if (e == '*' || e == '/')//将栈顶‘*’或‘/’运算符出栈并存放到s中
    				{
    					s[n++] = e;将e存放到s中
    					s[n++] = ' ';
    					e = st.top();//出栈元素e
    					st.pop();
    				}
    				else//e为非‘*’或‘/’运算符时退出循环
    					break;
    			}
    			st.push(c[i]);//将‘*’或‘/’进栈
    			i++;//继续扫描其他字符
    			break;
    		default://处理数字字符
    			while (c[i] >= '0' && c[i] <= '9')//判定为数字字符
    			{
    				s[n++] = c[i];
    				i++;
    			}
    			s[n++] = ' ';
    		}
    	}
    	while (!st.empty())//此时c扫描完毕,栈不空时循环
    	{
    		e = st.top();77出栈元素e
    		st.pop();
    		s[n++] = e;将e放到s中
    		s[n++] = ' ';
    
    	}
    	s[--n] = '';//给s表达式添加结束标识
    	cout << s;
    }
    

    只是简单的加减乘除,没有考虑到负数和小数。

    1.3队列

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

    顺序队列的结构
    队列中数据元素的逻辑关系呈线性关系,所以队列可以像线性表一样采用顺序存储结构进行存储,即分配一块连续的存储空间来存放队列中的元素,并用两个整型变量来反映队列中元素的变化,它们分别存储队首元素和队尾元素的下标位置,分别称为队首指针(队头指针)和队尾指针。采用顺序存储结构的队列称为顺序队(sequential queue)

    typedef struct
    {
      ElemType data[MaxSize];
      int front,rear;
    }SqQueue; 
    

    顺序队列的操作函数
    1.初始化队列
    构造一个空队列q,将front和rear指针均设置成初始状态,即一1值。算法如下:

    void InitQueue(SqQueue*&q)
    {
      q=(SqQueue*)malloc(sizeof(SqQueue));
      q->front=q->rear=-1;
    }
    

    2.销毁队列
    释放队列q占用的存储空间。算法如下:

    void DestroyQueue(SqQueue *&q)
    {
      free(q);
    }
    

    3.判断队列是否为空
    若队列q为空,返回真;否则返回假。算法如下:

    bool QueueEmpty(SqQueue *&q)
    {
      return (q->front==q->rear);
    }
    

    4.进队列
    在队列q不满的条件下先将队尾指针rear增1,然后将元素e插人到该位置。算法如下:

    bool enQueue(SqQueue*&q,ElemType e)
    {
      if(q->rear==MaxSize-1)
        return false;
      q->rear++;
      q->data[q->rear]=e;
      return true;
    }
    

    5.出队列
    在队列q不空的条件下先将队头指针front增1,并将该位置的元素值赋给e。算法如下:

    bool deQueue(SqQueue*&q,ElemType&e)
    {
      if(q->front==q->rear)
        return false;
      q->front++;
      e=q->data[q->front];
      return true;
    }
    

    环形队列的结构、操作函数
    环形队列的结构
    在环形队列q中设置队空条件是q->rear=q->front;队满条件是(q->rear 1) MaxSize=q-> front。而进队操作和出队操作改为分别将队尾rear和队头指针front循环进1.

    typedef struct
    {
      ElemType data[MaxSize];
      int front,rear;
    }SqQueue; 
    

    环形队列的操作函数
    1.初始化队列
    构造一个空队列q,将front和rear指针均设置成初始状态,即0值。算法如下:

    void InitQueue(SqQueue*&q)
    {
      q=(SqQueue*)malloc(sizeof(SqQueue));
      q->front=q->rear=0;
    }
    

    2.销毁队列
    释放队列q占用的存储空间。算法如下:

    void DestroyQueue(SqQueue *&q)
    {
      free(q);
    }
    

    若队列q为空,返回真;否则返回假。算法如下:

    bool QueueEmpty(SqQueue *&q)
    {
      return (q->front==q->rear);
    }
    

    3.判断队列是否为空
    若队列q为空,返回真;否则返回假。算法如下:

    bool QueueEmpty(SqQueue *&q)
    {
      return (q->front==q->rear);
    }
    

    4.进队列
    在队列q不满的条件下先将队尾指针rear增1,然后将元素e插人到该位置。算法如下:

    bool enQueue(SqQueue*&q,ElemType e)
    {
      if((q->rear+1)%MaxSize==q->front)
        return false;
      q->rear=(q->rear+1)%MaxSize;
      q->data[q->rear]=e;
      return true;
    }
    

    5.出队列
    在队列q不空的条件下先将队头指针front增1,并将该位置的元素值赋给e。算法如下:

    bool deQueue(SqQueue*&q,ElemType&e)
    {
      if(q->front==q->rear)
        return false;
      q->front=(q->front+1)%MaxSize;
      e=q->data[q->front];
      return true;
    }
    

    链队列的结构、操作函数
    链队列的结构

    1.初始化队列
    构造一个空队,即创建一个链队节点,其front和rear域均置为NULL.算法如下:

    void InitQueue(LinkQuNode*&q)
    {
      q=(LinkQuNode*)malloc(sizeof(LinkQuNode));
      q->front=q->rear=NULL;
    }
    

    2.销毁队列
    释放链队占用的全部存储空间,包括链队结点和所有数据结点的存储空间。算法如下:

    void DestroyQueue(LinkQuNode)
    {
      DataNode *pre=q->front,*p;
      if(pre!=NULL)
      {
        p=pre->next;
        while(p!=NULL)
        {
          free(pre);
          pre-p;
          p=p->next;
        }
        free(pre);
      }
      free(q)
    }
    

    3.判断队列是否为空
    若队列为空,返回真;否则返回假。算法如下:

    bool QueueEmpty(LinkQuNode*q)
    {
      return(q->rear==NULL);
    }
    

    4.进队列
    创建一个新结点用于存放元素e(由p指向它)。若原队列为空,则将链队结点的两个域均咨向结点p,否则将结点p链接到单链表的末尾,并让链队结点的rear域指向它。算法如下:

    void enQueue(LinkQuNode*&q,ElemType e)
    {
      DataNode *p;
      p=(DataNode*)malloc(sizeof(DataNode));
      p->data=e;
      p->next=NULL;
      if(q->rear==NULL)
        q->front=q->rear=p;
      else
      {
        q->rear->next=p;
        q->rear=p;
      }
    }
    

    5.出队列
    若原队列为空,则下溢出返回假;若原队列不空,则将首结点的data域值赋给e,并副除之,若原队列只有一个结点,则需将链队结点的两个域均置为NULL,表示队列已为空。

    bool deQueue(LinkQuNode*&q,ElemType&e)
    {
      DataNode *t;
      if(q->rear==NULL)
        return false;
      t=q->front;
      if(q->front==q->rear)
        q->front==q->rear==NULL;
      else
        q->front=q->front->next;
      e=t->data;
      free(t);
      return true;
    }
    

    队列应用
    pta 6.3
    代码如下

    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->rear + 1) % MAXQSIZE;//队尾加一
        Q->data[Q->rear] = e;//将数据写入队尾
        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符号配对

    https://gitee.com/z202021123020/zs777/commit/02627105f630054e8ba96e055995fe43a247b0c1

    2.1.1解题思路及伪代码

    #include <iostream>
    #include <stack>
    #include <string>
    using namespace std;
    
    int main()
    {
    	stack <char> str;//初始化
    	int i, length, flag = 0;
    	static char A[1000];
    	char ch;
    	i = 0;
    	scanf("%c", &A[i]);
    	while (!(i >= 1 && A[i] == '
    '&&A[i - 1] == '.'))//遍历整个输入来计算长度
    	{
    		i++;
    		scanf("%c", &A[i]);
    	}
    	length = i - 1;
    	i = 0;
    	while (i < length)//开始一一判断
    	{
    		switch (A[i])//对左符号进行判断
    		{
    		case '/':
    			if (i + 1 < length&&A[i + 1] == '*')//将整个/*入栈
    			
    		case'('://将‘(’入栈
    			
    		case'['://将‘【’入栈
    			
    		case '{'://将‘{’入栈
    			
    		case'*':
    			if (i + 1 < length&&A[i + 1] == '/')//对*/进行判断
    			{
    				if (str.empty())//对?-*/判断 如果是flag=2
    
    				ch=str.top;
    
    				if (ch == '*')//如果匹配就出栈
    				
    				else//不匹配返回no
    						
    			break;
    		case ')'://对‘)’进行判断
    			if (str.empty())//对?-)判断
    		
    			ch = str.top();
    			if (ch == '(')//如果匹配就出栈
    		
    			else//不匹配返回no
    			
    			break;
    		case '}'://对‘}’进行判断
    			if (str.empty())//对?-}判断
    			
    			ch = str.top();
    			if (ch == '{')//如果匹配就出栈
    		
    			else//不匹配返回no
    			
    			break;
    		case ']'://对‘】’进行判断
    			if (str.empty())//对?-】判断
    			
    			ch = str.top();
    			if (ch == '[')//如果匹配就出栈
    	
    			else//不匹配返回no
    			
    			break;
    		default://其他则跳过
    			break;
    		}
    		if (flag == 2)
    		{
    			printf("NO
    ");
    			printf("?-*/");
    			break;
    		}
    		else if (flag == 1)
    		{
    			printf("NO
    ");
    			if (str.empty())
    			{
    					printf("?-%c", A[i]);
    			}
    			else
    			{
    				if (ch == '*')
    				{
    					printf("/*-?");
    				}
    				else
    				{
    					printf("%c-?", ch);
    				}
    			}
    			break;
    		}
    		i++;
    	}
    	if (flag == 0 && !str.empty())
    	{
    		printf("NO
    ");
    		ch = str.top();
    		if (ch == '*')
    		{
    			printf("/*-?");
    		}
    		else
    		{
    			printf("%c-?",ch);
    		}
    	}
    	else if (flag == 0)
    	{
    		printf("YES
    ");
    	}
    }
    

    2.1.2 总结解题所用的知识点

    学会运用stack容器,不用单独再自己建一个栈的函数;

    2.2 银行业务队列简单模拟

    https://gitee.com/z202021123020/zs777/blob/master/7-6 银行业务队列简单模拟

    2.2.1 解题思路及伪代码

    #include <iostream>
    #include <queue>
    using namespace std;
    int main()
    {
    	int i, n, k;
    	cin >> k;//输入人数
    	queue<int> q1;
    	queue<int> q2;//创建队列
    	for (i = 0; i < k; i++)//输入编号
    	{
    		cin >> n;
    		if (n % 2)//入奇数的栈
    		
    		else//入偶数的栈
    		
    	}
    	for (i = 0; i < k; i++)
    	{
    		if (i == 0)//先对第一个数据输出处理空格
    		
    		if ((i + 1) % 3)//开始对a窗口处理
    		{
    			if (!q1.empty())//判断是否处理完毕,未处理完毕打出a窗口的编号
    			
    			else//处理完毕直接打出b窗口的所有编号
    			
    		}
    		else//对b窗口处理
    		{
    			if (!q2.empty())//判断是否处理完毕,未处理完毕打出b窗口的编号
    			
    			else//处理完毕直接打出b窗口的所有编号
    			
    		}
    	}
    	return 0;
    }
    

    2.2.2 总结解题所用的知识点
    学会运用queue容器,不用单独再自己建一个队列的函数;

    3.阅读代码

    3.1 题目及解题代码

    class MyQueue {
    private:
        stack<int> inStack, outStack;
    
        void in2out() {
            while (!inStack.empty()) {
                outStack.push(inStack.top());
                inStack.pop();
            }
        }
    
    public:
        MyQueue() {}
    
        void push(int x) {
            inStack.push(x);
        }
    
        int pop() {
            if (outStack.empty()) {
                in2out();
            }
            int x = outStack.top();
            outStack.pop();
            return x;
        }
    
        int peek() {
            if (outStack.empty()) {
                in2out();
            }
            return outStack.top();
        }
    
        bool empty() {
            return inStack.empty() && outStack.empty();
        }
    };
    

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

    将一个栈当作输入栈,用于压入 push 传入的数据;另一个栈当作输出栈,用于 pop 和 peek 操作。
    每次 pop 或 peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。

    复杂度分析
    时间复杂度:push 和 empty 为 O(1),pop 和 peek 为均摊 O(1)。对于每个元素,至多入栈和出栈各两次,故均摊复杂度为O(1)。
    空间复杂度:O(n)。其中 n 是操作总数。对于有 n 次 push 操作的情况,队列中会有 n 个元素,故空间复杂度为 O(n)。

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

    利用栈实现队列的功能。
    难点:如何实现利用两个栈来实现数据的输入与输出。

  • 相关阅读:
    【实战】如何实现滚轮时间的显示
    NSDate的常用用法
    UIDatePicker的简单用法
    NSDateFormatter相关整理
    UIPickerView
    回家任务
    addTarget:self 的意思是说,这个方法在本类中
    2020/2/25
    树上启发式合并
    题解
  • 原文地址:https://www.cnblogs.com/13859862647z/p/14616221.html
Copyright © 2011-2022 走看看