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

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

0. PTA得分截图

1. 本周学习总结

1.1 栈

① 顺序栈

  • 顺序栈通常用数组来存放数据,栈内数据在栈顶进行交互;

操作函数

  • 顺序栈结构体
struct SNode {
    ElementType* Data;  /* 存储元素的数组   */
    Position Top;     /* 栈顶指针 */
    int MaxSize;
};
  • 创建栈并初始化
Stack CreateStack(int MaxSize)
{
    Stack S = (Stack)malloc(sizeof(struct SNode));
    S->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));
    S->Top = -1;//将栈空时的栈顶指针设为 -1 
    S->MaxSize = MaxSize;
    return S;
}
  • 入栈
void PushStack(Stack S, ElementType x)//入栈
{
    if (S->Top == S->MaxSize)
    {
        printf("Stack Full\n");
        return;
    }
    S->Top++;
    S->Data[S->Top] = x;
}
  • 出栈
ElementType PopStack(Stack S)//出栈,并将栈顶元素作为返回值
{
    if (S->Top == -1)
    {
        printf("Stack Empty\n");
        return 0;
    }
    return S->Data[S->Top--];
}
  • 栈空
int StackEmpty(Stack S)//当栈为空时返回1
{
    if (S->Top == -1)
        return 1;
    else
        return 0;
}

② 链栈

  • 相比于顺序栈,链栈没有预申请空间不足的顾虑;头插法也保证入栈出栈操作的一步到位;

操作函数
由于C++标准库的栈函数就是以链栈结构为基础,此处只介绍操作函数;

#include<stack>//头文件
stack<int> s;//创建一个int变量类型的链栈 s ;
s.empty(); //如果栈为空则返回true, 否则返回false;
s.size(); //返回栈中元素的个数
s.top(); //返回栈顶元素, 但不删除该元素
s.pop(); //弹出栈顶元素, 但不返回其值
s.push(); //将元素压入栈顶,大多是使用头插法

链栈稍微牺牲空间,换来条理分明的结构与更快的操作速度

1.2 栈的应用

  • 中缀表达式改写为后缀表达式
    在数学计算中,我们习惯与通过中缀表达形式计算算式,即数字在运算符号的两边,而对于计算机而言,后缀表达式更适合处理算式,因此必然有,从中缀表达式到后缀表达式的过程,并且计算机利用后缀表达式计算的过程,,而这些都可以通过栈实现。
    通过一个预先设定好的优先级入栈出栈,'+''-'优先级最低,'*''/'较高,括号内的算式优先级更高,在进行比较出栈入栈操作

  • 括号匹配检验
    在辅助编程的软件中,总会有一个括号匹配的判断检验,如果有括号没有对应的另一半进行配对,编译器就会报错来提醒那些粗心的程序员;而对于括号来说,每一个左括号都需要一个相应的右括号与之匹配,表达式中越迟出现并且没有得到匹配的左括号匹配的优先级越高,这种先进先出的特点正与栈结构匹配;
    基本思路:创建一个栈,在读入字符的过程中,如果是左括号,则直接入栈,等待相匹配的同类右括号;如果是右括号,且与当前栈顶左括号匹配,则将栈顶左括号出栈;

  • 迷宫求解
    对迷宫来说,所有的迷宫都有一个特点,即从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路退回,换一个方向再继续探索,直至所有可能的通路都探索到为止;原路返回的操作,显然需要个先进先出的结构来储存上一级信息,所以栈自然可以来求解迷宫;

伪代码

初始化,将起点加入堆栈;
while(堆栈不为空){
    取出栈顶位置为当前位置;
    if(当前位置是终点)
        使用堆栈记录的路径标记从起点至终点的路径;
    else
    {
        按照某个顺序将当前位置下一个可以探索的位置入栈;
        if(当前位置的四周均为死路)
            当前位置出栈;
    }
}

1.3 队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头.

① 顺序队列

  • 结构体定义
typedef struct
{
    ElemType data[MaxSize];
    int front, rear;
}SqQueue;
typedef SqQueue* Queue;
  • 初始化队列
void InitQueue(Queue Q)
{
    Q = (Queue)malloc(sizeof(SqQueue)*Max);
    Q->front = Q->rear = 0;
}
  • 销毁队列
void DestroyQueue(Queue Q)
{
    free(Q);
}
  • 判断队列是否为空
bool QueueEmpty(Queue Q)
{
    return (Q->front == Q->rear);
}
  • 入队
bool EnQueue(Queue Q,ElemType e)
{
    if(Q->rear == MaxSize)
        return false;
    Q->data[Q->rear++] = e;
    return true;
}
  • 出队
