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

第十九节 数据结构之双向链表

2013-11-24 23:44 459 查看
在单向链表中,获取当前节点的下一个节点,即可使用ptr = ptr->next,但是如果要获取当前节点的上一个节点,则需要重新遍历一次链表。但是如果有指向上一个节点的指针,ptr
= ptr->befor ,这样即可获取当前节点的上一个节点。虽然增加了空间上的执行,但是时间效率却提高了,这也算是以空间换时间的一种方法吧。其实老子在《道德经》中就多次提到时间和空间的置换,两者在一定条件下是要互相变通。

[align=left]每一个节点应该保存的数据,有数据指针、指向下一个节点的指针、指针上一个节点的指针。[/align]
[align=left]/* 双向链表节点数据结构 */[/align]
typedef struct _DoubleListNode
{
void
*pData ; //数据指针,用于保存链表元素的个数
struct _DoubleListNode
*pNext ; //指向下一个节点的指针
struct _DoubleListNode
*pPrev ; //指向前一个节点的指针
[align=left]} DoubleListNode , *POSITION;[/align]
[align=left]相对于总个链表,肯定需要一个头节点,让头节点的pNext指向第一个节点,而头节点的pPrev指向最后一个节点,而第一个节点的pPrev和最后一个节点的pNext都是指向头节点,这样就形成了循环链表。如果将头节点的的数据域空间用来存储链表节点的个数,这样我们就能明了链表的长度。[/align]

