队列
1.链队列
队列可以在一端(队头)删除,另一端(队尾)插入。其操作符合先进先出(FIFO)
typedef int ELEM_TYPE;
//链式队列的有效节点
typedef struct LQ_Node
{
ELEM_TYPE data;
struct LQ_Node* next;
}LQ_Node;
//头节点
typedef struct List_Queue
{
struct LQ_Node* front; //队头指针
struct LQ_Node* rear; //队尾指针
}List_Queue;
1.初始化
void Init_List_Queue(struct List_Queue* head)
{
assert(head != NULL);
head->front = head->rear = NULL;
}
2.入队
入队的操作是尾插法,与rear有关。但要判断入队的节点是不是第一个节点,如果是的话,front和rear都要指向它,后续rear一直向最后一个元素指向。
bool Push(List_Queue* head)
{
//尾插头删
assert(NULL != head);
//1.malloc申请新节点
LQ_Node* pnewnode = (LQ_Node*)malloc(sizeof(LQ_Node));
if(pnewnode == NULL) return false;
pnewnode->data = val;
//2.分情况
pnewnode->next = NULL;
if(Is_Empty(head))//若队列为空,则头尾指针均指向新节点
head->front = head->rear = pnewnode;
else//队列非空,尾插法
{
head->rear->next = pnewnode;
head->rear = pnewnode;
}
return true;
}
3.出队
出队的操作是头删法,同单链表一样还是跨越指向+释放内存,但是要判断删除的节点是不是最后一个节点,如果是的话则要改变rear的指向(置为NULL)。
bool Pop(List_Queue* head)
{
//头删
assert(NULL != head);
if(head->front == NULL)
return false;//空队列
LQ_Node* p = head->front;
head->front = p->next;
//如果删除的是最后一个元素,则队列变空
if(head->front == NULL)
head->rear = NULL
free(p);
p = NULL;
return true;
}
4.获取队头元素值
ELEM_TYPE Front(List_Queue* head)
{
assert(NULL != head);
if(head->front == NULL)
return -1;//空队列无法出队
return head->front->data;
}
5.查找
LQ_Node* Search(List_Queue* head,ELEM_TYPE val)
{
assert(head != NULL);
for(LQ_Node* p = head0>front;p != NULL; p = p->next)
{
if(p->data == val)
return p;
}
return NULL;
}
6.判空
bool IsEmpty(List_Queue* head)
{
return head->front == NULL;
}
7.获取有效值个数
int Get_Size(List_Queue* head)
{
int count = 0;
for(LQ_Node* p = head->front;p != NULL; p = p->next)
{
count++;
}
return count;
}
8.销毁
void Destroy(List_Queue* head)
{
LQ_Node* head->front;
while(p != NULL)
{
LQ_Node* q = p->next;
free(p);
p = q;
}
}
2.循环队列
循环队列用顺序表实现,所以其面临着几个问题。
首先插入/删除的时间复杂度为O(n),为了解决这个问题,则需要数据不再挪动,改为头尾指针移动,用队尾指针来控制顺序表的有效范围。如图,如果想要删除第一个节点1,则只需要front指向下一个节点,删除5同理。
第二,顺序表在删除头节点的值时,前面的空间就闲置了,如何能将它们再次利用起来呢?让头尾相连可以解决这个问题。如图,要插入35时,只需要将rear指向前面即可把闲置的节点利用起来。
请添加图片描述
但是这样的话,判空和判满条件冲突了,原本判空和判满的条件都是当front和rear相遇。要解决这个问题,有两种解决方案:
1.在头节点上再添加一个成员,有效值个数count
2.再顺序表里,再表尾处浪费一个格子不用,当作标记位。此时,判空条件是头尾相遇,判满条件是尾指针再向后走一步遇到头指针。
最后,获取有效值个数。由于循环链表的出现,会导致reae - front出现负数,如此一来,就不能简单的用减法来计算大小,可以给reae - front加上MAXSIZE,这样计算的数值又会变大,再%MAXSIZE即可,所以最后的算式为:length = (reae - front + MAXSIZE) % MAXSIZE。
//循环队列的结构体设计:
typedef int ELEM_TYPE;
#define MAX_SIZE 10
typedef struct CQueue
{
ELEM_TYPE* base;
int front;//队头指针
int rear;//队尾指针
}CQueue;
1.初始化
void Init_Circle_Queue(CQueue* head)
{
assert(NULL != head);
head->abse = (ELEM_TYPE*)malloc(MAX_SIZE*sizeof(ELEM_TYPE));
head->front = 0;
head->rear = 0;
}
2.入队
bool Push(CQueue* head,ELEM_TYPE val)
{
//直接往队尾指针指向的下标进行插值
assert(head != NULL);
//1.判满
if(IsFull(head)) return false;
head->base[head->rear] = val;
head->rear = (head->rear + 1) % MAX_SIZE;//考虑队尾指针有可能从尾到头
return true;
}
3.出队
bool Pop(CQueue* head)
{
assert(head != NULL);
//判空
if(IsEmpty(head)) return false;
head->front = (head->front + 1) % MAX_SIZE;//头指针后移一位,%MAX_SIZE表示循环队列
return true;
}
4.获取队头元素值
ELEM_TYPE Front(CQueue* head)
{
assert(NULL != head);
//1.判空
if(IsEmpty(head)) return false;
return head->base[head->front];
}
5.查找
int Search(CQueue* head, ELEM_TYPE val)
{
assert(head != NULL);
for(int i = head->front; i != head->rear; i = (i + 1) % MAX_SIZE)
{
if(head->base[i] == val) return i;
}
return -1;
}
6.判空与判满
//判空
bool IsEmpty(CQueue* head)
{
return head->front == head->rear;
}
//判满
//判空和判满条件冲突
//采用上述解决方案2:在顺序表里,在标为处浪费一个格子不用 当标记位 ,rear+1=front则满
bool IsFull(CQueue* head)
{
return (head->rear + 1) % MAX_SIZE == head->front;
}
7.获取有效值个数
int Get_Size(CQueue* head)
{
return (head->rear - head->front + MAX_SIZE) % MAX_SIZE;
}
8.清空与销毁
//清空
void Clear(CQueue* head)
{
head->front = head->reae = 0;
}
//销毁
void Destroy(CQueue* head)
{
assert(head != NULL);
free(head->base);
}
7.获取有效值个数
int Get_Size(CQueue* head)
{
return (head->rear - head->front + MAX_SIZE) % MAX_SIZE;
}
8.清空与销毁
//清空
void Clear(CQueue* head)
{
head->front = head->reae = 0;
}
//销毁
void Destroy(CQueue* head)
{
assert(head != NULL);
free(head->base);
}