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

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

0.PTA得分截图

1.本周学习总结(0-5分)

1.1 栈

1.1.1顺序栈

顺序栈的结构体定义:


/* 存储空间初始分配量 */
#define MAXSIZE 20

/* SElemType类型根据实际情况而定,这里假设为int */
typedef int SElemType;

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

顺序栈的基本操作:

  • 初始化栈
/*顺序栈*/
void InitStack(SNode &s)
{
     s.top=-1;//一般的顺序表栈顶指针的初始化为-1,
}
  • 判断是否为空栈
/*顺序栈*/
bool IsEmpty(SNode s)//如果为空返回true,不为空则返回false;
{
    if(s.top==-1)
        return true;
    else
        return false;
}
  • 判断是否栈满
    对于顺序栈来说,是用数组来保存数据,因为数组在被定义时就需要预先申请一段连续的空间,所以如果我们要往里面进行插入的操作,必须要先判断该栈是否已经满了,否则就会出现数组溢出,访问错误的情况。
/*顺序栈*/
bool IsFull(sNode s)
{
    if(s.top==MaxSize-1)
       return true;
    else
       return false;
}
  • 进栈
    对栈的一端进行插入。
bool Push(SNode &s,Elemtype e)
{
    if(IsFull(s))//在进栈之前一定要先判断是否栈满
        return false;
    else
        s.data[++s.top] = e;
    return true;//说明进栈成功;
}
  • 取栈顶
    取得栈顶元素
/*顺序栈*/
bool Gettop(SNode &s, Elemtype &e)
{
     if(IsEmpty(s))//取栈顶时要判断是否为空栈!
       return false;
     else 
       e = s.data[s.top];
     return true ;
}

  • 出栈

/*顺序栈*/
bool Pop(SNode &s,Elemtype &e)//出栈并返回该栈顶元素;
{
    if(IsEmpty(s))//在出栈之前一定要判断是否为空栈
       return false ;
    else
      e = s.data[s.top--];//这里只是移动了栈顶指针,并没有真正的删除数据; 
    return true;//出栈成功;
}
  • 销毁栈
    将栈顶元素删除,并移动栈顶指针。
void DestroyStack(SNode &s)
{
    delete s;
}

1.1.2链栈

链栈的结构体定义:

typedef struct stack
{
     Elemtype  data;
     struct stack* next;
}SNode,*Stack;

链栈的基本操作:

  • 初始化栈
/*链栈*/
void InitStack(Stack &s)
{
     s = new SNode;//申请空间;
     s->next=NULL;
}
  • 判断是否为空栈

