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

0.PTA得分截图


1.本周学习总结

1.1 总结栈和队列内容

栈的存储结构及操作


  栈其实本质还是线性表:限定仅在表尾进行插入或删除操作。 俗称:后进先出 ,也可说是先进后出(FILO),栈也分为顺序和链式两大类。
  栈是限定只能在表尾删除和插入操作的线性表。
  由于栈本身就是一个线性表,所以线性表的操作特性它都具备,针对它的特殊性,在它的操作上可能会有一些变化。将进栈和出栈分别改名为push和pop。
  用顺序存储结构存储的栈称为顺序栈。线性表是用数组来实现的。对于栈,用下标为0的一端作为栈底比较好,因为首元素都存在栈底,变化比较小。
  我们定义一个top变量来指示栈定元素在数组中的位置。若存储栈的长度为MaxSize,则栈顶位置top必需小于MaxSize。当栈存在一个元素时,top等于0,因此空栈的判断条件为top等于-1.
  栈的结构定义:
  typedef int SElemType;
    typedef struct {
        SElemType data[MAXSIZE];
        int top;                //用于栈顶指针
    } SqStack;


入栈操作:
  bool Push(Stack S, ElementType X)
{
    if (S->Top == S->MaxSize)
    {
        printf("Stack Full
");
        return false;
    }
    else
    {
        S->Data[S->Top] = X;
        S->Top++;
        return true;
    }
    
}

出栈操作:
  ElementType Pop(Stack S)
{
    if (S->Top == 0)
    {
        printf("Stack Empty
");
        return ERROR;
    }
    else
    {
        S->Top--;
        return S->Data[S->Top];
    }
}


1.2关于共享栈

  共享栈,即是两个栈使用同一段存储空间。
  第一个栈从数组头开始存储,第二个栈从数组尾开始,两个栈向中间拓展。
  当top1+1==top2或者top1==top2-1时,即有栈空
  与普通栈一样,共享栈出栈入栈的时间复杂度仍为O(1).

数据结构
typedef struct shareStack{
    int data[MAXSIZE];
    int top1;
    int top2;
}shareStack;

出栈操作

int Pop(shareStack *ss,int flag){
    if(flag == 1){
        if(ss->top1 == -1)
            return -1;
        return ss->data[ss->top1--];
    }else if(flag == 2){
        if(ss->top2 == MAXSIZE)
            return -1;
        return ss->data[ss->top2++];
    }
    return -1;
}

入栈操作
int Push(shareStack *ss,int num,int flag){
    if(ss->top1+1 == ss->top2)
        return 0;
    if(flag == 1){
        ss->data[++ss->top1] = num;
        return 1;
    }else if( flag == 2){
        ss->data[--ss->top2] = num;
        return 1;
    }
    return 0;
}


1.3栈的应用

表达式求值


  中缀表达式:运算符号位于两个运算数之间。如 ,a+b*c-d/e
  后缀表达式:运算符号位于两个运算数之后。如, abc*+de/-
  借助一个栈来存放运算数。
  优先级比栈顶运算符高写入栈
  优先级比较低或者相等,出栈,写入后缀表达式中

伪代码

while (从exp读取字符ch,ch!='')
{       ch为数字:将后续的所有数字均依次存放到postexp中,
                     并以字符'#'标志数值串结束;
        ch为左括号'(':将此括号进栈到Optr中;
        ch为右括号')':将Optr中出栈时遇到的第一个左括号'('以前的运算符
                    依次出栈并存放到postexp中,然后将左括号'('出栈;
        ch为'+'或'-':
                   出栈运算符并存放到postexp中,直到栈空或者栈顶为'(',
                   然后将'+'或'-'进栈;
        ch为'*'或'/':
                      若 栈顶为'*'或'/',出栈,直到栈顶不是'*'或'/'
                      否则 入栈
}
若exp扫描完毕,则将Optr中所有运算符依次出栈并存放到postexp中。

迷宫问题


  求解思想:回溯法
  从入口出发,按某一方向向未走过的前方探索
  若能走通,则到达新点,否则试探下一方向 ; 
  若所有的方向均没有通路,则沿原路返回前一点,换下一个方向再继续试探
  直到所有可能的通路都探索到,或找到一条通路,或无路可走又返回到入口点。


  每个方块的数据定义:
  typedef struct
  {  int i;		//当前方块的行号
    int j;		//当前方块的列号
    int di;	
  } Box;		//定义方块类型
  栈定义:
  typedef struct
  {  Box data[MaxSize];
     int top;		//栈顶指针
  } StType;		//顺序栈类型
  
  迷宫问题这一块比较不清楚.


1.4队列的存储结构及操作


  限定在表的一端插入、另一端删除。 插入的那头就是队尾,删除的那头就是队头。也就是说只能在线性表的表头删除元素,在表尾插入元素。
  先进先出 (FIFO结构)。显然我们不能在表(队列)的中间操作元素,只能是在尾部进,在头部出去
  当队列没有元素的时候,我们就说队列是空队列。

链式队列


  用链表表示的队列,限制仅在表头删除和表尾插入的单链表。一个链队列由一个头指针和一个尾指针唯一确定。

顺序队列

(1)初始为空队列,那么头尾指针相等

(2)入队,那么尾指针加1,头指针不变。

(3)出队,则尾指针不变,头指针加1,先进先出原则

(4)删除,则头指针再次和尾指针相等,说明队列空了

(5)在顺序队列中,当尾指针已经指向了队列的最后一个位置的下一位置时,若再有元素入队,就会发生“溢出”

循环队列


  循环队列我们必须给定最大值MAXQSIZE。
  当添加一个元素时,(rear+1)%MAXQSIZE
  当删除一个元素时,(front+1)%MAXQSIZE
  队列满:(Q.rear+1)%MAXSIZE=Q.front
  队列空:Q.rear==Q.front

添加操作

status EnQueue(SqQueue *q,QElemtype e)
{
    //插入到队尾
    if((q->rear+1)%MAXQSIZE==q->front)
        return 0;
    q->base[q->rear]=e;
    q->rear=(q->rear+1)%MAXQSIZE;
    return 1;
}

删除操作

status DeQueue(SqQueue *q)
{
    if(q->front==q->rear)
        return 0;
    printf("%d",q->base[q->front]);
    q->front =(q->front+1)%MAXQSIZE;
    return 1;
}

获取队列长度

int QueueLength(SqQueue *q)
{
    return (q->rear-q->front+MAXQSIZE)%MAXQSIZE;
}

定义

typedef struct{
    QElemtype *base;
    int front;
    int rear;
    }SqQueue;

初始化队列

SqQueue* InitQueue()
{
    SqQueue *q;
    q=new SqQueue;
    q->base=new int[MAXQSIZE];
    q->rear=q->front=0;
    return q;
}


1.5队列应用

舞伴问题


  舞伴问题,利用队列将舞伴人分别存到两个性别不同的队列当中,党配对舞伴时,将其再出队列
  

  int QueueLen(SqQueue Q)//队列长度获取
{
	int length = 0;
	int i = Q->front;
	int j = Q->rear;
	for (i = 0; i != j; i++)
	{
		length++;
	}
	return length;
}

int EnQueue(SqQueue& Q, Person e)//加入队列
{
	if (Q->rear == MAXQSIZE - 1)
	{
		return 0;
	}
	else
	{
		Q->rear++;
		Q->data[Q->rear] = e;
		return 1;
	}
}
int QueueEmpty(SqQueue& Q)//队列是否为空
{
	return(Q->front == Q->rear);
}
int DeQueue(SqQueue& Q, Person& e)//出队列
{
	if (Q->front == Q->rear)
	{
		return 0;
	}
	else
	{
		Q->front++;
		e = Q->data[Q->front];
		return 1;
	}
}
void DancePartner(Person dancer[], int num)//配对舞伴
{
	Person son;
	int i;
	for (i = 0; i < num; i++)
	{
		son = dancer[i];
		if (son.sex == 'F')
		{
			EnQueue(Fdancers, son);
		}
		if (son.sex == 'M')
		{
			EnQueue(Mdancers, son);
		}
	}
	while (!QueueEmpty(Fdancers) && !QueueEmpty(Mdancers))
	{
		DeQueue(Fdancers, son);
		cout << son.name << "  ";
		DeQueue(Mdancers, son);
		cout << son.name << endl;

	}
}


1.2.谈谈你对栈和队列的认识及学习体会。


本周我们主要学习了栈和队列的基本操作和应用。首先来说栈,栈只能在栈顶进行操作,是先进后出,可用于符号配对,走迷宫和计算后缀表达式等。栈可以分为顺序栈和链栈,运用于不同的情况。队列是先进先出,可在队头删除,也可以在队尾插入。队列也可分为顺序队和链队,在顺序队中,为了防止假溢出,又增加了循环队列。
在C++中的stack、queue类模板中,已经有写好的函数帮我们执行一些基础函数,所以我们要学会应用C++函数。本章节还应该熟记各种栈空、栈满、队空、队满的条件,并注意在出队,出栈和取队、栈时,都应该注意判断队列、栈是否为空。


2.PTA实验作业

2.1.题目1:符号配对

2.1.1代码截图



2.1.2本题PTA提交列表说明。


Q1:编译错误
A1:因为在PTA编译器里面没有吧C改为C++编译
Q2:部分正确,栈空没有通过
A2:加上判断栈是否为空的条件

2.2.题目2:银行业务队列简单模拟

2.2.1代码截图



2.2.2本题PTA提交列表说明。


  Q1:编译错误
  A1:oddQu.front()误写成oddQu.front
  Q2:最小的N(即N=1)时格式错误
  A2:当N=1即队列中只有一个队列中有一个数时,输出的数字前会多一个空格。在源代码下面添加了 
  对N=1的特殊判断。


3.阅读代码

3.1 题目及解题代码

题目:

解题代码:


public class SortedStack {
    Stack<int> stack = new Stack<int>();
    Stack<int> temp = new Stack<int>();

    public SortedStack()
    {
    }

    public void Push(int val)
    {
        
        while (true)
        {
            var max = stack.Count == 0 ? int.MaxValue : stack.Peek();
            var min = temp.Count == 0 ? int.MinValue : temp.Peek();

            if (val > max)
            {
                temp.Push(stack.Pop());
            }
            else if (val < min)
            {
                stack.Push(temp.Pop());
            }
            else
            {
               
                break;
            }
        }

   
        stack.Push(val);
    }

    public void Pop()
    {
        
        while (temp.Count > 0)
        {
            stack.Push(temp.Pop());
        }

        if (stack.Count == 0)
        {
            return;
        }

        stack.Pop();
    }

    public int Peek()
    {
        
        while (temp.Count > 0)
        {
            stack.Push(temp.Pop());
        }

        if (stack.Count == 0)
        {
            return -1;
        }

        return stack.Peek();
    }

    public bool IsEmpty()
    {
        return stack.Count == 0 && temp.Count == 0;
    }
}


3.1.2该题的设计思路

  • 临时栈存放的数据永远小于主栈的数据,每次新数字入栈时,仅仅动态调整两个栈,使临时栈的数据小于插入值,主栈的数据大于插入值,然后插入数值到主栈,每次Peek或Pop时才懒惰更新将临时栈的数据全部放回主栈。

3.1.2 该题的伪代码


public class SortedStack {
    Stack<int> stack = new Stack<int>();
    Stack<int> temp = new Stack<int>();

   
    public void Push(int val)
    {
        
        while (true)
        {
           
            if (主栈数据大于插入值)
            {
                插入数值到主栈;
            }
            else if (主栈数据小于插入值)
            {
                插入数值到主栈;
            }
            else
            {
                此时调整好了
                    就break;
            }
        }

        往栈中写入数据;
    }

    public void Pop()
    {
        
        while (临时栈大于0)
        {
            将临时栈内的数据返回已有栈;
        }

        if (临时栈等于0)
        {
            返回;
        }

        stack.Pop();
    }

    public int Peek()
    {
        
        while (临时栈大于0)
        {
            将临时栈内的数据返回已有栈
        }

        if (临时栈等于0)
        {
            return -1;
        }

        
    }

    public bool IsEmpty()
    {
        return stack.Count == 0 && temp.Count == 0;
    }
}

3.1.3运行结果

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

  • 优势:该题通过利用双栈来完成,使得做法的空间更大更容易操作,用临时栈来存放思路更加清晰
  • 难点:该题是LeetCode上找到的一道面试题,总的难度不是很大,主要是感觉判断部分比较难

3.2题目及解题代码

题目:

解题代码:


class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> st;
        int n = popped.size();
        int j = 0;
        for (int i = 0; i < pushed.size(); ++i){
            st.push(pushed[i]);
            while(!st.empty() && j < n && st.top() == popped[j]){
                st.pop();
                ++j;
            }
        }
        return st.empty();
    }
};

3.2.1该题的设计思路

思路很简单,我们尝试按照 popped 中的顺序模拟一下出栈操作,如果符合则返回 true,否则返回 false。这里用到的贪心法则是如果栈顶元素等于 popped 序列中下一个要 pop 的值,则应立刻将该值 pop 出来。

3.2.2 该题的伪代码


  初始化栈 stack,j = 0;
  遍历 pushed 中的元素 x;
    当 j < popped.size() 且栈顶元素等于 popped[j]:
      使栈顶元素出栈;
      j += 1;
  如果栈为空,返回 true,否则返回 false。

3.2.3 运行结果


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

  • 优势:通过pushed和st,使用st来模拟操作,将数组中的每个数依次入栈,使得思路比较清晰
  • 难点:该题难度不大,就是在检查栈的程序比较复杂
原文地址:https://www.cnblogs.com/w-y-h--/p/12508532.html