bool DeQueue(Queue Q,ElemType &e)
{
    if(Q->front == Q->rear)
        return false;
    e = Q->data[Q->front++];
    return true;
}

② 环形队列

  • 结构体定义
typedef struct
{
    ElemType data[MaxSize];
    int front, rear;
}SqQueue;
typedef SqQueue* Queue;
  • 初始化队列
void InitQueue(Queue Q)
{
    Q = (Queue)malloc(sizeof(SqQueue)*Max);
    Q->front = Q->rear = 0;
}
  • 销毁队列
void DestroyQueue(Queue Q)
{
    free(Q);
}
  • 判断队列是否为空
bool QueueEmpty(Queue Q)
{
    return (Q->front == Q->rear);
}
  • 入队
bool EnQueue(Queue Q,ElemType e)
{
    if((Q->rear +1) % MaxSize == Q->front)
        return false;
    Q->data[Q->rear] = e;
    Q->rear = (Q->rear + 1) % MaxSize;
    return true;
}
  • 出队
bool DeQueue(Queue Q,ElemType &e)
{
    if(Q->front == Q->rear)
        return false;
    e = Q->data[Q->front];
    Q->front = (Q->front + 1) % MaxSize;
    return true;
}

③ 链式队列

  • 结构体定义
typedef struct QNode    /* 声明链式队列的结点 */
{
    int data;
    struct QNode *next;
}Node;
typedef struct QueuePoint    /* 声明链式队列的首尾指针 */
{
    Node *front;
    Node *rear;
};
typedef QueuePoint* Queue;
  • 初始化队列
Queue InitQueue (Queue Q)    
{                        
    Q = (Queue)malloc(sizeof(QueuePoint));
    Q->front = Q->rear = NULL;

    return Q;
}
  • 销毁队列
void DestroyQueue(Queue Q)
{
    Node* pre = Q->front,* p;
    if(pre != NULL)
    {
        p = pre->next;
        while(p != NULL)
        {
            free(pre);
            pre = p;p = p->next;
        }
        free(pre);
    }
    free(Q);
}
  • 判断队列是否为空
bool QueueEmpty(Queue Q)
{
    return (Q->rear == NULL);
}
  • 入队
void EnQueue(Queue Q,ElemType e)
{
    Node* p;
    p = (Node*)malloc(sizeof(Node));
    p->data = e;
    p->next = NULL;
    if(Q->rear == NULL)
        Q->front = Q->rear = p;
    else
    {
        Q->rear->next = p;
        Q->rear = p;
    }
}
  • 出队
bool DeQueue(Queue Q,ElemType &e)
{
    Node* p;
    if(Q->rear == NULL)
        return false;
    p = Q->front;
    if(Q->front == Q->rear)
        Q->front = Q->rear = NULL;
    else
        Q->front = Q->front->next;
    e = p->data;
    free(p);
    return true;
}

④ 队列应用

  • 银行业务队列简单模拟
    某银行有A、B两个业务窗口,且处理业务的速度不一样,其中A窗口处理速度是B窗口的2倍 —— 即当A窗口每处理完2个顾客时,B窗口处理完1个顾客。编号为奇数的顾客需要到A窗口办理业务,为偶数的顾客则去B窗口;给出顾客人数与编号,顺序输出顾客办理完成业务的编号;
创建队列A,B;
读入人数n;
for (int i = 0; i < n; i++)//入列
    {
        cin >> numb;
        if (numb % 2 == 1)
            EnQueue(WindowA, numb);//奇数号入A列
        else
            EnQueue(WindowB, numb);//偶数号入B列
    }
按照AAB的顺序输出即可;

2. PTA实验作业

2.1 符号配对

2.1.1 解题思路及伪代码

  • 解题思路
    后缀表达式,顾名思义是运算符在数字后的表达式,即取运算符前的两个数字作为被运算数和运算数;
    在中缀表达式转后缀表达式的过程中,同样需要考虑运算符优先级的问题,这就可能导致优先度高的运算符先于优先度低的运算符输出,先入先出的特性,适合用栈结构来辅助转换;
    令'+'与'-'作为低级运算符,优先度为0;'*'与'/'作为高级运算符,优先度为1;括号内式子优先度最高,在我的思路里将括号内的算式看做新的算式,进行递归转换;
    将按序读取到的运算符与栈顶运算符进行对比,优先度低的运算符入栈,优先度高的运算符输出,优先度相等则先出栈后入栈,遇到括号则递归调用函数;
    数值数据则直接输出,不进行栈操作;

  • 伪代码

