10.队列

1.理论

队列是一种先进先出(FIFO)的数据结构,和栈一样在线性表的基础上扩展而来,相对于线性表可任意位置插入和删除,它只允许在一端插入,在另一端删除,又叫入列和出列操作。

和栈一样,队列程序设计中经常出现,最典型的就是操作系统中的作业排队:在多道程序共同运行的计算机系统中,如果运行的程序都要输出,那么就按照请求输出的顺序排队,最先请求的最先输出,类似的还应用在网络请求、多线程程序设计等地方。


对于队列的存储结构设计,一般的理论设计如下:

typedef struct tagQueue
{
	QueueElem	*pFront;		//队列头元素指针
	QueueElem	*pRear;			//队列尾元素指针
}Queue;

队列空时pFront=pRear


同栈的实现一样,我们想把顺序结构及链式结构的线性表、栈和队列统一起来,在这里我没有使用通用设计,直接使用原来的线性表存储数据:顺序线性表的下标最大的数据元素即为队列尾元素,队列头元素为下标最小的元素,长度为0时队列为空;重新设计链式存储表的结构使头结点和元素节点不同,队列头元素为第一个元素节点,队列尾元素为最后一个元素节点,当头结点指向NULL时,队列为空。


2.顺序队列

存储结构

typedef struct
{
	JWArrayElem	*pElem;			//数据元素存储区域
	int			nLength;		//当前数据元素长度
	int			nTotalSize;		//当前线性表长度
	int			nIncrSize;		//当线性表已满时,重新分配的新增区域大小
}JWArray;

核心程序

/************************************************************************/
/* 获得当前队列头元素的值
** 下标为0的为队列头元素													*/
/************************************************************************/
JWArray_BOOL JWArrayGetHead(JWArray *pArray, JWArrayElem *pElem)
{
	//判断队列是否为空
	if (0 == pArray->nLength)
	{
		return JWARRAY_FALSE;
	}

	//获得当前队列头元素的值
	*pElem =  pArray->pElem[0];

	return JWARRAY_TRUE;
}

/************************************************************************/
/* 设置当前队列头元素的值
** 下标为0的为队列头元素													*/
/************************************************************************/
JWArray_BOOL JWArraySetHead(JWArray *pArray, const JWArrayElem elem)
{
	//判断队列是否为空
	if (0 == pArray->nLength)
	{
		return JWARRAY_FALSE;
	}

	//设置当前队列头元素的值
	pArray->pElem[0] = elem;

	return JWARRAY_TRUE;
}

/************************************************************************/
/* 当前队列入列一个元素--即为线性表末尾插入一个元素							*/
/************************************************************************/
JWArray_BOOL JWArrayEnQueue(JWArray *pArray, const JWArrayElem elem)
{
	JWArrayElem *pNewElem;

	//如果队列满则重新分配更大的队列
	if (JWARRAY_TRUE == JWArrayIsFull(pArray))
	{
		pNewElem = (JWArrayElem *)realloc(pArray->pElem, (pArray->nTotalSize + pArray->nIncrSize) * sizeof(JWArrayElem));

		if (NULL == pNewElem)
		{
			return JWARRAY_FALSE;
		}
		else
		{
			pArray->pElem = pNewElem;
			pArray->nTotalSize += pArray->nIncrSize;
		}
	}

	//末尾位置插入值
	pArray->pElem[pArray->nLength++] = elem;
	return JWARRAY_TRUE;
}

/************************************************************************/
/* 当前队列出列一个元素--即为删除线性表第一个元素			                  */
/************************************************************************/
JWArray_BOOL JWArrayDeQueue(JWArray *pArray, JWArrayElem *pElem)
{
	int i;

	//判断队列是否为空
	if (0 == pArray->nLength)
	{
		return JWARRAY_FALSE;
	}

	//出队列--删除第一个元素的值
	if (NULL != pElem)
	{
		*pElem = pArray->pElem[0];
	}

	for (i = 0; i < (pArray->nLength-1); i++)
	{
		pArray->pElem[i] = pArray->pElem[i+1];
	}
	pArray->nLength--;

	return JWARRAY_TRUE;
}


3.双链式队列

存储结构

typedef struct tagJWListNode
{
	JWListElem				elem;		//数据元素
	struct tagJWListNode	*pNext;		//下一个节点指针
	struct tagJWListNode	*pPrior;	//上一个节点指针
}JWListNode, *PJWListNode;

