队列是一种先进先出(FIFO)的数据结构,和栈一样在线性表的基础上扩展而来,相对于线性表可任意位置插入和删除,它只允许在一端插入,在另一端删除,又叫入列和出列操作。
和栈一样,队列程序设计中经常出现,最典型的就是操作系统中的作业排队:在多道程序共同运行的计算机系统中,如果运行的程序都要输出,那么就按照请求输出的顺序排队,最先请求的最先输出,类似的还应用在网络请求、多线程程序设计等地方。
对于队列的存储结构设计,一般的理论设计如下:
typedef struct tagQueue { QueueElem *pFront; //队列头元素指针 QueueElem *pRear; //队列尾元素指针 }Queue;
队列空时pFront=pRear
同栈的实现一样,我们想把顺序结构及链式结构的线性表、栈和队列统一起来,在这里我没有使用通用设计,直接使用原来的线性表存储数据:顺序线性表的下标最大的数据元素即为队列尾元素,队列头元素为下标最小的元素,长度为0时队列为空;重新设计链式存储表的结构使头结点和元素节点不同,队列头元素为第一个元素节点,队列尾元素为最后一个元素节点,当头结点指向NULL时,队列为空。
存储结构
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; }
存储结构
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; }
1.这里为了保证统一性,顺序结构的队列出列时需要移动后面所有数据元素,最坏时间复杂度为O(n),链式结构的队列入列时需要遍历到最后一个数据元素,最坏时间复杂度为O(n)。这里只是为了完成功能,对于计算量不大的场合完全可以满足使用,对于计算量大的场合则需要另外单独实现一个队列,毕竟统一性和性能这里不可兼得。但是不论这里的代码还是单独编写的高效队列代码原理都是一样的,搞懂了原理,没什么难度。
2.注意这里链式队列新的数据结构,头结点和元素节点的结构是不一样的,在插入和删除操作时要注意和头结点相关的边界条件的判断。
3.队列还有双端队列和循环队列,前者应用不多,后者在数据元素长度固定时应用较广,要注意其“假满”状态。
4.至此,JWArray和JWList编写完毕,分别把把顺序结构及链式结构的线性表、栈和队列操作统一起来。在编写队列代码的同时,我对之前的线性表和栈代码做了一些修改和新增了一些实用函数,现在在一些场合,你可以使用JWArray和JWList做为自己的C语言线性表、栈和队列操作库了。
完整的源代码下载链接(包括了线性表、栈、队列)
原创,转载请注明来自http://blog.csdn.net/wenzhou1219