/*链栈*/
bool IsEmpty(Stack s)//如果为空返回true,不为空则返回false;
{
    if(s->next==NULL)
        return true;
    else 
        return false;

  • 判断是否栈满

因为是链式存储结构,不用预先设定需要多少的存储空间来保存数据,都是现用现配,空间利用效率比较高。所以只要一个数据我们就可以直接进栈,不必考虑是否会栈满。

  • 进栈
void Push(Stack &s,Elemtype e)//使用头插法;
{
    Stack str;
    str=new Stack;
    str->data = e;
    str->next= s->next;//先将原来的链栈连接到str后面;
    s->next = str;//再将str连接到头结点后面成为新的栈顶;
}

  • 出栈
/*链栈*/
void Pop(Stack & s,Elemtype &e)
{
    Stack str;
    if(IsEmpty(s))//出栈之前要判断是否为空栈;
         return false;
    else
    {
         e = s->next->data;
         str = s->next;
         s->next = str->next;//让头结点连接栈顶的下一个结点,使s->next->next成为新的栈顶;
         delete str;//释放空间,这里数据是真的被删除
    }
}

  • 取栈顶
bool Gettop(Stack &s,Elemtype &e)
{
   if(IsEmpty(s))//取栈顶时要判断是否为空栈!
      return false;
   else
      e = s->next->data;
   return true;
}
  • 销毁栈
/*链栈*/
void DestroyStack(Stack &s)
{ 
    ListStack str;
    while(s!=NULL)
    {
          str = s;//str保存当前删除的结点
          s = s->next;//s指向下一个需要删除的结点;
          delete str;
    }
}

1.1.3 C++类模板:stack

头文件 #include <stack>
stack <Elemtype> s;初始化栈,保存Elemtype类型的数据;
s.push(x);入栈元素t;
s.top();返回栈顶指针;
s.pop();出栈操作,只做删除栈顶元素的操作,不返回该删除元素;
s.empty();判断是否栈空,如果为空返回true;
s.size();返回栈中元素个数;

1.2 栈的应用

表达式转换
运算符在操作数中间,这种表达式叫做中缀表达式,如果计算机要跳过数据去判断所有运算符的优先级,再按顺序取运算符两边的数据进行运算的话,对计算机来说工作量是很大的。于是如果我们能把运算符的优先级先排好,然后直接取该运算符的前两个数据运算,这样就使得运算变的简便起来。像这样 运算符位于两个运算数之后的表达式式叫做后缀表达式。
我们可以设置一个栈来保存运算符,利用运算符的优先级来进行运算符进栈入栈的操作。这里我们可以用到map容器,对运算符进行赋值,以此来判断栈内栈外运算符优先级的高低。

  • 伪代码
结合运算符优先级用map容器给运算符赋值;
mp<char,int>in;//已入栈运算符的优先级;
mp<char,int>un;//未入栈时运算符优先级;
while(str[i])
{
    if(str[i]为数字或者小数点'.')
          while(str[i]为数字或者小数点'.')
              str[i]写入后缀表达式中,继续读取下一字符;
    else if(str[i]为正负号)
          将负号写入后缀表达式中,读取下一字符;
    else//都是运算符
    {
          if(栈空 || 栈顶运算符的优先级比str[i]低)
              str[i]进栈,再读取下一字符;
          else if(栈顶运算符的优先级比str[i]高)
              while(栈顶运算符的优先级比str[i]高)
                   将栈顶元素写入后缀表达式,读取下一字符;
          else //栈顶运算符的优先级等于str[i]的优先级,说明是左右括号配对的情况;
              将栈顶元素'('出栈;读取下一字符;
    }
    如果栈不空,则出栈所有元素,写入后缀表达式中;
}

  • 代码



1.3 队列

队列也是一个运算受限的线性表,它只能选取一端进行插入操作,另一端做删除操作,是先进先出的一种结构。我们把进行删除的一段叫做队头,进行插入的一端叫做队尾。

1.3.1顺序队列

顺序队结构体定义

typedf struct
{
   Elemtype data[MaxSize];
   int front;//队头指针;
   int rear;//队尾指针;
}QNode;

顺序队初始化

void InitQueue(Queue &q)
{
    q.front=q.rear=-1;
}

判断是否为空队

bool IsEmpty(Queue &q)
{
     if(q.rear==q.front)//队空
         return true;
     else//队不空
         return false;
}

判断是否队满

/*顺序队*/
bool IsFull(Queue &q)
{
      if(q.rear==MaxSize-1)//队满
          return true;
      else
          return false;
}

进队

bool Push(Queue &q,Elemtype e)
{
    if(IsFull(q))//在进队之前一定要先判断是否队满;
        return false;//表示入队失败;
    else
        q.data[++q.rear] = e;
    return true;//表示入队成功;
}

出队

bool Pop(Queue &q,Elemtype &e)
{
    if(IsEmpty())//出栈是要先判断是否为空栈;
        return false;
    else
        e = q.data[++q.front];
    return true;//表示出队成功;
}

取队头元素

bool GetFront(Queue q,Elemtype &e)
{
    if(IsEmpty(q))//取队头是要判断是否为空队;
       return false;
    else
       e = q.data[q.front + 1];
    return true;
}

销毁顺序队

void DestroyQueue(Queue &q)
{
    delete q;
}

1.3.2链队列

顺序队结构体定义

typedef struct qnode//用于保存每个结点;
{
    Elemtype data;
    struct qnode *next;
}Node,*LinkNode;

typedef struct 
{
   LinkNode front;//队头指针;
   LinkNode rear;//队尾指针;
}Queue;

顺序队初始化

void InitQueue(Queue &q)
{
    q.front->next = NULL;
    q.rear->next = NULL;
}

判断是否为空队


bool IsEmpty(Queue &q)
{
      if(q.front->next==NULL)//队空
        return true;
      else
        return false;
}

判断是否队满

因为是链式存储结构,不用预先设定需要多少的存储空间来保存数据,都是现用现配,空间利用效率比较高。所以只要一个数据我们就可以直接进队,不必考虑是否会队满。

进队

void Push(Queue &q,Elemtype e)
{
    LinkNode qtr;
    qtr->data=e;  
    qtr->next=NULL;
    if(IsEmpty(q))//先判断是否为空栈,如果为空栈要对队头指针一起修改;
       q.front->next = qtr;
    q.rear->next = qtr;
    q.rear =  qtr;
}

出队

bool Pop(Queue &q,Elentype &e)
{
    LinkNode qtr;
    if(IsEmpty())
        return false;
    else
    {
        qtr=q.front->next;//先用qtr保存要出队的结点;
        q.front->next=qtr->next;//修改队头指针;
        e = qtr->data;
        delete qtr;//删除结点;
    }    
}

取队头元素


bool GetFront(Queue q.Elemtype &e)
{
     if(IsEmpty(q))//取队头要先判断是否为空栈;
        return false;
     else
        e= q.front->next->data;
     return true;
}

销毁顺序队

void DestroyQueue(Queue &q)
{ 
    LinkNode qtr;
    while(q.front!=NULL)
    {
          qtr = q.front;//str保存当前删除的结点
          q.front = q.front->next;//s指向下一个需要删除的结点;
          delete qtr;
    }
}

1.3.3环形队列

由于队列进行插入和删除的操作不在同一端口进行,所以在顺序链中,当队尾指针rear指向数组的最后一个位置作时,存在队头指针front不在数组的第一个位置上,也就是front!=0,队中还存在若干空位置,这种情况我们称为假溢出。所以,为了提高空间的利用率,我们引入一个特殊的队列————循环队列。

顺序队结构体定义

typedef struct 
{
    Elemtype data[MaxSize];
    int front;
    int rear;
}Queue;

初始化

void InitQueue(Queue &q)
{
   front = rear = 0;//指向0位置;
}

判断是否为空队

bool IsEmpty(Queue &q)
{
    if(front == rear)
       return true;
    else
       return false;
}

判断是否队满

bool IsFull(Queue &q)
{
    if((q.rear+1)%MaxSize == q.front)
        return true;
    else
        return false;
}

进队

bool Push(Queue &q, Elemtype e)
{
     if(IsFull(q))//先判断是否队满;
          return false;
     else
     {
          q.data[q.rear] = e;//先保存e,再移动rear指针,所以操作完的rear指针指向的是下一个需要插入的位置,也就是队尾元素的下一个位置。
          q.rear = (q.rear+1)%MaxSize;
     }
     return true;
}

出队

bool Pop(Queueu &q, Elemtype &e)
{
     if(IsEmpty(q))
         return false;
     else
         e = q.data[q.front];
         q.front = (q.front+1)%MaxSize;//操作完的front指向的是队头元素。
}

1.3.4 C++容器:queue


头文件:#include <queue>
q.push(x);将x插入到队列末端,成为新的队尾元素;
q.pop();弹出队列的第一个元素,注意!!这里不返回被弹出元素;
q.front();返回队头元素;
q.back();返回队尾元素;
q.empty();当队空是,返回true;
q.size();返回队列的元素个数;

1.3.5队列应用

7-5 jmu-报数游戏

  • 思路:
    定义队列,将游戏人数按编号依次进队,定义变量i循环自增,
    用i求余m来判断要出队的是几号,i求余m=0时,输出号数,再将此数出队,求余m不等于0时,
    定义临时变量k来存放这个数,再将其出队,之后再让k进队构成循环,直至队列为空。


2.PTA实验作业(4分)

2.1 符号配对

2.1.1 解题思路及伪代码

  • 伪代码
使用map容器将符号进行配对;
while(str[i])
{
    if(str[i]==左括号)
        s.push(str[i])//左括号进栈;
    else if(str[i]==右括号)
    {
        if(s.empty())//栈空;
             缺少左括号,不匹配,退出循环;
        else
        {
             if(左右括号匹配)
                s.pop();//弹出栈顶元素
             else//不匹配
                退出循环;
         }
     }
     读取下一字符;   
}



2.1.2 总结解题所用的知识点

1.使用C++容器map
2.左右符号配对的判断条件

2.2 银行业务队列简单模拟

2.2.1 解题思路及伪代码

  • 伪代码
输入n的数值
for i=1 to n
输入一个数num;
if(n为奇数)进入q1队列
else 进入q2队列
while(q1,q2均不为空时)
输出q1中的第一个数, 输出后让该数出队列   

if(q1出队列的次数为偶数倍)
        q2出队一个元素
end while

while(q1不为空)
    将q1中剩余元素输出 
while(q2不为空)
    将q2中剩余元素输出 
return 0;

  • 代码



2.2.2 总结解题所用的知识点

1.queue 模板运用
2.考虑队列空满情况
3.注意数据输出的格式

3.阅读代码(0--1分)

3.1 题目及解题代码

题目


解题代码

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

3.2.1 该题的设计思路

先排身高更高的,这是要防止后排入人员影响先排入人员位置
每次排入新人员[h,k]时,已处于队列的人身高都>=h,所以新排入位置就是people[k]
时间复杂度:O(N)。 理由:循环地从头读取people,根据people[i][1]也就是k,插入list
空间复杂度:O(N)。 理由:这里将完成所有插入操作的list重建为vector返回

3.2.2 该题的伪代码

class Solution {
public:
        先将people按照身高降序排序,
        又由于每次插入的位置是k,所以相同身高需要按k升序排序,否则插入位置会越界
        确定循环次数
        新建容器tmp用来存放数据
        // 循环插入
        for i = 0 to len
            使用advance()找到应插入位置
            插入数据
        end for
        // 重建vector返回
        将完成所有插入操作的list重建为vector返回
    }
};

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

3.3.1优势:

并没有直接使用vector,而是将完成所有插入操作的list重建为vector,这样的操作使得整个代码的运行效率大大提高
先利用了sort函数进行排序,大大减少了后续代码的执行的难度.

3.3.2难点:

每次排入新人员[h,k]时,如何确定新排入的位置
相同身高需要按k升序排序,否则插入的位置会越界

原文地址:https://www.cnblogs.com/chtdeboke/p/14613976.html