紫薇星上的数据结构(4)

昨天整理了链表的知识点,可以发现不管是单链表、循环链表、静态链表还是双向链表,原理都差不多,大家要多敲几遍代码。今天我们来整理 栈和队列 的知识点并做一个小实例看一下。


4.1初识栈

栈(stack)是限定仅在表尾插入和删除操作的线性表,允许插入和删除的一端称为“栈顶(top)”,另一端称为“栈底(bottom)”,不含任何数据元素的栈被称为空栈;

栈的特点:先进后出、后进先出。

注意:

  • 栈又被称为后进先出(Last In First Out)结构的线性表,简称LIFO结构;
  • 栈的插入操作,称为进栈,也称压栈、入栈(push);
  • 栈的删除操作,称为出栈,也成为弹栈(pop)。

可以将栈想象为一口井,入栈就是往井里扔东西,每扔一个都会落在前一个的上面,出栈就是从井里取东西,取的时候只能从最上面的开始取。那么当我们初始化一个栈时,栈顶和栈底都是一样的,当开始入栈的时候栈顶就会移动到入账的数据处,而出栈时也会从栈顶数据开始取出。

栈的抽象数据类型

ADT 栈 (Stack)
Data
    栈的数据对象集合为{a1, a2, a3, ..., an},每个元素的类型均为DataType。其中,除第一个元素a1外,每个元素有且只有一个
直接前驱元素,除了最后一个元素an外,每个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
Operation
    initStack(*S);        初始化操作,建立一个空栈S
    destoryStack(*S);     若栈存在,则销毁他
    stackEmpty(S);        若栈为空,返回true;否则返回false
    clearStack(*S);       将栈清空
    getTops(S, *e);       若栈存在且非空,用e返回S的栈顶元素
    push(*S, e);          若栈存在,插入新元素e到栈S中并称为栈顶元素
    pop(*S, *e);          删除栈S中栈顶元素,并用e返回其值
    stackLength(L);       返回栈S的元素个数
endADT

可以发现栈的操作与线性表的操作差不多,因为栈本身就是一个线性表,限制那个表的顺序存储结构与链式存储结构,对栈来说也同样适用。

4.2栈的操作

入栈与出栈

与之前一样,我们使用代码来实现一下刚才所说的操作,首先建立Element.h与SequenceStack.h头文件,这两个头文件中分别放一些我们要用到的数据和顺序栈的定义与操作,在Element.h中添加:

#ifndef ELEMENT_H_INCLUDED
#define ELEMENT_H_INCLUDED

#define MAX_SIZE 255
#define TRUE 1
#define FALSE 0

typedef struct{
    int id;
    char *name;
}ElementType;

#endif // ELEMENT_H_INCLUDED

在SequenceStack.h中添加:

#ifndef SEQUENCESTACK_H_INCLUDED
#define SEQUENCESTACK_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include "Element.h"

/**栈的顺序存储方式定义*/
typedef struct SeqStack{
    ElementType elements[MAX_SIZE]; //顺序栈中用来存放数据元素的数组
    int top; //栈顶下标,如果为-1,这栈为空
    int length; //当前栈的长度

}SeqStack;

/**栈的初始化*/
void InitSeqStack(SeqStack *seqStack);

/**向栈中压入元素,返回压入的结果true/false*/
int PushSeqStack(SeqStack *seqStack, ElementType element);

/**以返回指针的方式返回出栈元素,返回值为出栈的结果true/false*/
int PopSeqStack(SeqStack *seqStack, ElementType *element);

#endif // SEQUENCESTACK_H_INCLUDED

然后我们新建一个SequenceStack.c文件,在里面实现操作:

#include "SequenceStack.h"

/**栈的初始化*/
void InitSeqStack(SeqStack *seqStack){
    seqStack->top = -1;
    seqStack->length = 0;
}