创建字符串并将算式读入字符串内;
某函数()
{
    创建并初始化栈;
    遍历字符串
    {
        if(读到'\0'或')')
            返回;
        if(字符为数字,小数点或正负值的符号)
            输出字符;
        else if(栈内为空 或 字符优先级高于栈顶字符)
            入栈;
        else if(字符优先级低于栈顶字符)
        {
            do 出栈并输出 while (字符优先级高于栈顶 或 栈为空);
            入栈;
        }
        else if(字符为左括号)
            递归调用 某函数();
    }
}  
  • 源代码
展开

#include
#include
#include
#include
using namespace std;
#define Max 1000
#define ERROR -1
typedef char ElementType;
typedef int Position;
struct SNode {
    ElementType* Data;  /* 存储元素的数组   */
    Position Top;     /* 栈顶指针 */
    int MaxSize;
};
typedef struct SNode* Stack;
Stack CreateStack(int MaxSize);
void EnStack(Stack S, char x);
char PopStack(Stack S);
void SymbolPairing(Stack S);
void NotesSymbol(Stack S);
int main()
{
    Stack S;
    S = CreateStack(Max);
    SymbolPairing(S);
    return 0;
}
void SymbolPairing(Stack S)
{
    char chL = 0, chR = 0;
    char leftSymbolL, leftSymbolR;
    while ((chR = getchar()) && chR != EOF)
    {
        if (chL == '.' && chR == '\n')
            break;
        if (chR == '[' || chR == '{' || chR == '(')
            EnStack(S, chR);
        else if (chL == '/' && chR == '*')
        {
            EnStack(S, chL);
            EnStack(S, chR);
            //NotesSymbol(S);
            chR = ' ';
        }
        else if (chR == ']' || chR == '}' || chR == ')' || (chL == '*' && chR == '/'))
        {
            if (S->Top == -1)
            {
                cout << "NO" << endl;
                if (chL == '*' && chR == '/')
                    cout << "?-" << chL << chR << endl;
                else
                    cout << "?-" << chR << endl;
                return;
            }
            leftSymbolL = PopStack(S);
            if ((leftSymbolL == '[' && chR != ']') || (leftSymbolL == '{' && chR != '}') || (leftSymbolL == '(' && chR != ')') || (leftSymbolL == '*' && !(chL == '*' && chR == '/')))
            {
                cout << "NO" << endl;
                cout << leftSymbolL << "-?" << endl;
                return;
            }
            else if (leftSymbolL == '*')
            {
                if (chL == '*' && chR == '/')
                    PopStack(S);
                else
                {
                    cout << "NO" << endl;
                    cout << PopStack(S) << leftSymbolL << "-?" << endl;
                    return;
                }
            }
            chR = ' ';
        }
        chL = chR;
    }
    if (S->Top != -1)
    {
        cout << "NO" << endl;
        if (S->Data[S->Top] != '*')
            cout << PopStack(S) << "-?" << endl;
        else
            cout << PopStack(S) << PopStack(S) << "-?" << endl;
    }
    else
        cout << "YES" << endl;
    return;
}
Stack CreateStack(int MaxSize)
{
    Stack S = (Stack)malloc(sizeof(struct SNode));
    S->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));
    S->Top = -1;
    S->MaxSize = MaxSize;
    return S;
}
void EnStack(Stack S, char x)
{
    if (S->Top == S->MaxSize)
    {
        printf("Stack Full\n");
        return;
    }
    S->Top++;
    S->Data[S->Top] = x;
}
char PopStack(Stack S)
{
    if (S->Top == -1)
    {
        printf("Stack Empty\n");
        return 0;
    }
    return S->Data[S->Top--];
}
/*void NotesSymbol(Stack S)
{
    char chL = 0, chR = 0;
    while ((chR = getchar()) && chR != EOF)
    {
        if (chL == '.' && chR == '\n')
        {
            cout << "NO" << endl;
            cout << PopStack(S) << PopStack(S) << "-?" << endl;
            exit(0);
        }
        if (chL == '*' && chR == '/')
        {
            PopStack(S);
            PopStack(S);
            return;
        }
        chL = chR;
    }
}*/

2.1.2 总结解题所用的知识点

  • 四则运算法则;
  • 顺序栈的结构;
  • 顺序栈的出栈入栈操作;
  • 递归函数的调用;
  • 标准输入输出流的运用;

2.2 符号配对

2.2.1 解题思路及伪代码

  • 解题思路
    这题啊,这题比上面那题好搞多了;
    弄两个队列A、B,读入数据,将奇数编号的入A列,将偶数编号的入B列;
    在按A、A、B的顺序输出(若队列为空则跳过);
    结束
  • 伪代码
