栈与队列-C语言

栈与队列-C语言

初始化栈-入栈-出栈

通过代码依次实现初始化栈,判断栈是否为空,压栈,获取栈顶元素,弹栈。此处规定S.top=-1时,栈为空。

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

#define MaxSize 50

typedef int ElemType;
typedef struct {
    ElemType data[MaxSize];
    int top;
}SqStack;

//初始化栈
void InitStack(SqStack &S){
    S.top=-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; //先存入数据,栈顶指针再加
    return true;
}

//出栈
bool Pop(SqStack &S, ElemType &x){
    if(-1==S.top) //栈为空无法出栈,这样的写法是为了避免书写错误,将常量放在前面如果只写一个等号代码会报错
    {
        return false;
    }
    x=S.data[S.top--]; //先弹出,栈顶指针再减
    return true;
}

//读取栈顶元素
bool GetTop(SqStack &S, ElemType &x){
    if(-1==S.top)
    {
        return false;
    }
    x=S.data[S.top];
    return true;
}

int main(){
    SqStack S;
    InitStack(S);
    bool ret;
    ret= StackEmpty(S);
    if(ret)
    {
        printf("Stack is Empty");
    }
    Push(S,3);
    Push(S,4);
    Push(S,5);
    ElemType T; //接收输出的元素或弹出的元素
    ret= GetTop(S,T);
    if(ret)
    {
        printf("Stack Top Elem is %d\n",T);
    }
    ret= Pop(S,T);
    if(ret)
    {
        printf("Pop Elem is %d\n",T);
    }
    return 0;
}
循环队列

初始化循环队列,判断循环队列是否为空,入队,出队。注意代码执行结束可以通过调试来观察循环队列内的元素。

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

#define MaxSize 5
typedef int ElemType;
typedef struct {
    ElemType data[MaxSize]; // 数组,存储MaxSize-1个元素
    int front, rear; // 队列头、队列尾
} SqQueue;

// 初始化队列
void InitQueue(SqQueue &Q) {
    Q.rear = Q.front = 0;
}

// 判断队列是否为空
bool IsEmpty(SqQueue Q) {
    return Q.rear == Q.front;
}

// 入队
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;
    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;
    return true;
}

// 遍历队列内的元素并输出
void ListQueue(SqQueue Q) {
    int i = Q.front;
    while (i != Q.rear) {
        printf("%d\n", Q.data[i]);
        i = (i + 1) % MaxSize;
    }
}

int main() {
    SqQueue Q;
    InitQueue(Q);
    bool ret; // 存储返回值
    ret = IsEmpty(Q);
    if (ret) {
        printf("SqQueue is empty\n");
    } else {
        printf("SqQueue is not empty\n");
    }
    EnQueue(Q, 3);
    EnQueue(Q, 4);
    EnQueue(Q, 5);
    EnQueue(Q, 6);
    ret = EnQueue(Q, 7);
    if (ret) {
        printf("EnQueue Success\n");
    } else {
        printf("EnQueue Fail\n");
    }
    ElemType E; // 存储出队元素
    ret = DeQueue(Q, E);
    if (ret) {
        printf("DeQueue Elem is %d\n", E);
    } else {
        printf("DeQueue Fial\n");
    }
    EnQueue(Q, 8);
    if (ret) {
        printf("EnQueue Success\n");
    } else {
        printf("EnQueue Fail\n");
    }
    printf("Elements in Queue:\n");
    ListQueue(Q);
    return 0;
}

通过断点调试,可以观察到当前front和rear指向的位置和当前出队的元素,如下图所示

循环链表调试

队列(链表实现)

实现的步骤依次为初始化队列、入队、出队、和展示队列元素。代码执行结束可以通过调试来观察队列内元素。

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

typedef int ElemType;
typedef struct LinkNode{
   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; //头指针的next指针指向空
}

//队列判空
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; //rear指针的next指针始终指向尾部
    Q.rear=s; //rear指针指向结点的最后一个元素
}

//出队,头删法
bool DeQueue(LinkQueue &Q,ElemType &x){
    if(Q.front==Q.rear)
    {
        return false;
    }
    LinkNode *p=Q.front->next; //头结点无数据,front指针next指针指向的节点才有数据,并赋值给指针p
    x=p->data; //将准备删除的元素的数据取出,赋值给x
    Q.front->next=p->next; //断链
    if(Q.rear==p) //删除的元素为队列的最后一个元素
    {
        Q.rear=Q.front; //将队列置为空
    }
    free(p); //释放节点
    return true; //由于是bool类型的子函数,所以要返回值,否则编译器会出现警告
}

// 展示当前队列元素
void ListElem(LinkQueue Q) {
    LinkNode *p = Q.front->next; // 从队列的第一个元素开始遍历
    while (p != NULL) {
        printf("%3d", p->data);
        p = p->next;
    }
}

