数据结构基础之队列

队列

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值