您的位置:首页 > 理论基础 > 数据结构算法

看数据结构写代码(15)链式队列的实现(总结篇)

2015-03-06 10:46 926 查看
队列 和 栈 是 一种 受限制的 线性表。所以 他们的 实现方式 都 相差 无几。之前有过  链栈 和 链式线性表 的 实现经验,自然 写 链队 ,也毫无问题。

下面详细讲解每一段代码 的技术要点

下面是队列节点的数据结构

struct QueueNode
{
ElementType data;
QueueNode * next;
};
//生成一个节点
QueueNode * queueNodeMake(ElementType data){
QueueNode * pNode = (QueueNode *)malloc(sizeof(QueueNode));
if (pNode != NULL)
{
pNode->data = data;
pNode->next = NULL;
}
return pNode;
}


无论是 线性表,还是栈,队列的 节点 结构 都是 一个 数据元素 和 一个 后继指针的 经典结构。(可以加一个前驱,形成双向XXX)。

然后是  根据 数据元素 生成 一个 节点的 函数,这个 函数 需要 注意 :1.内存分配失败的问题  2.将节点 后继设置为NULL,减少出错的可能性。

下面是 队列 的 数据结构:

struct LinkQueue
{
QueueNode * front;//头指针
QueueNode * rear;//队尾
int len;
};
front 是 头指针,其后继节点 是 队头节点,

rear 指向 队尾节点。

len :队列长度;有 len 这个部分,是一个非常 优秀的 实现方案,简化了许多操作 ,使逻辑清晰。例如:1.在判断队列是否为空时 2. 判断队列长度时 3.在出队时,判断队列是否为空时。

但是 要 时刻 谨记 len 长度的变化问题  :1.出队时  减1    2.入队时 + 1  3.清空队列时, 清0

下面是 初始化 队列函数

E_State queueInit(LinkQueue * queue){

QueueNode * node = (QueueNode *) malloc(sizeof(QueueNode));
if (node == NULL)
{
return E_State_Error;
}
else
{
//初始头节点为NULL 很重要
node->next = NULL;
queue->front = queue->rear = node;
queue->len = 0;
return E_State_Ok;
}
}
这个函数 比较简单,只是 要注意 初始化 队列的 属性 
queue->front = queue->rear = node;
queue->len = 0;


 以及 头节点的 后继 设置 为NULL. 

//初始头节点为NULL 很重要
node->next = NULL;


下面是 清空 队列函数:

void queueClear(LinkQueue * queue){
QueueNode * next = queue->front->next;
while (next != NULL)
{
//顺序很重要
QueueNode * freeNode = next;
next = next->next;
free(freeNode);
}
//重置成空队列的状态
queue->rear = queue->front;
queue->len = 0;
}
写这个函数的时候 注意 两部分:

1.释放节点的顺序问题,操作失误很可能导致 内存问题

//顺序很重要
QueueNode * freeNode = next;
next = next->next;
free(freeNode);
2.漏写 重新 空队列时的 状态
queue->rear = queue->front;
queue->len = 0;


下面是销毁队列函数

void queueDestory(LinkQueue * queue){
queueClear(queue);
free(queue->front);
queue->front = queue->rear = NULL;
}

1.清空队列  2. 释放头节点 3. 设置 指针为NULL

下面是 判空 和 求 长度函数

bool queueEmpty(LinkQueue queue){
return queue.len == 0 ? true : false;
}

int queueLen(LinkQueue queue){
return queue.len;
}
证明 len 属性的 好处

入队 和 出 队 是 队列最难写的两个函数。首先 给出 入队的函数

//入队
E_State enqueue(LinkQueue * queue,ElementType data){
QueueNode * newNode = queueNodeMake(data);//设置新节点的值,并将节点后继设置成 NULL
if (newNode != NULL)
{
//将队尾的后继设置成 新节点
queue->rear->next = newNode;
//设置队尾节点
queue->rear = newNode;
queue->len++;
}
return newNode != NULL ? E_State_Ok : E_State_Error;
}
QueueNode * newNode = queueNodeMake(data);//设置新节点的值,并将节点后继设置成 NULL
这个函数 简化了 入队的 细节问题(将队尾 后继 设置 为NULL)

queue->rear = newNode;
queue->len++;
在一个 使队尾指向 新节点,并将长度+1

出队的细节处理比 入队更加多。

//出队
E_State dequeue(LinkQueue * queue,ElementType * top){
if (queue->len != 0)
{
QueueNode * frontNode = queue->front->next;//指向队头节点
queue->front->next = frontNode->next;
queue->len--;
//容易出错
if (frontNode == queue->rear)//出队列的是 尾指针,重新设置尾指针
{
queue->rear = queue->front;
}
//容易漏写
*top = frontNode->data;
free(frontNode);
return E_State_Ok;
}
else//空队列
{
return E_State_Error;
}
}
再次证明 len 在判空时 的逻辑清晰 和 便利行。