int main(){
    LinkQueue  Q;
    InitQueue(Q);
    bool ret; //存储判断条件
    ret=IsEmpty(Q);
    if(ret)
    {
        printf("Queue is empty\n");
    } else
    {
        printf("Queue is not empty\n");
    }
    EnQueue(Q,3);
    EnQueue(Q,4);
    EnQueue(Q,5);
    EnQueue(Q,6);
    EnQueue(Q,7);
    ElemType element; //存储出队元素
    ret= DeQueue(Q,element);
    if(ret)
    {
        printf("DeQueue elem is %d\n",element);
    } else
    {
        printf("DeQueue fail\n");
    }
    ListElem(Q);
    return 0;
}

下面附带两道题目,供读者自行练习

一、设计一个队列,要求满足:

①初始时队列为空。

②入队时,允许增加队列占用空间。

③出队后,出队元素所占用的空间可重复使用,即整个队列所占用的空间只增不减。

④入队操作和出队操作的时间复杂度始终保持为O(1)。

(1)该队列应选择链式存储结构还是顺序存储结构?

(2)给出判断队空和队满的条件?

(3)画出第一个元素入队后的队列状态?

(4)给出入队操作和出队操作的基本过程?

答:

(1)选择链式存储结构(两段式单向循环链表)是更好的选择。因为链表的结点可以动态地分配和释放,使得队列的大小可以灵活地扩展和收缩,而且入队和出队操作的时间复杂度为 O(1)。

(2)初始时,创建一个只有一个空闲结点的两段式单向循环链表,头指针front和尾指针rear均指向空闲节点。队空的判断条件为front==rear。队满的判断条件为front=rear->next。

2019 49 第二问

(3)如下图所示

2019 49 第三问

(4)入队操作:若front==rear->next,队满,则在rear后面插入一个新的空闲结点,入队元素保存到rear所指的结点中,rear=rear->next,随后返回。出队操作:若front==rear,队空,出队失败,返回失败。若队列不为空。取front所指结点中的元素e,front=front->next,随后返回e。

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

typedef int ElemType;
typedef struct LNode{
   ElemType data;
   struct LNode *next;
}LNode,*LinkList;

//入队
void EnQueue(LinkList front, LinkList &rear, ElemType val){
    LinkList p;
    if(rear->next==front)
    {
        //队满,申请一个结点的空间,放入队列
        p=(LinkList) malloc(sizeof(LNode));
        rear->data=val; //把入队元素放入rear指向的结点
        rear->next=p; //新申请的结点作为队列的最后一个结点
        p->next=front;
        rear=p;
    } else
    {
        rear->data=val;
        rear=rear->next;
    }
}

//出队
void DeQueue(LinkList &front, LinkList &rear){
  if(front==rear)
  {
      printf("Queue is empty\n");
  }else
  {
      printf("DeQueue elem is %d\n",front->data);
      front=front->next;
  }
}

void CircleQueue(LinkList &front, LinkList &rear){
    //队列头和队列尾都指向一个结点,这时队列即是空的,也是满的
    front=(LinkList) malloc(sizeof(LNode));
    rear=front;
    rear->next=front;
    //入队
    EnQueue(front,rear,3);
    EnQueue(front,rear,4);
    //出队
    DeQueue(front,rear);
    DeQueue(front,rear);
    DeQueue(front,rear);
}
int main(){
    LinkList front,rear;
    CircleQueue(front,rear);
    return 0;
}

提示:在入队操作时,else分支语句会被执行吗?

二、新建一个栈,读取标准输入3个整数3 4 5,入栈3 4 5,依次出栈,打印 5 4 3,新建循环队列(Maxsize为5),读取标准输入3 4 5 6 7,入队7时,队满,打印false,然后依次出队,输出 3 4 5 6。

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

#define MaxSize 5
typedef int ElemType;
typedef struct {
   ElemType data[MaxSize];
   int top;
}SqStack;

void InitStack(SqStack &S){
    S.top=-1;
}

bool Push(SqStack &S, ElemType x){
    if(S.top==MaxSize-1)
    {
        return false;
    }
    S.data[++S.top]=x;
    return true;
}

bool Pop(SqStack &S, ElemType &x){
    if(-1==S.top)
    {
        return false;
    }
    x=S.data[S.top--];
    return true;
}

typedef struct {
    ElemType data[MaxSize];
    ElemType front,rear;
}SqQueue;

void InitQueue(SqQueue &Q){
    Q.rear=Q.front=0;
}

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;
    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;
    return true;
}
int main(){
    SqStack S;
    bool flag;
    ElemType m;
    InitStack(S);
    int i,num;
    for(i=0;i<3;i++)
    {
        scanf("%d",&num);
        Push(S,num);
    }
    for(i=0;i<3;i++)
    {
        Pop(S,m);
        printf("%2d",m);
    }
    printf("\n");
    SqQueue Q;
    InitQueue(Q);
    for(i=0;i<5;i++)
    {
        scanf("%d",&num);
        flag= EnQueue(Q,num);
        if(false==flag)
        {
            printf("elem %d enqueue is fail\n",num);
        }
    }
    ElemType elem;
    for(i=0;i<4;i++)
    {
        DeQueue(Q,elem);
        printf("%2d",elem);
    }
    printf("\n");
    return 0;
}