Data Structure·Chapter 3 [Stack and Queue]

stack

sequence stack

A stack is a linear list that limits insertion or deletion operations to only one end of the list. One end of the insertion and deletion is usually called the top of the stack (top), and the other end is called the bottom of the stack (bottom).

typedef struct{
    
    
        DataType  data[StackSize];
        int  top;
}SeqStack;

Sequential stack - completed witharray (during the exam, you have to remember what the corresponding nouns mean when you see them)

Basic operations

Insert image description here

signal

Insert image description here
When there is no element, top=-1, the bit order of the first element of the stack is 0

Basic operation implementation

push to stack

void Push(seqStack &S, DataType x)
{
    
        if(StackFull(S))
       Error(“Stack overflow”);
      S.data[++S->top]=x;
}

pop

DataType Pop(seqStack &S)
{
    
        if(StackEmpty(S))
       Error(“Stack underflow”);
     return  S.data[S->top--];
}

chain stack

typedef struct Stacknode{
    
    
     DataType data
     struct stacknode *next
}StackNode;

typedef struct{
    
    
        StackNode *top; //栈顶指针
}LinkStack;

Implementation of basic operations

push to stack

void Push(LinkStack &S, DataType x){
    
    
     StackNode *p=(StackNode*)malloc(sizeof(StackNode));
     p->data=x;
     p->next=S->top;
     S->top=p;
}

pop

DataType Pop(LinkStack &S){
    
      
   DataType x;
   StackNode *p=S->top;
   if(StackEmpty(S))Error(“Stack underflow”);
   x=p->data;
   s->top=p->next;
   free(p);
   return x;
}

It should be noted that: p − > n e x t p->next p>nextDirected downward element< /span>以栈顶为上,栈底为下

Comparison of sequential stack and chain stack

Time efficiency:

  • All operations require constant time
  • Sequential stacks and chained stacks are indistinguishable in terms of time efficiency.

Space efficiency:

  • The sequence stack must specify a fixed length
  • The length of the chain stack is variable, but it increases structural overhead

maze problem

Sequential stack implementation

// 定义迷宫中通道块的数据结构
typedef struct {
    
    
    int ord;            // 通道块在路经上的“序号”
    PosType seat;        // 通道块在迷宫中的“坐标位置”
    int di;                // 从此通道块走向下一通道块的“方向”
} SElemType;

// 寻找从起点 start 到终点 end 的路径
Status MazePath(MazeType maze, PosType start, PosType end) {
    
    
    SqStack S;
    InitStack(S);        // 初始化栈

    PosType curpos = start;    // 设定“当前位置”为“入口位置”
    int curstep = 1;           // 探索第一步
    
    do {
    
    
        if (Pass(curpos)) {
    
    
            // 当前位置可以通过,即是未曾走到过的通道块
            FootPrint(curpos);                    // 留下足迹
            SElemType e = {
    
    curstep, curpos, 1};    // 新建通道块
            Push(S, e);                           // 加入路径
            if (curpos == end) {
    
    
                return (TRUE);                    // 到达出口(终点)
            }
            curpos = NextPos(curpos, 1);           // 下一位置是当前位置的东邻
            curstep++;                            // 探索下一步
        } else {
    
    
            // 当前位置不能通过
            if (!StackEmpty(S)) {
    
    
                SElemType e;
                Pop(S, e);
                while (e.di==4 && !StackEmpty(S)) {
    
    
                    MarkPrint(e.seat); Pop(S, e);     // 留下不能通过的标记,并退回一步
                }
                if (e.di < 4) {
    
    
                    e.di++;
                    Push(S, e);                          // 换下一个方向探索
                    curpos = NextPos(e.seat, e.di);      // 设定当前位置是该新方向上的相邻块
                }
            }
        }
    } while (!StackEmpty(S));
    return (FALSE);    // 无法从起点到达终点
}

Expression evaluation

Operation priority

Insert image description here