1.1 创建链表
[align=left][/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListCreate[/align]
[align=left]** 函数功能: 创建链表函数[/align]

该函数虽然有一个参数,为函数指针,但是在创建函数中并不调用它,

而仅仅是保存它。这种保存而不调用回调函数的方法也叫注册回调函数。

如果不需要销毁元素,则只需要传入NULL即可。
[align=left]** 入口参数: DestroyFunc:[/align]
[align=left]** 出口参数: 如果创建失败,则返回NULL;成功则返回一个双向链表结构体的指针[/align]
[align=left]******************************************************************************/[/align]
LIST *ListCreate(DESTROYFUNC
DestroyFunc)
[align=left]先开辟一个链表结构体空间,同时为头节点的所有成员初始化,初始状态下,pNext和pPrev都是指向本身,头节点的数据域用来保存节点个数。[/align]
[align=left]LIST *pList ;[/align]
pList = (LIST *)malloc(sizeof(LIST))
; //申请存储空间,建立头结点
[align=left][/align]
[align=left] if (pList) {[/align]
[align=left] /* 初始时,头结点两个指针都指向头结点本身 */[/align]
pList->nodeHead.pNext = &(pList->nodeHead)
;
pList->nodeHead.pPrev = &(pList->nodeHead)
;
pList->nodeHead.pData = 0
; //初始时节点个数为0
pList->DestroyFunc
= DestroyFunc ; //注册销毁函数
[align=left] }[/align]
return pList ;
//返回链表结构体的指针

1.1.1 获取链表第一个节点位置
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListGetBegin[/align]
[align=left]** 函数功能: 获取链表第一个节点的位置[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left]** 出口参数: pList->nodeHead.pNext:返回头结点指针[/align]
[align=left]******************************************************************************/[/align]
POSITION ListGetBegin( PLIST
pList )
[align=left]{[/align]
[align=left] assert(pList) ;[/align]
return (pList->nodeHead.pNext)
;
[align=left]}[/align]
[align=left][/align]
1.1.2 获取链表最后一个节点的下一个节点位置
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListGetEnd[/align]
[align=left]** 函数功能: 获取链表最后一个节点的下一个节点的位置[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left]** 出口参数: &(pList->nodeHead):返回最后一个节点的下一个节点地址[/align]
[align=left]******************************************************************************/[/align]
POSITION ListGetEnd( PLIST
pList )
[align=left]{[/align]
[align=left] assert(pList) ;[/align]
return (&(pList->nodeHead))
;
[align=left]}[/align]
[align=left][/align]
1.1.3 获取链表的元素个数
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListGetSize[/align]
[align=left]** 函数功能: 获取链表元素的个数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
** 出口参数: (unsigned
int)(pList->nodeHead.pData):返回链表个数
[align=left]******************************************************************************/[/align]
unsigned int ListGetSize(
PLIST pList )
[align=left]{[/align]
[align=left] assert(pList) ;[/align]
return ( (unsigned
int)(pList->nodeHead.pData) ) ;
[align=left]}[/align]
[align=left][/align]
[align=left][/align]
1.1.4 由位置获取数据的值
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListGetData[/align]
[align=left]** 函数功能: 由位置获取数据的值[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left] pos :待插入节点的位置指针[/align]
[align=left]** 出口参数:[/align]
[align=left]******************************************************************************/[/align]
DATATYPE ListGetData( PLIST
pList , POSITION pos )
[align=left]{[/align]
[align=left] assert(pList) ;[/align]
[align=left] assert(pos) ;[/align]
return (pos->pData)
;
[align=left]}[/align]

1.1 插入节点1.1.1
将数据插入到指定位置
[align=left]要想将数据data插入到双向链表中,则必须先为待插入数据data的节点申请存储空间,并且建立指向该节点的指针pNewNode,用于 存储新节点的地址。[/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListInsert[/align]
[align=left]** 函数功能: 将数据插入到链表的指定位置函数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left] pos :为待插入节点的位置指针[/align]

data :为待插入节点的数据指针
[align=left]** 出口参数: 如果失败,则返回FALSE;如果成功则返回TRUE[/align]
[align=left]******************************************************************************/[/align]
BOOL ListInsert( PLIST pList
, POSITION pos , DATATYPE data )
[align=left]插入节点第一步,为待插入数据data的节点申请存储空间;并将data数据存储在新建节点的数据域。[/align]
DoubleListNode *pNewNode
;
unsigned int uiTemp
= 0 ;
[align=left] assert(pList) ;[/align]
[align=left] assert(pos) ;[/align]
pNewNode = (DoubleListNode
*)malloc(sizeof(DoubleListNode)) ;

pNewNode->pData = data ;

插入节点第二步,将待插入节点的前向指针指向pos所指向的节点。

pNewNode->pPrev = pos ;

[align=left]插入节点第三步,将待插入节点的后向指针指向pos指向的下一个节点。[/align]
[align=left]pNewNode->pNext = pos->pNext ;[/align]
[align=left][/align]
[align=left]插入节点第四步,pos指向节点的下一节点的前向指针指向pNewNode。[/align]
[align=left]pos->pNext->pPrev = pNewNode ;[/align]
[align=left][/align]

[align=left]插入节点第五步,pos指向节点的后向指针pNewNode。[/align]
[align=left]pos->pNext = pNewNode ;[/align]

[align=left]uiTemp = ((unsigned int)(pList->nodeHead.pData)) ;[/align]
[align=left] uiTemp++ ;[/align]
[align=left] (pList->nodeHead.pData) = ( void * ) uiTemp ;[/align]
[align=left][/align]
[align=left] return TRUE ;[/align]
[align=left]成功插入节点后,链表的节点数要加1,返回成功插入的标志。[/align]

1.1.1 将数据插入到链表头成为第一个节点
[align=left]也就是说吃插入在头结点后面,成为第一个节点。那么同样要知道头节点的地址,即为&pList->nodeHead。[/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListPushFront[/align]
[align=left]** 函数功能: 将数据插入到链表头函数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left] data :为待插入节点的数据指针[/align]
[align=left]** 出口参数: 如果失败,则返回FALSE;如果成功则返回TRUE[/align]
[align=left]******************************************************************************/[/align]
[align=left]BOOL ListPushFront( PLIST pList , DATATYPE data )[/align]
[align=left]{[/align]
[align=left] return (ListInsert(pList , &(pList->nodeHead) ,data)) ;[/align]
[align=left]}[/align]
1.1.2 将数据插入到链表尾成为最后一个节点
[align=left]这里我们可以调用上面函数实现,只要我们知道链表尾的地址和待插入的数据,我们就能将数据插入到链表尾。链表最后一个节点的地址即为头结点的前向指针pList->nodeHead.pPrev。由于上面返回值只有TRUE和FALSE。则函数类型可以定义为BOOL型。[/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListPushBack[/align]
[align=left]** 函数功能: 将数据插入到链表尾函数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left] data :为待插入节点的数据指针[/align]
[align=left]** 出口参数: 如果失败,则返回FALSE;如果成功则返回TRUE[/align]
[align=left]******************************************************************************/[/align]
[align=left]BOOL ListPushBack( PLIST pList , DATATYPE data )[/align]
[align=left]{[/align]
[align=left] return (ListInsert(pList , (pList->nodeHead.pPrev) , data)) ;[/align]
[align=left]}[/align]
[align=left][/align]
[align=left][/align]
1.2 删除节点1.2.1
删除指定位置的节点
[align=left][/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListErase[/align]
[align=left]** 函数功能: 删除指定位置节点函数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left] data :为待插入节点的数据指针[/align]
[align=left]** 出口参数: 如果失败,则返回FALSE;如果成功则返回TRUE[/align]
[align=left]******************************************************************************/[/align]
[align=left]BOOL ListDelete( PLIST pList , POSITION pos )[/align]
[align=left]将指向待删除节点的两个指针重新指向即可,先判断待删除节点是否为头节点。[/align]
[align=left]unsigned int uiTemp = 0 ;[/align]
[align=left] assert(pList) ;[/align]
[align=left] /* 不允许删除头节点 */[/align]
[align=left] assert( pos && (pos != &(pList->nodeHead)) ) ;[/align]

[align=left]让(pos指向节点的前一个节点的后向指针)指向(pos指向节点的下一个节点)。[/align]
[align=left]pos->pPrev->pNext = pos->pNext ;[/align]
[align=left][/align]

[align=left]让(pos指向节点的后一个节点的前向指针)指向(pos指向节点的前一个节点)。[/align]
[align=left]pos->pNext->pPrev = pos->pPrev ;[/align]
[align=left][/align]
[align=left]如果用户定义了销毁函数,则执行调用销毁函数。然后释放待删除节点所占的空间。[/align]
[align=left]if (pList->DestroyFunc) {[/align]
[align=left] pList->DestroyFunc(pos->pData) ; [/align]
[align=left] }[/align]

图3. 11 free(pos)

free(pos) ;
[align=left][/align]
[align=left][/align]
[align=left]成功删除节点后,链表节点数要减1,并且返回TRUE。[/align]
[align=left]uiTemp = ((unsigned int)(pList->nodeHead.pData)) ;[/align]
[align=left] uiTemp++ ;[/align]
[align=left] (pList->nodeHead.pData) = (void *)uiTemp ;[/align]
[align=left][/align]
[align=left] return TRUE ;[/align]
1.1.1 删除链表头节点后的节点(即第一个节点)
[align=left]只要知道第一个节点的地址即可调用上面函数删除第一个节点。而第一个节点的地址为:pList->nodeHead.pNext。[/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListPopFront[/align]
[align=left]** 函数功能: 删除链表头节点函数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left]** 出口参数: 如果失败,则返回FALSE;如果成功则返回TRUE[/align]
[align=left]******************************************************************************/[/align]
[align=left]BOOL ListPopFront( PLIST pList )[/align]
[align=left]{[/align]
[align=left] return ( ListDelete(pList , (pList->nodeHead.pNext)) ) ;[/align]
[align=left]}[/align]
1.1.2 删除链表的尾节点
[align=left]尾节点地址为:pList->nodeHead.pPrev。[/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListPopBack[/align]
[align=left]** 函数功能: 删除链表尾节点函数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left]** 出口参数: 如果失败,则返回FALSE;如果成功则返回TRUE[/align]
[align=left]******************************************************************************/[/align]
[align=left]BOOL ListPopBack( PLIST pList )[/align]
[align=left]{[/align]
[align=left] return ( ListDelete(pList , (pList->nodeHead.pPrev)) ) ;[/align]
[align=left]}[/align]
1.2 按值搜索位置算法
[align=left]从第一个节点开始搜索,直到搜索到数据域为data的节点为止;如果从第一个节点搜索到最后一个节点都没有搜索到数据域为data的节点,则返回NULL。[/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListFind[/align]
[align=left]** 函数功能: 搜索算法函数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left] data :要查找的匹配数据[/align]
[align=left] CompareFunc:数据匹配比较函数[/align]
[align=left]** 出口参数:[/align]
[align=left]******************************************************************************/[/align]
[align=left]POSITION ListFind( PLIST pList , DATATYPE data , COMPAREFUNC CompareFunc )[/align]
[align=left]{[/align]
[align=left] DoubleListNode *pNode , *pEndNode ;[/align]
[align=left] assert(pList) ;[/align]
[align=left][/align]
[align=left] pNode = ListGetBegin(pList) ; //获取第一个节点位置[/align]
[align=left] pEndNode = ListGetEnd(pList) ; //获取最后一个节点的下一个节点位置[/align]
[align=left][/align]
[align=left] /* 从头到尾开始找 */[/align]
[align=left] while (pNode != pEndNode) {[/align]
[align=left] /* 如果找到则返回 */[/align]
[align=left] if ( 0 == CompareFunc((pNode->pData),data) ) {[/align]
[align=left] return pNode ;[/align]
[align=left] }[/align]
[align=left] pNode = pNode->pNext ;[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] return (NULL) ;[/align]
[align=left]}[/align]

1.1 遍历算法
[align=left]这个函数主要是在遍历双向链表的同时要对每一个节点进行怎么样的操作,这个操作由用户自己决定,入口参数只是给定了一个函数指针TRAVERSEFUNC,由用户自己调用。[/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListTraverse[/align]
[align=left]** 函数功能: 遍历算法函数[/align]
[align=left]** 入口参数: pList:要操作的双向链表指针[/align]
[align=left] TraverseFunc:节点数据的遍历操作函数[/align]
[align=left]** 出口参数: 如果失败,则返回 0 ;如果成功则返回 1[/align]
[align=left]******************************************************************************/[/align]
[align=left]void ListTraverse( PLIST pList , TRAVERSEFUNC TraverseFunc )[/align]
[align=left]{[/align]
[align=left] DoubleListNode *pNode , *pEndNode ;[/align]
[align=left] assert(pList) ;[/align]
[align=left][/align]
[align=left] pNode = ListGetBegin(pList) ; //获取第一个节点的位置[/align]
[align=left] pEndNode = ListGetEnd(pList) ; //获取最后一个节点的下一个节点位置[/align]
[align=left][/align]
[align=left] /* 从头到尾开始遍历 */[/align]
[align=left] while (pNode != pEndNode) {[/align]
[align=left] TraverseFunc(pNode->pData) ; //访问数据[/align]
[align=left] }[/align]
[align=left][/align]
[align=left]}[/align]
[align=left][/align]
1.2 销毁链表
[align=left]从最后一个节点开始逐个删除节点,一直到头结点为止。最后再释放掉链表所占空间。[/align]
[align=left]/******************************************************************************[/align]
[align=left]** 函数名称: ListDestroy[/align]
[align=left]** 函数功能: 销毁链表函数[/align]
[align=left]** 入口参数: pList:要释放的双链表指针[/align]
[align=left]** 出口参数: void[/align]
[align=left]******************************************************************************/[/align]
[align=left]void ListDestroy( PLIST pList )[/align]
[align=left]{[/align]
[align=left] assert(pList) ;[/align]
[align=left][/align]
[align=left] while( 0 != ListGetSize(pList) ) {[/align]
[align=left] ListPopBack(pList) ; //删除链表尾节点[/align]
[align=left] }[/align]
[align=left][/align]
[align=left] free(pList) ; //释放节点所占的空间[/align]
[align=left][/align]
[align=left]}[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: