赞
踩
什么是双向链表?
双向链表顾名思义,就是链表由单向的链变成了双向链。双向链表(double linked list)是在单链表的每个结点中再设置一个指向其前驱结点的指针域。
//双链表节点结构体
typedef struct DoubleLinkNode
{
char data;
struct DoubleLinkNode* prior;
struct DoubleLinkNode* next;
} Node,*NodePtr;
//初始化
NodePtr initLinkList()
{
NodePtr LinkHeader = (NodePtr)malloc(sizeof(Node));
LinkHeader->data = '\0';
LinkHeader->next = NULL;
LinkHeader->prior = NULL;
Linklength = 0;
return LinkHeader;
}
双向链表的插入其实并不复杂,不过顺序很重要,千万不能写反了。
我们假设存储e的节点为q,要实现将结点q插入到结点p和p -> next之间需要下面几步,如下图所示。
顺序是先搞定q的前驱和后继,再搞定后结点的前驱,最后解决前结点的后继
代码如下:
//在某位置插入 void ListInsert(NodePtr LinkHeader, int InsertPosition, char InsertChar) { if(InsertPosition < 0 || InsertPosition > Linklength) { printf("The position %d out of range of linked list!\n",InsertPosition); return ; } NodePtr p,q,r,tail; p = LinkHeader; for(int i = 0; i < InsertPosition; ++i) { p = p->next; if(!p) { printf("The position %d out of range of linked list!\n",InsertPosition); return ; } } q = (NodePtr)malloc(sizeof(Node)); q->data = InsertChar; r = p->next; q->prior = p; q->next = r; p->next = q; if(r) { r->prior = q; } Linklength++; }
如果插入操作理解了,那么删除操作就比较简单了。
若要删除节点q,只需要下面两个步骤,如下图所示。
代码如下:
//删除第Position个节点 void ListDeleteByPosition(NodePtr LinkHeader, int Position) { NodePtr p,q,r,tail; int j = 0; tail = tailNodeSearch(LinkHeader); p = LinkHeader; while(p->next && j < Position) { p = p->next; ++j; } if(!(p->next) || j > Position) { printf("Can't delete it!\n"); return ; } q = p->next; r = q->next; p->next = r; if(r) { r->prior = p; } free(q); Linklength--; }
//删除第一个数据域为x的节点 void ListDeleteByData(NodePtr LinkHeader, char DeleteChar) { NodePtr p,q,r; p = LinkHeader; while(p->next && p->next->data != DeleteChar) { p = p->next; } if(!(p->next)) { printf("The char '%c' does't exist.\n",DeleteChar); return ; } q = p->next; r = q->next; p->next = r; if(r) { r->prior = p; } free(q); Linklength--; }
双向链表与单链表不同的是单链表只能单向读取,双向链表可以双向读取,因此在反向打印时,需要先找到尾结点
//寻找尾节点
NodePtr tailNodeSearch(NodePtr LinkHeader)
{
NodePtr p = LinkHeader;
while(p->next)
{
p = p->next;
}
return p;
}
//正向打印
void printListByHead(NodePtr LinkHeader)
{
NodePtr p = LinkHeader->next;
while (p)
{
printf("%c",p->data);
p = p->next;
}
printf("\n");
}
//反向打印
void printListByTail(NodePtr LinkHeader)
{
NodePtr tail = tailNodeSearch(LinkHeader);
NodePtr p = tail;
while (p)
{
printf("%c",p->data);
p = p->prior;
}
printf("\n");
}
双链表除了有一个指向下一结点的指针外,还有一个指向前一结点的指针,可以双向遍历,故双链表的在查找时可利用二分法的思想去实现,那么这样效率就会大大提高。
//链表节点的读取(打印链表中第position个数据元素的值) void GetElement(NodePtr LinkHeader, int position) { NodePtr p,q,r; if(position <= Linklength/2) { p = LinkHeader->next; int j = 0; while(p && j < position) { p = p->next; ++j; } if(!p || j > position) { printf("Can't get it !\n"); return ; } printf("The element at its %d-th position is %c\n",position,p->data); }else { p = tailNodeSearch(LinkHeader); int j = 0; while(p->prior && j < Linklength-position-1) { p = p->prior; ++j; } if(!p || j > Linklength-position-1) { printf("Can't get it !\n"); return ; } printf("The element at its %d-th position is %c\n",position,p->data); } }
#include <stdio.h> #include <stdlib.h> int Linklength; //双链表节点结构体 typedef struct DoubleLinkNode { char data; struct DoubleLinkNode* prior; struct DoubleLinkNode* next; } Node,*NodePtr; //初始化 NodePtr initLinkList() { NodePtr LinkHeader = (NodePtr)malloc(sizeof(Node)); LinkHeader->data = '\0'; LinkHeader->next = NULL; LinkHeader->prior = NULL; Linklength = 0; return LinkHeader; } //寻找尾节点 NodePtr tailNodeSearch(NodePtr LinkHeader) { NodePtr p = LinkHeader; while(p->next) { p = p->next; } return p; } //正向打印 void printListByHead(NodePtr LinkHeader) { NodePtr p = LinkHeader->next; while (p) { printf("%c",p->data); p = p->next; } printf("\n"); } //反向打印 void printListByTail(NodePtr LinkHeader) { NodePtr tail = tailNodeSearch(LinkHeader); NodePtr p = tail; while (p) { printf("%c",p->data); p = p->prior; } printf("\n"); } //在某位置插入 void ListInsert(NodePtr LinkHeader, int InsertPosition, char InsertChar) { if(InsertPosition < 0 || InsertPosition > Linklength) { printf("The position %d out of range of linked list!\n",InsertPosition); return ; } NodePtr p,q,r,tail; p = LinkHeader; for(int i = 0; i < InsertPosition; ++i) { p = p->next; if(!p) { printf("The position %d out of range of linked list!\n",InsertPosition); return ; } } q = (NodePtr)malloc(sizeof(Node)); q->data = InsertChar; r = p->next; q->prior = p; q->next = r; p->next = q; if(r) { r->prior = q; } Linklength++; } //删除第一个数据域为x的节点 void ListDeleteByData(NodePtr LinkHeader, char DeleteChar) { NodePtr p,q,r; p = LinkHeader; while(p->next && p->next->data != DeleteChar) { p = p->next; } if(!(p->next)) { printf("The char '%c' does't exist.\n",DeleteChar); return ; } q = p->next; r = q->next; p->next = r; if(r) { r->prior = p; } free(q); Linklength--; } //删除第Position个节点 void ListDeleteByPosition(NodePtr LinkHeader, int Position) { NodePtr p,q,r,tail; int j = 0; tail = tailNodeSearch(LinkHeader); p = LinkHeader; while(p->next && j < Position) { p = p->next; ++j; } if(!(p->next) || j > Position) { printf("Can't delete it!\n"); return ; } q = p->next; r = q->next; p->next = r; if(r) { r->prior = p; } free(q); Linklength--; } //链表节点的读取(打印链表中第position个数据元素的值) void GetElement(NodePtr LinkHeader, int position) { NodePtr p,q,r; if(position <= Linklength/2) { p = LinkHeader->next; int j = 0; while(p && j < position) { p = p->next; ++j; } if(!p || j > position) { printf("Can't get it !\n"); return ; } printf("The element at its %d-th position is %c\n",position,p->data); }else { p = tailNodeSearch(LinkHeader); int j = 0; while(p->prior && j < Linklength-position-1) { p = p->prior; ++j; } if(!p || j > Linklength-position-1) { printf("Can't get it !\n"); return ; } printf("The element at its %d-th position is %c\n",position,p->data); } } //测试 void insertDeleteTest() { printf("---------------Initialize bidirectional linked list--------------\n"); NodePtr tempList = initLinkList(); printListByHead(tempList); printListByTail(tempList); printf("---------------Inserts a node at the specified location--------------\n"); ListInsert(tempList,0,'H'); ListInsert(tempList,1,'e'); ListInsert(tempList,2,'l'); ListInsert(tempList,3,'l'); ListInsert(tempList,4,'o'); printListByHead(tempList); printListByTail(tempList); printf("---------------Gets the node data field at the specified location--------------\n"); GetElement(tempList,0); GetElement(tempList,4); GetElement(tempList,5); printf("---------------Delete the first node whose data field is X--------------\n"); ListDeleteByData(tempList,'e'); printListByHead(tempList); printListByTail(tempList); printf("---------------Delete the position node--------------\n"); ListDeleteByPosition(tempList,3); printListByHead(tempList); printListByTail(tempList); } int main() { insertDeleteTest(); }
双向链表相当于单链表来说,要复杂一些,对于插入和删除要格外小心。另外它由于每个结点都需要记录两份指针,所以在空间上的占用要略微多一些,说白了就是用空间换取时间。
双链表在查找时,我们可以借用二分法的思路,从head(首节点)向后查找操作和last(尾节点)向前查找操作同步进行,这样双链表的效率可以提高一倍。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。