/**向栈中压入元素,返回压入的结果true/false*/
int PushSeqStack(SeqStack *seqStack, ElementType element){
    if(seqStack->top == MAX_SIZE - 1){
        //如果指向最后一个元素,栈满,压栈失败
        printf("满栈,压栈操作失败!");
        return FALSE;
    }
    //如果不满,可以压栈
    seqStack->top ++; //栈顶指针+1,一边加入新的元素
    //将新插入的元素赋值给栈顶
    seqStack->elements[seqStack->top] = element;
    seqStack->length ++;
    return TRUE;
}

/**以返回指针的方式返回出栈元素,返回值为出栈的结果true/false*/
int PopSeqStack(SeqStack *seqStack, ElementType *element){
    if(seqStack->top == -1){
        printf("空栈,出栈失败!");
        return FALSE;
    }
    //返回栈顶指向的元素
    *element = seqStack->elements[seqStack->top];
    seqStack->top --;
    seqStack->length --;
    return TRUE;
}

这样就可以实现了入栈与出栈了,我们在main.c文件中实现一下:

#include <stdio.h>
#include <stdlib.h>
#include "Element.h"
#include "SequenceStack.h"

ElementType datas[] = {
    {1, "紫薇一号"},
    {2, "紫薇二号"},
    {3, "紫薇三号"},
    {4, "紫薇四号"},
    {5, "紫薇五号"}
};

void TestStack();

int main()
{
    //printf("Hello world!\n");
    TestStack();
    return 0;
}

void TestStack(){
    SeqStack *stack = (SeqStack*)malloc(sizeof(SeqStack));
    ElementType *element = (ElementType*)malloc(sizeof(ElementType));
    InitSeqStack(stack);
    for(int i = 0; i < sizeof(datas) / sizeof(datas[0]); i++){
        printf("当前入栈:%d\t%s\n", datas[i].id, datas[i].name);
        PushSeqStack(stack, datas[i]);
    }
    PopSeqStack(stack, element);
    printf("当前出栈元素:%d\t%s\n", element->id, element->name);
    
    free(stack);
}

编译通过,运行结果如下:

当前入栈:1     紫薇一号
当前入栈:2     紫薇二号
当前入栈:3     紫薇三号
当前入栈:4     紫薇四号
当前入栈:5     紫薇五号
当前出栈元素:5 紫薇五号

Process returned 0 (0x0)   execution time : 0.032 s
Press any key to continue.

其他操作

还有一些操作比如清除栈、返回栈顶元素等我们也可以实现一下,同样先在SequenceStack.h中添加:

/**清空栈*/
void ClearSeqStack(SeqStack *seqStack);

/**返回栈顶元素*/
void GetSeqTopElement(SeqStack *seqStack, ElementType *element);

然后在SequenceStack.c实现操作:

/**清空栈*/
void ClearSeqStack(SeqStack *seqStack){
    seqStack->top = -1;
    seqStack->length = 0;
}

/**返回栈顶元素*/
void GetSeqTopElement(SeqStack *seqStack, ElementType *element){
    if(seqStack->top == -1){
        printf("空栈,栈顶元素为空!");
        element = NULL;
        return;
    }
    *element = seqStack->elements[seqStack->top];
}

现在我们在main.c中实现一下:

void TestStack(){
    SeqStack *stack = (SeqStack*)malloc(sizeof(SeqStack));
    ElementType *element = (ElementType*)malloc(sizeof(ElementType));
    InitSeqStack(stack);
    for(int i = 0; i < sizeof(datas) / sizeof(datas[0]); i++){
        printf("当前入栈:%d\t%s\n", datas[i].id, datas[i].name);
        PushSeqStack(stack, datas[i]);
    }
    PopSeqStack(stack, element);
    printf("当前出栈元素:%d\t%s\n", element->id, element->name);
    for(int i = 0; i < stack->length; i++){
        printf("当前栈内元素:%d\t%s\n", stack->elements[i].id, stack->elements[i].name);
    }
    GetSeqTopElement(stack, element);
    printf("当前栈顶元素:%d\t%s\n", element->id, element->name);
    printf("清除栈!");
    ClearSeqStack(stack);
    PopSeqStack(stack, element);
    free(stack);
}

编译通过,运行结果如下:

当前入栈:1     紫薇一号
当前入栈:2     紫薇二号
当前入栈:3     紫薇三号
当前入栈:4     紫薇四号
当前入栈:5     紫薇五号
当前出栈元素:5 紫薇五号
当前栈内元素:1 紫薇一号
当前栈内元素:2 紫薇二号
当前栈内元素:3 紫薇三号
当前栈内元素:4 紫薇四号
当前栈顶元素:4 紫薇四号
清除栈!空栈,出栈失败!
Process returned 0 (0x0)   execution time : 0.037 s
Press any key to continue.

4.3链栈

链栈的结构与链表相似,插入与删除都在链表的头部,所以链栈就是一个以top为头结点,从栈顶指向栈底的单链表,简单来说就是链栈从头添加与删除数据,单链表随处都可以插入数据。

链栈的出栈与入栈

我们使用代码来实现一下,首先建立LinkStack.h与LinkStack.c文件,在LinkStack.h中编写操作:

#ifndef LINKSTACK_H_INCLUDED
#define LINKSTACK_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include "Element.h"

/**定义链栈的结点*/
typedef struct StackNode{
    ElementType data; //结点中保存的元素
    struct StackNode *next; //只想下个结点的指针
}StackNode;

/**链栈结构*/
typedef struct LinkedStack{
    StackNode *top; //栈顶指针
    int length; //链栈的长度(元素个数)
}LinkedStack;

//初始化链栈
void InitLinkedStack(LinkedStack *linkedStack);

//压栈
int PushLinkedStack(LinkedStack *linkedStack, ElementType element);

//出栈
int PopLinkedStack(LinkedStack *linkedStack, ElementType *element);

#endif // LINKSTACK_H_INCLUDED

然后在LinkStack.c中编写操作:


#include "LinkStack.h"

//初始化链栈
void InitLinkedStack(LinkedStack *linkedStack){
    linkedStack->top = NULL;
    linkedStack->length = 0;
}

//压栈
int PushLinkedStack(LinkedStack *linkedStack, ElementType element){
    //创建一个新结点
    StackNode *newNode = (StackNode*)malloc(sizeof(StackNode));
    newNode->data = element;
    //新结点的next指向当前的栈顶
    newNode->next = linkedStack->top;
}

//出栈
int PopLinkedStack(LinkedStack *linkedStack, ElementType *element){
    if(linkedStack->top == NULL){
        printf("空栈!出栈失败!");
        return FALSE;
    }
    //返回栈顶元素
    *element = linkedStack->top->data;
    //记录出栈操作前的栈顶指针
    StackNode *node = LinkedStack->top;
    //栈顶指针向下移一位
    LinkedStack->top = linkedStack->top->next;
    //释放原栈顶空间
    free(node);
    linkedStack->length --;
    return TRUE;
}

在main.c中实现一下:

#include <stdio.h>
#include <stdlib.h>
#include "Element.h"
#include "SequenceStack.h"
#include "LinkStack.h"

ElementType datas[] = {
    {1, "紫薇一号"},
    {2, "紫薇二号"},
    {3, "紫薇三号"},
    {4, "紫薇四号"},
    {5, "紫薇五号"}
};

void TestStack();

void TestLinked();

int main()
{
    //printf("Hello world!\n");
    //TestStack();
    TestLinked();
    return 0;
}

void TestLinked(){
    LinkedStack *linkedStack = (LinkedStack*)malloc(sizeof(LinkedStack));
    ElementType *element = (ElementType*)malloc(sizeof(ElementType));
    InitLinkedStack(linkedStack);
    for(int i = 0; i < sizeof(datas) / sizeof(datas[0]); i++){
        printf("当前入栈:%d\t%s\n", datas[i].id, datas[i].name);
        PushLinkedStack(linkedStack, datas[i]);
    }
    PopLinkedStack(linkedStack, element);
    printf("当前出栈元素:%d\t%s\n", element->id, element->name);
}

编译通过,运行结果为:

当前入栈:1     紫薇一号
当前入栈:2     紫薇二号
当前入栈:3     紫薇三号
当前入栈:4     紫薇四号
当前入栈:5     紫薇五号
当前出栈元素:5 紫薇五号
Process returned 0 (0x0)   execution time : 0.030 s
Press any key to continue.

