链式队列的组织形式与链表没有什么不同,只不过插入、删除被约束在固定的两端。为了便于操作,通常也会创建管理结构体,用来存储队头指针、队尾指针、队列元素个数等信息。
图解:
由图可以看到,链式队列主要控制队头和队尾,由于管理结构体中保存了当前队列元素个数size,因此可以不用设计链表的头节点,初始化空队列的时候,只需要让队头、队尾指针同时指向空即可。
一、链式队列的管理结构体设计
图解:
示例代码:
// 链式队列节点设计
typedef struct node
{
// 数据域
int data;
// 指针域
struct node *next_p;
}node_t, *node_p;
// 链式队列管理结构体设计
typedef struct link_queue
{
node_p front_p; // 队头指针
node_p rear_p; // 队尾指针
int size; // 队列当前元素个数
}link_queue_t, *link_queue_p;
二、初始化链式队列
图解:
示例代码:
/**
* @brief 初始化链式队列
* @note None
* @param None
* @retval 成功:返回指向这个链式队列的管理结构体的指针
* 失败:返回NULL
*/
link_queue_p LINK_QUEUE_Init(void)
{
// 1、申请堆内存空间
link_queue_p p = malloc( sizeof( link_queue_t ) );
bzero( p, sizeof( link_queue_t ) );
// 2、申请成功,给堆内存空间进行赋值(里面还要申请头节点空间)
if ( p != NULL)
{
// a、申请头节点内存
node_p head_node = malloc( sizeof( node_t ) );
bzero( head_node, sizeof( node_t ) );
if ( head_node != NULL)
{
// 数据域无需赋值操作
// 指针域
head_node->next_p = NULL;
}
else
{
free(p);
return NULL;
}
// b、将链式队列的队头和队尾指针,指向头节点的下一个
p->front_p = head_node;
p->rear_p = head_node;
// c、链式队列的元素个数
p->size = 0 ;
}
else
return NULL;
// 3、成功返回指向这个链式队列管理结构体的指针
return p;
}
三、初始化数据节点、入队 --- 插入数据(头插法、尾插法)
图解:
1. 头插法
2. 尾插法
示例代码:
/**
* @brief 入队
* @note 初始化数据节点、入队(头插法、尾插法)
* @param p: 指向这个链式队列的管理结构体的指针
* new_data:要插入的数据
* @retval 成功:返回0
* 失败:返回非0
*/
int LINK_QUEUE_EnQueue( link_queue_p p, int new_data )
{
// 1、创建数据节点
node_p new_node = malloc( sizeof( node_t ) );
bzero( new_node, sizeof( node_t ) );
if ( new_node != NULL )
{
// 数据域:
new_node->data = new_data;
// 指针域:
new_node->next_p = NULL;
}
else
return -1;
// 2、判断链式队列的头节点的next_p是否为NULL
if ( p->front_p->next_p == NULL )
{
// 头插法
p->front_p->next_p = new_node;
new_node->next_p = NULL;
}
else
{
// 尾插法
p->rear_p->next_p = new_node;
new_node->next_p = NULL;
}
// 让队尾指针指向最后一个数据节点
p->rear_p = new_node;
// 3、当前链式队列的数据+1
p->size++;
// 4、成功返回0
return 0;
}
四、判断链表是否为空
示例代码:
/**
* @brief 判断链式队列是否为空
* @note None
* @param p:指向这个链式队列的管理结构体的指针
* @retval 如果队列内存为空: 返回true
* 队列内存为非空:返回false
*/
bool LINK_QUEUE_IfEmpty( link_queue_p p )
{
return ( p->size == 0 );
}
五、遍历整个队列
图解:
示例代码:
/**
* @brief 遍历整个链式队列数据
* @note None
* @param p: 指向这个链式队列的管理结构体的指针
* @retval 成功:返回0
* 失败:返回非0
*/
int LINK_QUEUE_ShowQueue( link_queue_p p )
{
// 1、判断链式队列数据是否为空
if ( LINK_QUEUE_IfEmpty(p) )
return -1;
// 2、遍历整个链式队列
node_p tmp_p = NULL;
int i = 0;
printf("================链式队列里的数据===============\n\n");
for (tmp_p = p->front_p->next_p, i = 0; tmp_p != NULL; tmp_p = tmp_p->next_p, i++)
{
printf("sq_queue[%d] = %d\n", i, tmp_p->data);
}
printf("==============================================\n\n");
// 3、成功返回0
return 0;
}
六、出队 --- 删除数据
图解:
示例代码:
/**
* @brief 出队
* @note None
* @param p:指向这个链式队列的管理结构体的指针
* @retval 成功:返回0
* 失败:返回非0
*/
int LINK_QUEUE_OutQueue( link_queue_p p )
{
// 1、判断链式队列是否为空
if ( LINK_QUEUE_IfEmpty(p) )
return -1;
// 2、将队头数据节点( p.front_p的next_p )给删除
node_p last_node = p->front_p;
node_p del_node = last_node->next_p;
node_p next_node = del_node->next_p;
last_node->next_p = next_node;
free(del_node);
// 3、链式队列的数据节点个数减-
p->size--;
// 4、成功返回0
return 0;
}
七、查询数据
图解:
示例代码:
/**
* @brief 取队头
* @note None
* @param p: 指向这个链式队列的管理结构体的指针
* @retval 成功:返回0
* 失败:返回非0
*/
int* LINK_QUEUE_GetFrontData( link_queue_p p )
{
// 1、判断链式队列是否为空
if ( LINK_QUEUE_IfEmpty(p) )
return -1;
// 2、返回链式队列的队头的数据节点的指针
return &( p->front_p->next_p->data );
}
八、销毁整个队列
图解:
示例代码:
/**
* @brief 销毁链式队列
* @note None
* @param p: 指向这个链式队列管理结构体的指针
* @retval None
*/
void LINK_QUEUE_UnInit( link_queue_p p )
{
// 1、将队头数据节点( p.front_p的next_p )给删除
node_p last_node = p->front_p;
node_p del_node = last_node->next_p;
node_p next_node = del_node->next_p;
node_p tmp_p = NULL;
for ( tmp_p = last_node; tmp_p->next_p != NULL; tmp_p = tmp_p->next_p )
{
// a、删除节点并释放其内存
last_node->next_p = next_node;
free( del_node );
// b、轮回继续
del_node = next_node;
next_node = next_node->next_p;
}
if ( tmp_p->next_p == NULL ) // 最后还有一个数据节点,也将其删除并释放了
{
last_node->next_p = next_node;
free( del_node );
}
// 2、释放头节点和链式队列的管理结构体
free(last_node);
free(p);
}
以上知识就是我的课后笔记了。