创建队列 A, B;
读入人数 n;
for(从0到n)
{
    读入数据x;
    如果x为奇数
        入A列;
    如果x为偶数
        入B列;
}
按AAB的顺序输出;
结束;
  • 源代码
展开

#include
#include
#include
#include
using namespace std;
#define Max 2000
#define ERROR -1
typedef int ElementType;
typedef int Position;
struct QNode {
    ElementType* Data;  /* 存储元素的数组   */
    Position Front, rear;     /* 队列的头、尾指针 */
    int MaxSize;        /* 队列最大容量     */
};
typedef struct QNode* Queue;
Queue CreateQueue(int MaxSize);
void EnQueue(Queue Q, int x);
int PopQueue(Queue Q);
int QueueEmpty(Queue Q);
int main()
{
    int n;
    cin >> n;
    int flag = 0;
    int numb;
    Queue WindowA, WindowB;
    WindowA = CreateQueue(Max);
    WindowB = CreateQueue(Max);
    for (int i = 0; i < n; i++)
    {
        cin >> numb;
        if (numb % 2 == 1)
            EnQueue(WindowA, numb);
        else
            EnQueue(WindowB, numb);
    }
    while (!QueueEmpty(WindowA) || !QueueEmpty(WindowB))
    {
        if (!QueueEmpty(WindowA))
        {
            if (flag++)
                cout << ' ';
            cout << PopQueue(WindowA);
        }
        if (!QueueEmpty(WindowA))
        {
            if (flag++)
                cout << ' ';
            cout << PopQueue(WindowA);
        }
        if (!QueueEmpty(WindowB))
        {
            if (flag++)
                cout << ' ';
            cout << PopQueue(WindowB);
        }
    }
    return 0;
}
Queue CreateQueue(int MaxSize)
{
    Queue Q = (Queue)malloc(sizeof(struct QNode));
    Q->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));
    Q->Front = 0;
    Q->rear = 0;
    Q->MaxSize = MaxSize;
    return Q;
}
void EnQueue(Queue Q, int x)
{
    if (Q->rear == Q->MaxSize)
    {
        printf("Queue Full\n");
        return;
    }
    Q->Data[Q->rear++] = x;
    return;
}
int PopQueue(Queue Q)
{
    if (Q->rear == Q->Front)
    {
        printf("Queue Empty\n");
        return -1;
    }
    return Q->Data[Q->Front++];
}
int QueueEmpty(Queue Q)
{
    if (Q->rear == Q->Front)
        return 1;
    else
        return 0;
}

2.2.2 总结解题所用的知识点

  • 顺序队列的结构
  • 顺序队列的入列出列操作
  • 奇数和偶数的定义
  • 生活常识

3.阅读代码

3.1 题目及解题代码

bool validateStackSequences(int *pushed, int pushedSize, int *popped, int poppedSize)
{
    if (pushed == NULL || pushedSize < 0 || popped == NULL || poppedSize < 0) {
        return false;
    }
    if (pushedSize > 1000 || poppedSize > 1000){
        return false;
    }
    int tmpStack[1000] = {0};
    int tmpStackTop = -1;
    int pushedIter = 0;
    int poppedIter = 0;
    while (pushedIter < pushedSize) {
        // 入栈一个数
        tmpStack[++tmpStackTop] = pushed[pushedIter++];

        // 当前栈顶如果和pop当前位置相同,则出栈
        while (tmpStackTop >= 0 && poppedIter < poppedSize && tmpStack[tmpStackTop] == popped[poppedIter]) {
            // 出栈后,栈顶下移,pop序列增加
            tmpStack[tmpStackTop--] = 0;
            poppedIter++;
        }
    }
    // 最终栈底回到-1位置,否则没有完全匹配
    if (tmpStackTop != -1) {
        return false;
    }
    return true;
}

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

  • 思路
    考察程序员对栈结构的理解,以及栈的基础操作的应用;

  • 伪代码

创建栈 S;
创建队列 Q;//图个方便
将popped的数据入队列Q;
while(队列不空)
{
    若pushed还有数据输入
        入栈;
    否则
        将栈顶元素与队头元素匹配,若不同
            返回false;
    将栈顶元素与队头元素匹配,若相同
        出栈且出列;
    
}
返回true;

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

解题优势:Java以及Python中的库函数(类似)可以大幅简化代码(10行)

难点:考察栈的基础知识以及应用,难点在于理解深度以及循环跳出(false)条件判断

posted @ 2021-04-11 12:02  DuckSword  阅读(65)  评论(0编辑  收藏  举报