链栈的其他操作

还有一些操作比如清除栈、返回栈顶元素等我们也可以实现一下,同样先在SequenceStack.h中添加:

//清空栈
void ClearLinkedStack(LinkedStack *linkedStack);

//销毁栈
void DestroyLinkedStack(LinkedStack *linkedStack);

//返回栈顶元素
void GetLinkedTopElement(LinkedStack *linkedStack, ElementType *element);

然后在SequenceStack.c中添加:

//清空栈
void ClearLinkedStack(LinkedStack *linkedStack){
    StackNode *tempNode;
    while(linkedStack->top){
        tempNode = linkedStack->top;
        //栈顶指向下个元素
        linkedStack->top  = linkedStack->top->next;
        free(tempNode);
        linkedStack->length --;
    }
}

//销毁栈
void DestroyLinkedStack(LinkedStack *linkedStack){
    //先清空
    ClearLinkedStack(linkedStack);
    free(linkedStack);
    linkedStack = NULL;
}

//返回栈顶元素
void GetLinkedTopElement(LinkedStack *linkedStack, ElementType *element){
    if(linkedStack->top == NULL){
        printf("空栈,栈顶元素为空!");
        element = NULL;
        return;
    }
    element = linkedStack->top;
}

在main.c中实现一下:

void TestLinked(){
    LinkedStack *linkedStack = (LinkedStack*)malloc(sizeof(LinkedStack));
    ElementType *element = (ElementType*)malloc(sizeof(ElementType));
    InitLinkedStack(linkedStack);
    for(int i = 0; i < sizeof(datas) / sizeof(datas[0]); i++){
        printf("当前入栈:%d\t%s\n", datas[i].id, datas[i].name);
        PushLinkedStack(linkedStack, datas[i]);
    }
    PopLinkedStack(linkedStack, element);
    printf("当前出栈元素:%d\t%s\n", element->id, element->name);
    GetLinkedTopElement(linkedStack, element);
    printf("当前栈顶元素:%d\t%s\n", element->id, element->name);
    printf("清除栈!");
    ClearLinkedStack(linkedStack);
    PopLinkedStack(linkedStack, element);
    free(linkedStack);
}

编译通过,运行结果如下:

当前入栈:1     紫薇一号
当前入栈:2     紫薇二号
当前入栈:3     紫薇三号
当前入栈:4     紫薇四号
当前入栈:5     紫薇五号
当前出栈元素:5 紫薇五号
当前栈顶元素:5 紫薇五号
清除栈!空栈!出栈失败!
Process returned 0 (0x0)   execution time : 0.053 s
Press any key to continue.

4.4通过链栈实现五子棋的悔棋

我们可以通过链栈来实现五子棋的悔棋功能,但要实现五子棋功能,我们首先需要添加棋子的类型,在Element.h中添加操作:

#ifndef ELEMENT_H_INCLUDED
#define ELEMENT_H_INCLUDED

#define MAX_SIZE 255
#define TRUE 1
#define FALSE 0
#define WHITE 1
#define BLACK 2

typedef struct{
    int id;
    char *name;
}ElementType;

typedef struct{
    int x; //棋子的坐标
    int y;
    int type; //当前棋子类型(WHITE/BLACK)
}ChessMan;

#endif // ELEMENT_H_INCLUDED

由于使用链栈来操作,所以我们将刚才的链栈代码中所有的ElementType全部换为ChessMan,然后新建Gobang.h与Gobang.c文件,在Gobang.h中添加如下操作:

#ifndef GOBANG_H_INCLUDED
#define GOBANG_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include "Element.h"

#define BOARD_SIZE 15

/**棋盘数组*/
char *ChessBoard[BOARD_SIZE][BOARD_SIZE];

/**初始化/重置棋盘*/
void InitChessBoard();

/**打印棋盘*/
void PrintChessBoard();
#endif // GOBANG_H_INCLUDED

很简单,只需要有初始化棋盘和打印棋盘和功能就可以了,在Gobang.c文件中添加操作:

#include "Gobang.h"

/**初始化/重置棋盘*/
void InitChessBoard(){
    for(int i = 0; i < BOARD_SIZE; i++){
        for(int j = 0; j < BOARD_SIZE; j++){
            ChessBoard[i][j] = "十";
        }
    }
}

/**打印棋盘*/
void PrintChessBoard(){
    for(int i = 0; i < BOARD_SIZE; i++){
        for(int j = 0; j < BOARD_SIZE; j++){
            printf("%s", ChessBoard[i][j]);
        }
        printf("\n");
    }
}

这时候我们在main.c文件中添加旗子与操作,然后试着打印一下:

#include <stdio.h>
#include <stdlib.h>
#include "Element.h"
#include "SequenceStack.h"
#include "LinkStack.h"
#include "Gobang.h"

ChessMan chessArray[] = {
    {7, 7, BLACK},
    {8, 7, WHITE},
    {7, 6, BLACK},
    {8, 6, WHITE},
    {6, 7, BLACK}
};

void TestStack();

void TestLinkedStack();

int main(){
    TestLinkedStack();
    return 0;
}

void TestLinkedStack(){
    InitChessBoard();
    PrintChessBoard();
    LinkedStack *linkedStack = (LinkedStack*)malloc(sizeof(LinkedStack));
    //模拟落子过程
    for(int i = 0; i < 5; i++){
        //记录下子情况
        PushLinkedStack(linkedStack, chessArray[i]);
        //根据棋子类型修改棋盘
        if(chessArray[i].type == BLACK){
            ChessBoard[chessArray[i].y - 1][chessArray[i].x - 1] = "●";
        }else{
            ChessBoard[chessArray[i].y - 1][chessArray[i].x - 1] = "○";
        }
    }
    printf("落子后:\n");
    PrintChessBoard();
}

我们的操作就是将棋盘全部由“十”来表示,然后落子保存在链栈中并修改棋盘内容,这样就形成了下棋的操作,我们先试着打印一下棋盘,这里下的分别是“●”和“○”,显示在CSDN上有些偏差,但在编译器中是正常的:

CSDN显示有点偏差,我们直接用截图来代替

我们还可以试着将代码稍作修改,可以看到一步一步下的过程:

void TestLinkedStack(){
    InitChessBoard();
    PrintChessBoard();
    LinkedStack *linkedStack = (LinkedStack*)malloc(sizeof(LinkedStack));
    //模拟落子过程
    for(int i = 0; i < 5; i++){
        //记录下子情况
        PushLinkedStack(linkedStack, chessArray[i]);
        printf("按任意键下棋:\n");
        getchar();
        //根据棋子类型修改棋盘
        if(chessArray[i].type == BLACK){
            ChessBoard[chessArray[i].y - 1][chessArray[i].x - 1] = "●";
        }else{
            ChessBoard[chessArray[i].y - 1][chessArray[i].x - 1] = "○";
        }
        printf("落子后:\n");
        PrintChessBoard();
    }
}

这样我们按一下就可以看到根据之前的设置下了一步棋:

粘贴结果太长了就用截图显示一下叭

然后我们进行悔棋,悔棋时需要注意要将链栈中的栈顶元素出栈,然后将其位置用“十”代替:

void TestLinkedStack(){
    InitChessBoard();
    PrintChessBoard();
    LinkedStack *linkedStack = (LinkedStack*)malloc(sizeof(LinkedStack));
    //模拟落子过程
    for(int i = 0; i < 5; i++){
        //记录下子情况
        PushLinkedStack(linkedStack, chessArray[i]);
        printf("按任意键下棋:\n");
        getchar();
        //根据棋子类型修改棋盘
        if(chessArray[i].type == BLACK){
            ChessBoard[chessArray[i].y - 1][chessArray[i].x - 1] = "●";
        }else{
            ChessBoard[chessArray[i].y - 1][chessArray[i].x - 1] = "○";
        }
        printf("落子后:\n");
        PrintChessBoard();
    }
    //悔棋
    while(linkedStack->top){
        printf("按任意键悔棋:\n");
        getchar();
        ChessMan currChess;
        //出栈(悔棋)
        PopLinkedStack(linkedStack, &currChess);
        ChessBoard[currChess.y - 1][currChess.x - 1] = "十";
        PrintChessBoard();
    }
    DestroyLinkedStack(linkedStack);
    return ;
}