QueueNode * frontNode = queue->front->next;//指向队头节点
queue->front->next = frontNode->next;
queue->len--;
将头节点后继 设置 为 队头节点的 后继 并 将 队列长度 减1

//容易出错
if (frontNode == queue->rear)//出队列的是 尾指针,重新设置尾指针
{
queue->rear = queue->front;
}
出队列的是尾指针,需要重新设置 尾指针,要不尾指针指向一个被释放的空间。( 可以将条件 改成 queue->len == 0 ,牛逼的 len 啊)

//容易漏写
*top = frontNode->data;
free(frontNode);
返回队头数据,以及 释放内存。 ×top = frontNode->data; 这段代码 很有可能漏写。

最后是遍历 队列函数

void queueTraverse(LinkQueue queue){
QueueNode * next = queue.front->next;
printf("---------队列遍历开始---------\n");
while (next != NULL)
{
printf("---------%d---------\n",next->data);
//容易漏写
next = next->next;
}
printf("---------队列遍历结束---------\n");
}
比较简单,但是 这一次 我竟然 漏写了 next = next->next; ,是我 在写这一段代码 唯一 犯下的错误。

以后写完代码,需要 检查代码。 而不是 等 发现问题了,再折返 来查问题。

下面是完整代码:(可直接拷贝运行)

欢迎指出代码不足

// LinkQueue.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdlib.h>
typedef int ElementType;

enum E_State
{
E_State_Error = 0,
E_State_Ok,
};

struct QueueNode { ElementType data; QueueNode * next; }; //生成一个节点 QueueNode * queueNodeMake(ElementType data){ QueueNode * pNode = (QueueNode *)malloc(sizeof(QueueNode)); if (pNode != NULL) { pNode->data = data; pNode->next = NULL; } return pNode; }
struct LinkQueue { QueueNode * front;//头指针 QueueNode * rear;//队尾 int len; };

E_State queueInit(LinkQueue * queue){ QueueNode * node = (QueueNode *) malloc(sizeof(QueueNode)); if (node == NULL) { return E_State_Error; } else { //初始头节点为NULL 很重要 node->next = NULL; queue->front = queue->rear = node; queue->len = 0; return E_State_Ok; } }

void queueClear(LinkQueue * queue){ QueueNode * next = queue->front->next; while (next != NULL) { //顺序很重要 QueueNode * freeNode = next; next = next->next; free(freeNode); } //重置成空队列的状态 queue->rear = queue->front; queue->len = 0; }

void queueDestory(LinkQueue * queue){ queueClear(queue); free(queue->front); queue->front = queue->rear = NULL; }

bool queueEmpty(LinkQueue queue){ return queue.len == 0 ? true : false; } int queueLen(LinkQueue queue){ return queue.len; }
//入队 E_State enqueue(LinkQueue * queue,ElementType data){ QueueNode * newNode = queueNodeMake(data);//设置新节点的值,并将节点后继设置成 NULL if (newNode != NULL) { //将队尾的后继设置成 新节点 queue->rear->next = newNode; //设置队尾节点 queue->rear = newNode; queue->len++; } return newNode != NULL ? E_State_Ok : E_State_Error; }

//出队 E_State dequeue(LinkQueue * queue,ElementType * top){ if (queue->len != 0) { QueueNode * frontNode = queue->front->next;//指向队头节点 queue->front->next = frontNode->next; queue->len--; //容易出错 if (frontNode == queue->rear)//出队列的是 尾指针,重新设置尾指针 { queue->rear = queue->front; } //容易漏写 *top = frontNode->data; free(frontNode); return E_State_Ok; } else//空队列 { return E_State_Error; } }

void queueTraverse(LinkQueue queue){ QueueNode * next = queue.front->next; printf("---------队列遍历开始---------\n"); while (next != NULL) { printf("---------%d---------\n",next->data); //容易漏写 next = next->next; } printf("---------队列遍历结束---------\n"); }

int _tmain(int argc, _TCHAR* argv[])
{
LinkQueue queue;
queueInit(&queue);
int len = 10;
ElementType data;
enqueue(&queue,1);//入队
dequeue(&queue,&data);//出队
queueClear(&queue);//清空
for (int i = 1; i <= len; i++)
{
enqueue(&queue,i);
}
queueTraverse(queue);
char * empty = queueEmpty(queue) ? "空" : "不为空";
printf("队列长度 %d, 队列是否为空 : %s ",queueLen(queue),empty);
queueDestory(&queue);
return 0;
}

运行截图;

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息