码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-03-06 12:50:50      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:队列   链队列   链式队列   数据结构   队列的链式表示   

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

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

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

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;
}

运行截图;

技术分享


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

标签:队列   链队列   链式队列   数据结构   队列的链式表示   

原文地址:http://blog.csdn.net/fuming0210sc/article/details/44096647

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!