【考研】栈和队列基础算法代码

前言

本文内容源于对王道书籍中栈和队列知识点的笔记和总结。主要以习题方式熟悉栈和队列的基本操作(包含初始化、判栈空、进栈出栈等基本运算的实现)。方便考研复习栈和队列。

可搭配以下链接一起学习:

【考研】栈在表达式求值中的应用(真题分析)_住在阳光的心里的博客-CSDN博客

【2023考研】数据结构常考应用典型例题(含真题)_住在阳光的心里的博客-CSDN博客

目录

前言

一、栈

(一)顺序栈基本运算的实现

(二)习题

二、队列

(一)循环队列的操作

(二)链式队列的基本操作

(三)习题


一、栈

(一)顺序栈基本运算的实现

//初始化
void InitStack(SqStack &S){
    S.top = -1;   //初始化栈顶指针为 -1 
}

//判栈空
bool StackEmpty(SqStack S){
    if(S.top == -1)
        return true;   //栈空   
    else
        return false;  //栈不空   
}

//进栈
bool Push(SqStack &S, ElemType &x){
    if(S.top == maxsize - 1)
        return false;   //栈满报错

    S.data[++S.top] = x;  //先栈顶指针加1,再进栈
/*若初始化 S.top = 0,即 top 指向栈顶元素的下一位置
则进栈改为 S.data[S.top++] = x; 
出栈改为 x = S.data[--S.top];
*/

    return true;    
}

//出栈
bool Pop(SqStack &S, ElemType &x){
    if(S.top == -1)
        return false;   //栈空报错

    x = S.data[S.top--];  //先出栈,再栈顶指针减1
    return true;    
}

//读栈顶元素
bool GetTop(SqStack S, ElemType &x){
    if(S.top == -1)
        return false;   //栈空报错

    x = S.data[S.top];  //x 记录栈顶元素
    return true;
}

(二)习题

1、设单链表的表头指针为 L,结点结构由 data 和 next 两个域构成,其中 data 域为字符型。试设计算法判断该链表的全部 n 个字符是否中心对称。例如 xyx、xyyx 都是中心对称。

解:(1)算法思想:使用栈来判断链表中的数据是否中心对称。让链表的前一半元素依次入栈。在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两个元素比较,若相等,则将链表中的下一个元素与栈中再弹出的元素比较,直至链表到尾。

这时若栈是空栈,则得出链表中心对称的结论;否则,当链表中的一个元素与栈中弹出元素不等时,结论为链表非中心对称,结束算法的执行。

(2)代码如下

//单链表结点类型
typedef struct LNode{
    char data;
    struct LNode *next;  
}LNode, *LinkList;

//判断带头结点的单链表的全部 n 个字符是否中心对称
int dc(LinkList L, int n){
    int i;
    char s[n/2];   // s 是字符栈
    LNode *p = L->next;   // p 是链表的工作指针,指向待处理的当前元素

    for(i = 0; i < n/2; i++){   //链表的前一半元素进栈
        s[i] = p->data;
        p = p->next;
    }

    i--;  //恢复最后的 i 值 

    if(n % 2 == 1)   //如果 n 是奇数,中心结点字符不必比较,移动指针到下一字符开始比较
        p = p->next;

    while(p != NULL && s[i] == p->data){  //检测是否中心对称
        i--;   // i 充当栈顶指针
        p = p->next;
    }

    if(i == -1)   //栈为空栈
        return 1;   //链表中心对称
    else
        return 0;   //链表不中心对称
}

 2、设有两个栈 s1 和 s2 都采用顺序栈方式,并共享一个存储区 [0, ... , maxsize - 1] ,为了尽量利用空间,减少溢出的可能,可采用栈顶相向、迎面增长的存储方式。试设计 s1, s2 有关入栈和出栈的操作算法。

解:(1)算法思想:两个栈共享向量空间,将两个栈的栈底设在向量两端,初始时,s1 栈顶指针为 - 1,s2 栈顶指针为 maxsize。两个栈顶指针相邻时为栈满。两个栈顶相向,迎面增长,栈顶指针指向栈顶元素。

(本题关键:两个栈入栈和退栈时的栈顶指针的计算。 s1 是通常意义下的栈,而 s2 栈入栈操作时,其栈顶指针左移(减1),退栈时,栈顶指针右移(加1)。此外,对所有栈的操作,都要注意“入栈判满,出栈判空” 的检查。

(2)代码如下

#define maxsize 100   //两个栈共享顺序存储空间所能达到的最多元素数,初始化为100
#define elemtp int    //假设元素类型为整型

typedef struct {
    elemtp stack[maxsize]; //栈空间
    int top[2];  // top 为两个栈顶指针
}stk;

stk s;   //全局变量

//入栈操作
// i 为栈号,i = 0 表示左边的 s1 栈,i = 1 表示右边的 s2 栈
// x 为入栈元素
// 入栈成功返回1,否则返回0
int push(int i, elemtp x){  
    if(i < 0 || i > 1){
        printf("栈号输入不对");
        exit(0);
    }
    
    if(s.top[1] - s.top[0] == 1){
        printf("栈已满\n");
        return 0;
    }

    switch(i){
        case 0: s.stack[++s.top[0]] = x; return 1; break;
        case 1: s.stack[--s.top[1]] = x; return 1;
    }
}


//退栈操作
// i 为栈号,i = 0 表示 s1 栈,i = 1 表示 s2 栈
// 退栈成功返回退栈元素,否则返回-1
elemtp pop(int i){
    if(i < 0 || i > 1){
        printf("栈号输入不对");
        exit(0);
    }

    switch(i){
        case 0: 
            if(s.top[0] == -1){
                printf("栈空\n");
                return -1;
            }
            else{
                return s.stack[s.top[0]--];
            }
        case 1:
            if(s.top[1] == maxsize){
                printf("栈满\n");
                return -1;
            }
            else{
                return s.stack[s.top[1]++];
            } 
    }
}

二、队列

(一)循环队列的操作

#define MaxSize 50    //定义队列中元素的最大个数
typedef struct{
    ElemType data[MaxSize];  //存放队列元素
    int front, rear;  //队头指针和队尾指针
}

//初始化
void InitQueue(SqQueue &Q){
    Q.rear = Q.front = 0;  //初始化队首、队尾指针
}

//判队空
bool IsEmpty(SqQueue Q){
    if(Q.rear == Q.front)   //队空条件
        return true;
    else
        return false;
}

//入队
bool EnQueue(SqQueue &Q, ElemType x){
    if((Q.rear + 1) % MaxSize == Q.front)   //队满则报错
        return false;

    Q.data[Q.rear] = x;   
    Q.rear = (Q.rear + 1) % MaxSize;   //队尾指针加1取模

    return true;
}

//出队
bool DeQueue(SqQueue &Q, ElemType &x){
    if(Q.rear == Q.front)   //队空则报错
        return false;

    x = Q.data[Q.front];   
    Q.front = (Q.front + 1) % MaxSize;   //队头指针加1取模

    return true;
}

(二)链式队列的基本操作

typedef struct{   //链式队列结点
    ElemType data;
    struct LinkNode *next;  
}LinkNode;

typedef struct{  //链式队列
    LinkNode *front, *rear;    //队列的队头和队尾指针
}LinkQueue;

//初始化
void InitQueue(LinkQueue &Q){
    Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));   //建立头结点
    Q.front->next = NULL;   //初始化空
}

//判队空
bool IsEmpty(LinkQueue Q){
    if(Q.front == Q.rear)
        return true;
    else
        return false;
}

//入队
void EnQueue(LinkQueue &Q, ElemType x){
    LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode)); 
    s->data = x;   //创建新结点,插入到链尾
    s->next = NULL;

    Q.rear->next = s;
    Q.rear = s;
}

//出队
void DeQueue(LinkQueue &Q, ElemType &x){
    if(Q.front == Q.rear)   //队空则报错
        return false;    

    LinkNode *p = Q.front->next;

    x = p->data;
    Q.front->next = p->next;
    if(Q.rear == p)
        Q.rear = Q.front;    //若原队列只有一个结点,删除后变空
    free(p);

    return true;
}

(三)习题

 1、若希望循环队列中的元素都能得到利用,则需设置一个标志域 tag,并以 tag 的值为 0 或 1 来区分队头指针 front 和队尾指针 rear 相同时的队列状态是“空”还是“满”。试编写与此结构相应的入队和出队算法。

解:(1)算法思想:在循环队列的类型结构中,增设一个 tag 的整型变量,进队时置 tag 为1,出队时置 tag 为 0。(因为只有入队操作可能导致队满,也只有出队操作可能导致队空)

队列 Q 初始时,置 tag = 0、front = rear = 0。这样队列的 4 要素如下:

队空条件 Q.front == Q.rear Q.tag == 0
队满条件 Q.front == Q.rear Q.tag == 1
进队操作

Q.data[Q.rear] = x;

Q.rear = (Q.rear+1)%MaxSize;

Q.tag = 1
出队操作

x = Q.data[Q.front];

Q.front = (Q.front+1)%MaxSize;

Q.tag = 0

(2)代码如下

//设 "tag" 法的循环队列,入队算法
int EnQueue1(SqQueue &Q, ElemType x){
    if(Q.front == Q.rear && Q.tag == 1)  //队满
        return 0; 

    Q.data[Q.rear] = x;
    Q.rear = (Q.rear + 1) % MaxSize;  
    Q.tag = 1;   //可能队满

    return 1;
}

//设 "tag" 法的循环队列,出队算法
int DeQueue1(SqQueue &Q, ElemType &x){
    if(Q.front == Q.rear && Q.tag == 0)  //队空
        return 0; 

    x = Q.data[Q.front];
    Q.front = (Q.front + 1) % MaxSize;
    Q.tag = 0;   //可能队空

    return 1;
}

2、Q 是一个队列,S 是一个空栈,实现将队列中的元素逆置的算法。

解:(1)算法思想:由于对队列的一系列操作不可能将其中的元素逆置,而栈可以将入栈的元素逆序提取出来,因此我们可以让队列中的元素逐个出队列,入栈;全部入栈后再逐个出栈,入队列。

(2)代码如下

void Inverser(Stack &s, Queue &q){
    while(!QueueEmpty(q)){
        x = DeQueue(q);   //队列中全部元素依次出队
        Push(s, x);   //元素依次入栈
    }

    while(!StackEmpty(s)){
        Pop(s, x);   //栈中元素依次出栈
        EnQueue(Q, x);   //再入队
    }
}

你可能感兴趣的:(考研,C++,数据结构,考研,学习,数据结构,算法,c++)