数据结构:队列(C语言实现)&一些个人总结

今天终于是把队列的基本操作学完了,队列和栈其实大同小异,队列遵循的是先进先出原则,而栈遵循的是先进后出原则,形象的来说,栈是一个杯子,而队列是一个水管,谁先进队自然就会先流出来。

1.队列创建以及初始化

这次还是用链表来实现,与栈不同的是,由于队列是一个两头都开口的结构,入队和出队分别在两个不同的方向进行,因此不仅需要头指针(head)还需要一个尾指针(tail)。

typedef struct NODE{
int data;
struct NODE*next;

}NODE,*LINK_NODE;//创建节点

typedef struct QUENE
{
   LINK_NODE head,tail;//队列首尾
   int size;//队列大小
}QUENE;

然后是初始化了,但是我自我觉得对节点的初始化好像一直没有什么用......

LINK_NODE initNODE(){
    LINK_NODE newnode=(LINK_NODE)malloc(sizeof(NODE));
    newnode->data=0;
    newnode->next=NULL;
    return newnode;
}//节点初始化

void initquene(QUENE* quene){
    quene->head=NULL;
    quene->size=0;//长度没有
    quene->tail=NULL;//头尾指针为空

}//队列的初始化

2.队列的插入(入队)以及删除(出队)

其实对于链表,栈,队列而言,感觉这两个操作都是最核心的操作,因为这涉及一些指针的转换,顺序的问题,自我感觉在实现的时候有点绕。

1.入队,操作队尾

入队是这样的,在队尾不断添加新节点,看起来比较简单但是在实现的时候,我想分享一下我自己的问题。

我对tail指针有一些误解,我一开始以为tail是一个节点中的next指针,因为我是从第一个节点的插入开始想的,我在想,head是新节点的首地址,那么tail不就是新节点中的next指针吗?

但是实际上,tail应该是队列中最后一个节点的地址,并非最后一个节点的next指针。针对第一个节点而言,第一个节点和最后一个节点是一个,那head和tail就重叠了,这样才是正确的操作。(见下图)

所以这样之后后面的操作就很顺畅了,只需要每次新添加一个节点,第一步,先连接,把新节点和原来的队尾连接;第二步,再更新,把队尾更新为现在新节点的地址即可。请看代码。

void pushquene(QUENE* quene,int element){
      LINK_NODE newnode=(LINK_NODE)malloc(sizeof(NODE));//新节点
      newnode->data=element;
      if(quene->size==0){      //对插入第一个节点单独讨论
        quene->head=newnode;
        quene->tail=newnode;   //第一个节点就是最后一个节点,所以首尾指针一致
      }
      else{
    quene->tail->next=newnode; //后面节点的插入,先连接
    quene->tail=newnode;       //再更新
      }
      quene->size++;       //变更长度
}
2.出队,操作队首

出队的时候是先入先出,因此都是从队首出去的,这里也有几个小细节。

第一,首先要判断队列是不是已经空了,如果size=0,那么就直接返回错误。当然,我们在实操的时候不会这样,因为最后一个元素出队之后,次数就会结束,比如5个元素,那么只操作5次,就不会出现长度为零的情况(最后一次长度是为一的,操作完之后才会为零)。

第二,在指针的变化中,针对使用新变量,我总结了一下:要被释放的节点,第一是要用新变量存储其中的数据(释放了之后就用不了了,所以先存起来,返回的时候用新变量),第二是要用新指针存储该节点的后继(next指针),道理也是一样的,释放了之后就不能用了。

然后,操作的顺序问题,我的总结:先不要更改head指针(因为这个head是要被释放的),因此先直接释放原有的head节点,再用已经存在新指针里面的head->next指针给已经被释放的head赋值,这样才是正确的更新操作。

第三,当最后一个元素出队的时候,head指针和tail指针再一次重叠(是不是和之前入队的时候似曾相识?),那么由于head指针是要被更换成为head->next的,而此时head->next指针就是指向NULL的,因此相应的,我们也要把tail指针置为空,否则,tail指针还是会指向这个已经被释放的节点,这是没有意义的。

下面先看一下图,再上代码。

对了,这里一直想说一下这个关系(橙色部分),感觉还是挺重要的。

代码部分:

int popquene(QUENE* quene){
   if (quene->size == 0) {  
        printf("Queue is empty.\n");  //队列为空
        return -1;   
    }  
    LINK_NODE cur=quene->head->next;//新指针存储
    int res=quene->head->data;//新变量存储数据
    free(quene->head);
    quene->head=cur;      //更新头节点
    if(quene->head==NULL){
        quene->tail=NULL;   //针对最后一个节点的操作
    }
    quene->size--;       //更新长度
    return res;
}

3.查找元素

这就很简单了,但是我还是出了一些问题,第一次写函数的时候灵光乍现竟然用了popquene(出队)函数,然后死循环了。在AI的帮助下我才明白出队会改变队列的内容啊!所以乖乖的用遍历就好了~~~

bool SearchQuene(QUENE* quene, int target) {  
    LINK_NODE current = quene->head;  
    while (current != NULL) {  
        if (current->data == target) {  
            return true;  
        }  
        current = current->next;  
    }  
    return false;  
}

虽然队列好像很简单,但是好像写的是最长的一次,里面有很多个人的教训经验在里面,希望大家多多支持~~~

  • 39
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
顺序队列实现: ```c #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 // 队列的最大长度 typedef struct{ int data[MAXSIZE]; int front; // 队头指针,指向队头元素 int rear; // 队尾指针,指向队尾元素的后一个位置 }SqQueue; // 初始化队列 void InitQueue(SqQueue *Q){ Q->front = 0; Q->rear = 0; } // 判断队列是否为空 int IsEmptyQueue(SqQueue Q){ if(Q.front == Q.rear) return 1; else return 0; } // 判断队列是否已满 int IsFullQueue(SqQueue Q){ if((Q.rear+1)%MAXSIZE == Q.front) return 1; else return 0; } // 入队 int EnQueue(SqQueue *Q, int data){ if(IsFullQueue(*Q)){ printf("Queue is full\n"); return 0; } Q->data[Q->rear] = data; Q->rear = (Q->rear+1)%MAXSIZE; return 1; } // 出队 int DeQueue(SqQueue *Q, int *data){ if(IsEmptyQueue(*Q)){ printf("Queue is empty\n"); return 0; } *data = Q->data[Q->front]; Q->front = (Q->front+1)%MAXSIZE; return 1; } // 输出队列 void PrintQueue(SqQueue Q){ if(IsEmptyQueue(Q)){ printf("Queue is empty\n"); return; } int i = Q.front; while(i != Q.rear){ printf("%d ", Q.data[i]); i = (i+1)%MAXSIZE; } printf("\n"); } int main(){ SqQueue Q; // 声明一个队列 InitQueue(&Q); // 初始化队列 int flag = 1, choice, data; while(flag){ // 循环操作 printf("1. EnQueue\n"); printf("2. DeQueue\n"); printf("3. PrintQueue\n"); printf("0. Exit\n"); printf("Enter your choice: "); scanf("%d", &choice); switch(choice){ case 0: flag = 0; break; case 1: printf("Enter data to Enqueue: "); scanf("%d", &data); if(EnQueue(&Q, data)) printf("Enqueue %d successfully!\n", data); break; case 2: if(DeQueue(&Q, &data)) printf("Dequeue %d successfully!\n", data); break; case 3: PrintQueue(Q); break; default: printf("Invalid choice\n"); } } return 0; } ``` 链队列实现: ```c #include <stdio.h> #include <stdlib.h> typedef struct Node{ // 链队列结点的定义 int data; struct Node *next; }Node, *pNode; typedef struct{ // 链队列的定义 pNode front; // 队头指针 pNode rear; // 队尾指针 }LinkQueue; // 初始化队列 void InitQueue(LinkQueue *Q){ Q->front = Q->rear = (pNode)malloc(sizeof(Node)); Q->front->next = NULL; } // 判断队列是否为空 int IsEmptyQueue(LinkQueue Q){ if(Q.front == Q.rear) return 1; else return 0; } // 入队 void EnQueue(LinkQueue *Q, int data){ pNode p; p = (pNode)malloc(sizeof(Node)); p->data = data; p->next = NULL; Q->rear->next = p; Q->rear = p; } // 出队 int DeQueue(LinkQueue *Q, int *data){ if(IsEmptyQueue(*Q)){ printf("Queue is empty\n"); return 0; } pNode p = Q->front->next; *data = p->data; Q->front->next = p->next; if(Q->rear == p) // 如果队列中只有一个元素 Q->rear = Q->front; free(p); return 1; } // 输出队列 void PrintQueue(LinkQueue Q){ if(IsEmptyQueue(Q)){ printf("Queue is empty\n"); return; } pNode p = Q.front->next; while(p){ printf("%d ", p->data); p = p->next; } printf("\n"); } int main(){ LinkQueue Q; // 声明一个队列 InitQueue(&Q); // 初始化队列 int flag = 1, choice, data; while(flag){ // 循环操作 printf("1. EnQueue\n"); printf("2. DeQueue\n"); printf("3. PrintQueue\n"); printf("0. Exit\n"); printf("Enter your choice: "); scanf("%d", &choice); switch(choice){ case 0: flag = 0; break; case 1: printf("Enter data to Enqueue: "); scanf("%d", &data); EnQueue(&Q, data); printf("Enqueue %d successfully!\n", data); break; case 2: if(DeQueue(&Q, &data)) printf("Dequeue %d successfully!\n", data); break; case 3: PrintQueue(Q); break; default: printf("Invalid choice\n"); } } return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值