OperandType EvaluateExpression()  {
    
    
    //算术表达式求值的算符优先算法。设OPTR和OPND
    //分别为运算符栈和运算数栈,OP为运算符集合。
    InitStack (OPTR); Push(OPTR,’#’);
    InitStack (OPND); c=getchar();
    while (c!=‘#’||GetTop(OPTR)!=‘#’) {
    
    
       if (!In (c, OP)) {
    
     Push (OPND, c); c= getchar();}
                                                     //不是运算符则进栈
       else 
         switch (Precede( GetTop (OPTR), c)){
    
    
           case<:                               //栈顶元素优先权低
                   Push (OPTR, c); c= getchar();
                   break;
           case=:    //脱括号并接受下一字符
                        Pop (OPTR, c); c= getchar();
                        break;
           case>:          //退栈并将运算结果入栈
                        Pop (OPTR, theta);
                        Pop (OPND, b); Pop (OPND, a);
                        Push (OPND, Operate (a, 
                        theta, b)); 
                        break;
           }//switch
    }//while
    return GetTop(OPND);
}//EvaluateExpression

recursion

  • The problem definition is recursive. For example, the factorial function in mathematics, the second-order Fibonacci function, etc.
  • Some data structures, such as binary trees, generalized tables, etc., due to the inherent recursive characteristics of the structure itself, their operations can be described recursively.
  • Some problems, although the problem itself does not have an obvious recursive structure, are simpler to solve recursively than iteratively. Such as the Eight Queens Problem, Hanoi Tower Problem, etc.

There's nothing much to say about this part

queue

first in first out

chain queue

 typedef struct QNode {
    
    // 结点类型
    QElemType      data;
    struct QNode  *next;
  } QNode, *QueuePtr;
typedef struct {
    
     // 链队列类型
    QueuePtr  front;  // 队头指针
    QueuePtr  rear;   // 队尾指针
} LinkQueue;

Schematic diagram

Insert image description here
It is easy to know: the next pointer of the queue points to the element after the current element in the queue

Basic operations

Insert image description here

Queue

Status EnQueue (LinkQueue &Q, QElemType e) {
    
    
    // 插入元素e为Q的新的队尾元素
    p = (QueuePtr) malloc (sizeof (QNode));
    if(!p) exit (OVERFLOW);  //存储分配失败
    p->data = e;   p->next = NULL;
    Q.rear->next = p;    Q.rear = p;
    return OK;
}

Among them, when writing the algorithm, you should also pay attention to adding statements similar to if(!p) exit (OVERFLOW); //存储分配失败 to determine exceptions

Dequeue

Status DeQueue (LinkQueue &Q, QElemType &e) {
    
    
  // 若队列不空,则删除Q的队头元素,
  //用 e 返回其值,并返回OK;否则返回ERROR
   if (Q.front == Q.rear)    return ERROR;
   p = Q.front->next;   e = p->data;
   Q.front->next = p->next;
   if (Q.rear == p)  Q.rear = Q.front;
   free (p);      return OK;
}

Judgment abnormalityif (Q.front == Q.rear) return ERROR;
Special judgment in this stepif (Q.rear == p) Q.rear = Q.front;

Notep = Q.front->next;, the chain queue has a head node2023.6.16复习

sequential queue

cycle

#define MAXQSIZE  100  //最大队列长度
  typedef struct {
    
    
    QElemType *base; // 动态分配存储空间
    int front;   // 头指针,若队列不空,
                 //  指向队列头元素
    int  rear;  // 尾指针,若队列不空,
          //指向队列尾元素 的下一个位置
  } SqQueue;

这り的 r e a r rear rearis-orientedthe elemental Bottom position
Iron column r e a r rear reardirection队尾element

Basic operations

Generally the same chain queue

Queue

Status EnQueue (SqQueue &Q, ElemType e) {
    
       
     // 插入元素e为Q的新的队尾元素
    if ((Q.rear+1) % MAXQSIZE == Q.front) return ERROR;     //队列满
    Q.base[Q.rear] = e;
    Q.rear = (Q.rear+1) % MAXQSIZE;
    return OK;
}

Understand this carefully, (Q.rear+1) % MAXQSIZE means (combined with the following Q.rear = (Q.rear+1) % MAXQSIZE;)

Dequeue

Status DeQueue (SqQueue &Q, ElemType &e) {
    
     
    // 若队列不空,则删除Q的队头元素,
   // 用e返回其值,并返回OK;  否则返回ERROR
    if (Q.front == Q.rear)  return ERROR;
    e = Q.base[Q.front];
    Q.front = (Q.front+1) % MAXQSIZE;
    return OK;
}

Also note:Q.front = (Q.front+1) % MAXQSIZE;

Banking Problem (Discrete Event Simulation)

Suppose a bank has four windows open, and customers have been entering the bank since the bank opened in the morning. Each window can only receive one customer at a time. For customers who have just entered the bank, if the clerk at a certain window is idle, he can go forward to handle the business; on the contrary, if all four windows are occupied by customers, he will Get behind the line with the smallest number of people. Now we need to prepare a program to simulate this kind of business activity of the bank and calculate the average time that customers stay in the bank in a day.

Ideas

  • The general idea is event-driven

code

typedef struct {
    
    
         int OccurTime;  //事件发生时刻
         int NType;   //事件类型,0表示到达事件,1至4表示四个窗口的离开事件
}Event, ElemType;     //事件类型,有序链表LinkList的数据元素类型
typedef LinkList EventList  //事件链表类型,定义为有序链表
typedef struct {
    
    
         int ArrivalTime;            //到达时刻
         int Duration;              //办理事务所需时间
}QElemType;                        //队列的数据元素类型

EventList     ev;                        //事件表
Event         en;                       //事件
LinkQueue     q[5];                    //4个客户队列   
QElemType    customer;           //客户记录
int   TotalTime, CustomerNum;   //累计客户逗留时间,客户数
int com (Event a, Event b);    //依事件a的发生时刻<或=或>事件b的发生时刻分别返回-1或0或1

void OpenForDay() {
    
                         //初始化操作
       TotalTime=0;   CustomerNum=0;   //初始化累计时间和客户数为0
       InitList (ev);                 //初始化事件链表为空表
       en.OccurTime=0;  en.NType=0;  //设定第一个客户到达事件 
       OrderInsert (ev, en, cmp);   //插入事件表
       for (i=1; i<=4; ++i)   InitQueue (q[i]); //置空队列
}//OpenForDay 

void CustomerArrived()  {
    
    
    //处理客户到达事件,en.NType=0。
    ++CustomerNum;
    Random (durtime, intertime);  //生成随机数
    t=en.OccurTime+ intertime; //下一客户到达时刻
    if (t<CloseTime)        //银行尚未关门,插入事件表
      OrderInsert (ev, (t, 0), cmp);
    i=Minimum (q);                        //求长度最短队列
    EnQueue (q[i], (en.OccurTime, durtime));
    if(QueueLength (q[i])==1)
        OrderInsert (ev, (en.OccurTime + durtime, i), cmp);  //设定第i队列的一个离开事件并插入事件表
}// CustomerArrived

void CustomerDeparture()  {
    
    
    //处理客户离开事件,en.NType>0。
    i=en.NType;  DelQueue (q[i], customer);          //删除第i队列的排头客户
    TotalTime+=en.OccurTime- customer.Arrivaltime; //累计客户逗留时间
    if (!QueueEmpty(q[i])) //设定第i队列的一个离开事件并插入事件表
        GetHead (q[i], customer);
        OrderInsert (ev, (en.OccurTime + custom.Duration, i), (*cmp)());                  
}// CustomerDeparture

void Bank_ Simulation (int CloseTime) {
    
    
        OpenForDay();
        while (! ListEmpty(ev) ) {
    
       //初始化
           DelFirst (GetHead (ev), p); 
           en=GetCurElem(p);
           if(en.NType==0) CustomerArrived();    //处理客户到达事件
           else CustomerDeparture() ; //处理客户离开事件
   }                //计算平均逗留时间
 printf(“The Average Time is %f\n”, (float)TotalTime/CustomerNum);
}// Bank_ Simulation 

exercise

3.2

Briefly describe the difference between stack and linear list

Solution
A linear list is a finite sequence of data elements with the same characteristics. A stack is a linear list that restricts insertion or deletion operations only to the end of the list

3.11

Briefly describe the similarities and differences between the two data types of queue and stack

Queues and stacks are both linear lists with limited operations, and both belong to the logical structure of linear lists. The difference is that queue insertion is done at the end of the queue, and deletion is done at the head end of the queue; while stack insertion and deletion can only be done at the top of the stack.

3.18

Insert image description here
Judging from the title, this question is not difficult in terms of thinking. But you need to pay attention to the details of writing the algorithm:

  • InitStack(s)
  • ElemType x
  • Finallyif(s.size!=0) return False; (the number of left and right brackets varies). I think that I personally may not have considered this detail after writing the while and switch statements. It is worth writing it again< /span>

3.21

Insert image description here
Insert image description here

3.32

Try to write a query using a circular queue k k k Fibonacci sequence middle n + 1 n+1 n+1 ( f 0 , f 1 , … , f n ) (f_0, f_1 ,…, f_n) (f0f1fnThe algorithm of ) is required to satisfy: f n ≤ m a x , f n + 1 > m a x f_n≤max, f_{n+1} >max fnatx,fn+1>max, inside m a x max max is an agreed constant. (Note: The capacity of the circular queue used in this question is only k k k, then at the end of the algorithm execution, the elements left in the circular queue should be the required k k kThe last Fibonacci sequence k k k f n − k + 1 … … f n f_{n-k+1}……f_n fnk+1……fn

long Fib_CirQueue(long Q[], int k, int& rear, int& n, long max) {
    
    
    // 使用循环队列Q[k]计算k阶斐波那契数fn,要求fn是不超过max的最大的斐波那契数,
    // 函数返回计算结果,rear是队尾指针,指向实际队尾位置。
    long sum; // sum为计算过程中的中间变量,记录前k项斐波那契数的和
    int i; // 循环计数器

    // 给前0~k-1项赋初值
    for (i = 0; i < k - 1; i++) {
    
    
        Q[i] = 0;
    }
    Q[k - 1] = 1; // 第k项斐波那契数初始化为1
    rear = n = k - 1; // 队尾指针,指示实际队尾位置;n为当前fj计数,初始值为k-1

    sum = 0; // 初始化sum为0,开始计算第k项斐波那契数

    while (1) {
    
    
        // 累加前k项的斐波那契数的值
        for (i = 0; i < k; i++) {
    
    
            sum = sum + Q[(rear - i + k) % k];
        }
        if (sum > max) {
    
    
            break; // 若计算出来的斐波那契数超过了max,则退出循环
        }
        n++; // 计数器自增1,记录当前计算到了第几项斐波那契数
        rear = (rear + 1) % k; // 队列中仅存入到fn项,队尾指针向后移动
        Q[rear] = sum; // 将计算结果存入循环队列中,并取代已无用的项
    }
    return Q[rear]; // 返回计算结果
}

This question is a bit complicated, mainly k k kThe concept of Fibonacci sequence

Guess you like

Origin blog.csdn.net/qq_61786525/article/details/130907563