我们来看一下结果:

可以看到我们在下完了所有设置好的棋后就可以悔棋了。

4.5初识队列

队列(Queue)只允许在一端进行操作,而在另一端进行删除操作的线性表,队列是一种先进先出(First In First Out)的线性表,允许插入的一端称为队尾,允许删除的一端称为队头,同时要知道:队列是操作受限制的线性表。

与栈不同,栈是先进后出式,就像弹夹中的子弹,子弹压的越下面就越晚打出去;而队列就像它的名字一样,在排队时先排的人先被轮到。

队列的顺序存储结构

在队列的操作中也同样有插入与删除,我们称为入队与出队,我们用排队的方式来类比一下:在入队时从队尾入队,刚开始队头就是队尾,开始入队后队尾的指针就开始移到下一个空位置直到队伍最后一位;出队时从队头出队,开始出队后队头指针就开始移动直到队伍最后一位;所以当我们入队时只能从前往后排,而出队时也只能从前往后出,这就是队列的出队与入队的操作过程。

但这样的流程有个问题,因为并不是真的排队,人们不会“自觉地”向前移动,数据只会听从指针的召唤,所以如果在入队过程中有人出队再入队,那么直到入队结束出队后的空间就会一直空闲,而由于出队指针与入队指针都只会“从前往后”的移动,所以入队半途出队的空间就被“遗忘”了,直到入队结束,再入队显示“队列已满”的时候,其实在队列最前方还有空间。

所以使用顺序表或者数组来实现队列就有很大的缺点:队列已满,无法继续在队尾插入元素时数组仍有剩余空间,造成空间浪费(假溢出)。

队列的循环储存结构

那么如何解决这种问题呢?我们可以使用循环链表来实现队列,当入队到队尾时,因为是循环链表,所以队尾的指针指向的就是原来的队头,这时候如果中途有出队的情况也没关系,因为队头在出队的时候就已经空闲了,也就是可以被循环过来的队尾指向了;但我们并不知道何时队满,如果没有一个提示的话队尾就会一直循环覆盖,有可能将没有出队的元素也覆盖插入新的元素,那么如何处理队满和队空的情况呢?有两种方法:

  • 少用一个空间元素,即当空间大小为MAX_SIZE时,有MAX_SIZE - 1 个元素的时候就认为队满了;
  • 单独设置一个标识位以便区别队列是否满状态或者空状态。

如果将头指针表示为rear,尾指针表示为front,即可以表达为:

  • 队列为空:rear == front == 0;
  • 队列为满:(rear + 1) % MAX_SIZE == front。(例如:队列一共有6个位置(0-5),那么头结点为5,尾结点为0的时候就是满队列了,此时(5 + 1) % 6 = 0 == 尾指针)

我们用代码来表示一下秒首先新建SeqQueue.h和SeqQueue.c文件,在SeqQueue.h中编写定义和操作:

#ifndef SEQQUEUE_H_INCLUDED
#define SEQQUEUE_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include "Element.h"
#define STATE_OK 1
#define STATE_FALSE 0
typedef int State; // 用整型来表示状态

/**循环队列结构*/
typedef struct{
    ElementType data[MAX_SIZE];
    int front; //队头指针
    int rear; //队尾指针
    int length; 队列长度
}SeqQueue;

//初始化
void InitSeqQueue(SeqQueue *seqQueue);

//返回队列是否为空
State IsSeqQueueEmpty(SeqQueue *seqQueue);

//返回队列是否为满
State IsSeqQueueFull(SeqQueue *seqQueue);

#endif // SEQQUEUE_H_INCLUDED

然后在SeqQueue.c中实现操作:

#include "SeqQueue.h"

//初始化
void InitSeqQueue(SeqQueue *seqQueue){
    if(seqQueue == NULL){
        seqQueue = (SeqQueue*)malloc(sizeof(SeqQueue));
    }
    seqQueue->length = 0;
    seqQueue->front = 0;
    seqQueue->rear = 0;
}