typedef struct tagJWList
{
	int						nLength;	//数据元素
	struct tagJWListNode	*pNext;		//下一个节点指针
}JWList, *PJWList;

核心程序

/************************************************************************/
/* 获得当前队列头元素的值
** 注意在这里最靠近头结点的节点为队列头元素									*/
/************************************************************************/
JWList_BOOL JWListGetHead( JWList *pList, JWListElem *pElem )
{
	//判断栈为空的情形
	if (NULL == pList->pNext)
	{
		return JWLIST_FALSE;
	}

	//获取栈顶元素值
	*pElem = pList->pNext->elem;

	return JWLIST_TRUE;
}

/************************************************************************/
/* 设置当前队列元素的值
** 注意在这里最靠近头结点的节点为队列头元素									*/
/************************************************************************/
JWList_BOOL JWListSetHead( JWList *pList, const JWListElem elem )
{
	//判断栈为空的情形
	if (NULL == pList->pNext)
	{
		return JWLIST_FALSE;
	}

	//设置栈顶元素值
	pList->pNext->elem = elem;

	return JWLIST_TRUE;
}

/************************************************************************/
/* 当前队列入列一个元素										              */
/************************************************************************/
JWList_BOOL JWListEnQueue( JWList *pList, const JWListElem elem )
{
	JWListNode *pNewNode, *pNode;

	pNode = pList->pNext;

	//添加指定节点
	if(NULL == (pNewNode = (PJWListNode)malloc(sizeof(JWListNode))))
	{
		return JWLIST_FALSE;
	}
	pNewNode->elem			= elem;

	if (NULL == pNode)//当前队列为空
	{
		pNewNode->pNext = NULL;
		pNewNode->pPrior = NULL;
		pList->pNext = pNewNode;
	}
	else
	{
		//找到最后一个元素节点
		while (NULL != pNode->pNext)
		{
			pNode = pNode->pNext;
		}

		pNewNode->pNext = NULL;
		pNewNode->pPrior = pNode;
		pNode->pNext = pNewNode;
	}

	//长度+1
	pList->nLength += 1;

	return JWLIST_TRUE;
}

/************************************************************************/
/* 当前队列出列一个元素			                                                 */
/************************************************************************/
JWList_BOOL JWListDeQueue( JWList *pList, JWListElem *pElem )
{
	JWListNode *pFirstNode;

	pFirstNode = pList->pNext;

	//判断队列为空的情形
	if (NULL == pFirstNode)
	{
		return JWLIST_FALSE;
	}

	//删除队列头元素(修改相关指针)
	pList->pNext = pFirstNode->pNext;
	if(NULL != pFirstNode->pNext)//不是删除末尾节点
	{
		pFirstNode->pNext->pPrior = NULL;
	}

	//长度-1
	pList->nLength -= 1;

	//获取队列头元素
	if (NULL != pElem)
	{
		*pElem = pFirstNode->elem;
	}

	//清除队列头元素内存
	free(pFirstNode);

	return JWLIST_TRUE;
}


4.程序说明

1.这里为了保证统一性,顺序结构的队列出列时需要移动后面所有数据元素,最坏时间复杂度为O(n),链式结构的队列入列时需要遍历到最后一个数据元素,最坏时间复杂度为O(n)。这里只是为了完成功能,对于计算量不大的场合完全可以满足使用,对于计算量大的场合则需要另外单独实现一个队列,毕竟统一性和性能这里不可兼得。但是不论这里的代码还是单独编写的高效队列代码原理都是一样的,搞懂了原理,没什么难度。

2.注意这里链式队列新的数据结构,头结点和元素节点的结构是不一样的,在插入和删除操作时要注意和头结点相关的边界条件的判断。

3.队列还有双端队列和循环队列,前者应用不多,后者在数据元素长度固定时应用较广,要注意其“假满”状态。

4.至此,JWArray和JWList编写完毕,分别把把顺序结构及链式结构的线性表、栈和队列操作统一起来。在编写队列代码的同时,我对之前的线性表和栈代码做了一些修改和新增了一些实用函数,现在在一些场合,你可以使用JWArray和JWList做为自己的C语言线性表、栈和队列操作库了。


完整的源代码下载链接(包括了线性表、栈、队列)

原创,转载请注明来自http://blog.csdn.net/wenzhou1219


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值