队列
队列是一个先进先出(FIFO:first in first out)的线性表。 它允许在表的一端进行插入操作,而在另外一端进行删除 队尾(rear): 允许插入元素的一端 队头(front):允许删除元素的一端 “队列的思想”:先进先出 “排队论” 队列的实现: (1)、队列的数据类型的表述 顺序结构:顺序队列 用一组地址连续的空间,来保存队列的每一个元素。 “数组” 链式结构: 链表(带头节点的) (2)、队列操作 InitQueue 初始化一个队列 DestoryQueue 销毁一个队列 ClearQueue 清空一个队列 QueueEmpty 判断一个队列是否为空 QueueLength 返回队列中的元素的个数,也就是队列的长度 EnQueue 元素入队,把一个元素加入到队中 DeQueue 出队,把队尾元素给干掉 GetHead 返回一个队头元素,但是不出队
顺序队列: 用一组地址连续的存储空间来存储队列中的每一个元素。
“循环队列” 将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。当队头指针Q.front == max_len-1 后,再前进一个位置就自动到0 , 这可以利用除法取余运算符(%)来实现。 初始的时候: Q->front = Q->rear = 0; 队头指针进1 : Q->front = (Q->front + 1) % max_len 队尾指针进1 : Q->rear = (Q->rear + 1) % max_len 队列的长度 : (Q->rear - Q->front + max_len) % max_len 那么,循环队列队空和队满的判断条件是什么???显然,队空的条件是 Q->rear == Q->front 若入队元素的速度快于出队元素的速度,则队尾指针很快就会追上队头指针,如图下:! 此时也可以看出队满的时候,也有Q->rear == Q->front 为了区分是队空还是队满的情况,有如下三种处理方式: 1、牺牲一份单元来区分队满还是队空,入队的时候少用一个队列单元,这是一种最普遍的做法, 约定以“队头指针在队尾指针的下一个位置作为队满的标志”! 队满的条件:(Q->rear+1) % man_len == Q->front 队空的条件:Q->rear == Q->front 队列中的元素个数:(Q->rear - Q->front + max_len) % max_len 2、类型中增设表示元素个数的数据成员。这样,队空的条件为 Q.size == 0; 队满的条件为Q.size == max_len 3、类型中增设一个tag数据成员,以区分是队满还是队空。 tag 等于0 的时候,若是因为删除导致 Q->front == Q->rear tag 等于1 的时候,若是因为插入导致 Q->front == Q->rear链式队列
带头结点的单链表/带头节点的双链表#include<stdio.h> #include<stdlib.h> #include"listqueue.h" //@InitQueue 初始化一个链式队列 //@return 返回一个链式队列的队列 listQueue* InitQueue(void) { listQueue* l=(listQueue*)malloc(sizeof(listQueue)); l->rear=NULL; l->front=NULL; l->nodenum=0; return l; } //@EnQueue:入队 //@listQueue* l:队列的头节点 //@QElemtype x:入队的元素 int EnQueue(listQueue* l,QElemtype x) { if(l==NULL) return 0;//失败 l->nodenum+=1; Qnode* p=malloc(sizeof(Qnode)); p->data=x; p->next=NULL; if(l->front==NULL) { l->front=p; l->rear=p; } else { l->rear->next=p; l->rear=p; } return 1;//入队成功 } /* @DeQueue:出队 @listQueue* s:队头 @QElemtype* e:要出队的元素的存放地址 */ int DeQueue(listQueue* l,QElemtype *e) { if(l==NULL||l->nodenum==0) { return 0;//出栈失败 } *e=l->front->data; Qnode* px=l->front; l->front=px->next; px->next=NULL; free(px); if(l->front==NULL) { l->rear=NULL; } l->nodenum-=1; return 1; } //返回一个队头元素,但是不出队 int GetHead(listQueue* l,QElemtype* e) { if(l==NULL||l->nodenum==0) return 0;//队列为空 *e=l->front->data; return 1;//队列不为空 } //@ClearQueue 清空一个链式队列 void ClearQueue(listQueue* l) { if(l==NULL||l->front==NULL) return; Qnode* px=l->front; while (px!=NULL) { l->front=px->next; px->next=NULL; free(px); px=l->front; } l->rear=NULL; l->nodenum=0; } //DestoryQueue 销毁一个链式队列 void DestoryQueue(listQueue* l) { if(l==NULL) return; ClearQueue(l); free(l); } //判断一个队列是否为空 int QueueEmpty(listQueue* l) { if(l==NULL||l->nodenum==0) return 0;//队列为空 else return 1;//栈=队列不为空 } //返回一个队列的元素个数 int Queuelength(listQueue* l) { if(l==NULL||l->nodenum==0) return 0; return l->nodenum;//队列的元素个数 }
作业
利用两个栈S1 和 S2 来模拟一个队列,已知栈的四个运算定义如下: Push(S,x); //元素x入栈 Pop(S,&x); //s出栈并将出栈元素赋值给x StackEmpty(s); //判断栈是否为空 StackOverflow(s); //判断栈是否满了 如何利用栈的运算来实现这个队列的三个运算 EnQueue DeQuene QueneEmpty 这个三个运算的形参由用户自己定义 基本思想: 利用两个栈s1ands2来模拟一个队列,当需要想队列中插入一个元素的时候,用s1来存放已输入的元素,即s1执行入栈的操作。当需要出队的时候,则对s2执行出栈的操作。 由于从栈中取出元素的顺序是原顺序的逆序,所以必须先将s1的所有元素,全部出栈并且入栈到s2中,再从s2中执行出栈操作,就完成了出队操作, 而再执行此操作的时候你要判断s2是否为空,否则就会导致顺序混乱。 总结一下: 1)、对s1的出栈操作 用作 出队,若s2为空,则先将s1的所有的元素送入s2 2)、对s1的入栈操作 用作 入队,若s1满了,必须先保证s2为空,才能将s1中的元素全部插入s2 入队的算法: int EnQueue(seqstack *S1,seqstack *S2,int e) 出队的算法: int DeQueue(seqstack *S1,seqstack *S2,int *e) 判断队列算法为空的代码: int QueueEmpty(seqstack *S1,seqstack *S2)
嵌入式学习一阶段——C语言:数据结构(4)
于 2023-09-08 16:27:04 首次发布