//返回队列是否为空
State IsSeqQueueEmpty(SeqQueue *seqQueue){
    return (seqQueue->front == seqQueue->rear ? TRUE : FALSE);
}

//返回队列是否为满
State IsSeqQueueFull(SeqQueue *seqQueue){
    if((seqQueue->rear + 1) % MAX_SIZE == seqQueue->front){
        return TRUE;
    }
    return FALSE;
}

这样就成功实现了检测是否为空队列或者满队列。

队列的入队与出队

出队列与入队列没有什么好说的,与循环列表相同,只需要注意因为是循环的入队与出队,所以头指针和尾指针在某种程度上的操作过程是一样的,还要确认队列是否为空或者为满,我们在SeqQueue.h中编写操作:

//入队操作
State OfferSeqQueue(SeqQueue *seqQueue, ElementType element);

//出队操作
State PollSeqQueue(SeqQueue *seqQueue, ElementType *element);

然后在SeqQueue.c中实现操作:

//入队操作
State OfferSeqQueue(SeqQueue *seqQueue, ElementType element){
    if(IsSeqQueueFull(seqQueue)){
        return STATE_FALSE; //队列已满,入队失败
    }
    seqQueue->data[seqQueue->rear] = element;
    seqQueue->rear = (seqQueue->rear + 1) % MAX_SIZE;
    seqQueue->length ++;
    return STATE_OK;
}

//出队操作
State PollSeqQueue(SeqQueue *seqQueue, ElementType *element){
    if(IsSeqQueueEmpty(seqQueue)){
         return STATE_FALSE; //队列为空,出队失败
    }
    //先取出队头元素
    *element = seqQueue->data[seqQueue->front];
    seqQueue->front = (seqQueue->front + 1) % MAX_SIZE;
    seqQueue->length --;
    return STATE_OK;
}

现在我们在main.c中实现一下:

#include <stdio.h>
#include <stdlib.h>
#include "Element.h"
#include "SequenceStack.h"
#include "LinkStack.h"
#include "Gobang.h"
#include "SeqQueue.h"

ElementType datas[] = {
    {1, "紫薇一号"},
    {2, "紫薇二号"},
    {3, "紫薇三号"},
    {4, "紫薇四号"},
    {5, "紫薇五号"}
};

void TestStack();

void TestLinkedStack();

void TestSeqQueue();

int main()
{
    TestSeqQueue();
    return 0;
}

void TestSeqQueue(){
    SeqQueue seq;
    InitSeqQueue(&seq);
    OfferSeqQueue(&seq, datas[0]);
    OfferSeqQueue(&seq, datas[1]);
    OfferSeqQueue(&seq, datas[2]);
    OfferSeqQueue(&seq, datas[3]);
    for(int i = seq.front; i < seq.rear; i++){
        printf("%d\t%s\n", seq.data[i].id, seq.data[i].name);
    }
    ElementType *element = (ElementType*)malloc(sizeof(ElementType));
    PollSeqQueue(&seq, element);
    printf("当前出队的元素:\n");
    printf("%d\t%s\n", element->id, element->name);
    printf("出队操作后的队列:\n");
    for(int i = seq.front; i < seq.rear; i++){
        printf("%d\t%s\n", seq.data[i].id, seq.data[i].name);
    }
}

这里我们要注意在打印队列的时候,由于不确定长度,所以从队尾打印到队头。编译通过,运行结果如下:

1       紫薇一号
2       紫薇二号
3       紫薇三号
4       紫薇四号
当前出队的元素:
1       紫薇一号
出队操作后的队列:
2       紫薇二号
3       紫薇三号
4       紫薇四号

Process returned 0 (0x0)   execution time : 0.031 s
Press any key to continue.

栈和队列是两个非常特殊的线性表,在数据结构中的作用也是非常大的,可以发现到现在所有的操作都是基于线性表来实现的,大家要将线性表的操作练的非常熟悉,下次我们将整理有关串的知识点,我们下次见👋

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值