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