赞
踩
双向链表与单向链表较为类似,单向链表有一个指针域,用来指向后继结点,而双向链表有两个指针域,分别用来指向前驱结点和后继结点。玩双向链表时一定要从单向链表的思维中跳出来,否则在操作双向链表时就会出现各种问题。在玩双向链表时,一定要动手画图或者在脑子里画图,考虑好每个操作细节。

双向链表的数据结构:
- typedef struct node//定义双向链表的数据结构
- {
- int data;
- struct node * prior;
- struct node * next;
- }Node;
这里以整型作为结点数据域的数据类型
1. 初始化链表
Node * initialize();
创建头结点,初始化头结点的各个成员变量,返回头结点的指针
2.头插法添加结点
void insertFirst(Node * head,int data);
方法形参上需传入头结点指针和新结点的数据,方法内会创建新结点,之后操作新结点、头结点、首结点,将新结点插入首结点的位置
3.尾插法添加结点
void append(Node * head,int data);
方法形参上需传入头结点指针和新结点的数据,方法内会创建新结点,之后操作头结点、末结点、新结点,将新结点插入到末结点的后面
4.删除结点
int delete(Node * head,int data);
方法形参上需传入头结点指针和结点的数据,方法内会从首结点向末结点遍历,查找指定数据的结点,若查找成功,则删除该结点,返回1,若查找无果,返回0
5.反转链表
void reverse(Node * head);
方法形参上需传入头结点指针,方法内会将链表的首尾顺序进行颠倒,原先的首结点作尾结点,原先的尾结点作首结点
6.正向遍历链表
void traverse(Node * head);
方法形参上需传入头结点指针,方法内输出链表的所有结点数据,不包括头结点
7.反向遍历链表
void traverseReversely(Node * head);
方法形参上需传入头结点指针,方法内从末结点到首结点进行数据输出,不输出头结点
-
- /*
- 双向链表
- 初始化链表
- 增加结点(头插法、尾插法)
- 删除结点
- 反转链表
- 遍历链表(正向遍历,反向遍历)
- */
- #include<stdio.h>
- #include<stdlib.h>
-
- typedef struct node//定义双向链表的数据结构
- {
- int data;
- struct node * prior;
- struct node * next;
- }Node;
-
- Node * initialize();//初始化链表
- void insertFirst(Node * head,int data);//头插法添加结点
- void append(Node * head,int data);//尾插法添加结点
- int delete(Node * head,int data);//删除结点
- void reverse(Node * head);//反转链表
- void traverse(Node * head);//正向遍历链表
- void traverseReversely(Node * head);//反向遍历链表
-
- int main(int argc,char * argv[])
- {
- puts("------------------初始化链表------------------");
- Node * head=initialize();
- printf("head->prior=%p\n",head->prior);
- printf("head->next=%p\n",head->next);
- printf("head->data=%i\n",head->data);
-
- puts("------------------头插法插入30,50,80------------------");
- insertFirst(head,30);
- insertFirst(head,50);
- insertFirst(head,80);
-
- puts("------------------正向遍历链表------------------");
- traverse(head);
- puts("------------------反向遍历链表------------------");
- traverseReversely(head);
-
- puts("------------------尾插法插入18,19,20------------------");
- append(head,18);
- append(head,19);
- append(head,20);
- puts("------------------正向遍历链表------------------");
- traverse(head);
- puts("------------------反向遍历链表------------------");
- traverseReversely(head);
-
- puts("------------------删除数据为20的结点------------------");
- puts(delete(head,20)?"删除成功":"删除失败");
- traverse(head);
-
- puts("------------------删除数据为80的结点------------------");
- puts(delete(head,80)?"删除成功":"删除失败");
- traverse(head);
-
- puts("------------------删除数据为30的结点------------------");
- puts(delete(head,30)?"删除成功":"删除失败");
- traverse(head);
-
- puts("------------------尾插法插入90,150------------------");
- append(head,90);
- append(head,150);
- traverse(head);
-
- puts("------------------反转链表------------------");
- reverse(head);
- traverse(head);
-
- puts("------------------再反转链表------------------");
- reverse(head);
- traverse(head);
-
- return 0;
- }
-
- Node * initialize()//初始化链表
- {
- Node * head=(Node *)malloc(sizeof(Node));
- head->data=0;//头结点的数据域用来存储链表中结点的数量,不包括头结点
- head->prior=NULL;//头结点的前指针域置为空,在整个程序的运行过程中不作改变
- head->next=NULL;//头结点的后指针域暂时置为空,在整个程序的运行过程中会产生变化
- return head;
- }
-
- void insertFirst(Node * head,int data)//头插法添加结点
- {
- Node * newborn=(Node *)malloc(sizeof(Node));//创建新结点
- newborn->data=data;//为新结点的数据域赋值
- /*
- 采用头插法为双向链表添加结点时,
- 注意:链表中只有一个头结点时,有三个指针变量需要赋值,
- 分别是新结点前指针域、后指针域,头结点的后指针域
- 链表中除了头结点外,还存在其它结点,有四个指针变量需要赋值
- 分别是新结点前指针域、后指针域,原先的首结点的前指针域,头结点的后指针域
- */
- newborn->prior=head;
- newborn->next=head->next;
- if(head->next!=NULL)//链表中不是只有头结点,头结点有后继结点,该后继结点就是首结点
- {
- head->next->prior=newborn;//将首结点的前指针域指向新结点
- }
- head->next=newborn;//头结点的后指针域指向新结点
- head->data++;//头结点的数据域自增
- }
-
- void append(Node * head,int data)//尾插法添加结点
- {
- Node * begin=head;//定义指针变量begin,初始值为头结点的指针,将其作为循环变量
- for(;begin->next!=NULL;begin=begin->next);//遍历至末结点
-
- Node * newborn=(Node *)malloc(sizeof(Node));//创建新结点
- newborn->data=data;//为新结点的数据域赋值
- newborn->prior=begin;//新结点的前指针域指向末结点
- newborn->next=begin->next;//把末结点后指针域的值赋给新结点的后指针域
- begin->next=newborn; //末结点的后指针域指向新结点
- head->data++;//链表中结点数量自增
- }
-
- int delete(Node * head,int data)//删除结点
- {
- for(Node * begin=head->next;begin;begin=begin->next)//从首结点向尾结点进行遍历
- {
- if(begin->data==data)//若查找到指定数据的结点,先问问该结点是否为末结点
- {
- if(begin->next!=NULL)//若非末结点
- {
- begin->prior->next=begin->next;
- begin->next->prior=begin->prior;
- }else//若为末结点
- {
- begin->prior->next=begin->next;
- }
- free(begin);//释放要删除的结点空间
- head->data--;//链表中的结点数量自减
- return 1;
- }
- }
- return 0;
- }
-
- void reverse(Node * head)//反转链表
- {
- //反转链表使用头插法完成
- Node * temporary;//临时变量,作一个中间存储
- Node * begin=head->next;//定义指针变量begin,存储首结点的指针
- head->next=NULL;//头结点的后指针域置为空
- while(begin)//从首结点向末结点进行遍历
- {
- temporary=begin->next;//将当前结点的后继结点指针存储到指针变量temporary中,
- //当前结点的两个指针域重新赋值后,就无法确定原先的后继结点,这里先把其后续结点指针存储起来
- begin->prior=head;//当前结点的前指针域指向头结点
- begin->next=head->next;//当前结点的后指针域指向头结点后指针域的指向
- if(head->next!=NULL)//若头结点后指针域不为空,则说明链表中不是只有头结点
- {
- head->next->prior=begin;//头结点的后继结点前指针域指向当前结点
- }
- head->next=begin;//头结点的后指针域指向当前结点
- begin=temporary;//指针变量begin从当前结点转到下一个结点
- }
- }
-
- void traverse(Node * head)//正向遍历链表
- {
- //头结点的数据域不进行展示,这里直接用head->next表示其它结点
- //用前驱的后指针域表示后继结点
- if(head->next->next==NULL)//到达末结点
- {
- printf("%d\n",head->next->data);
- return;
- }
- printf("%d , ",head->next->data);
- traverse(head->next);
- }
-
- void traverseReversely(Node * head)//反向遍历链表
- {
- Node * begin=head->next;//将首结点的指针赋给指针变量begin
- while(begin->next!=NULL)//将指针变量begin作为循环变量进行遍历,循环条件是begin未到达末结点
- {
- begin=begin->next;//指针变量begin向末结点遍历
- }
- while(begin!=head)
- {
- if(begin->prior==head)//到达首结点
- {
- printf("%d\n",begin->data);
- }else
- {
- printf("%d , ",begin->data);
- }
- begin=begin->prior;
- }
- }


玩双向链表,画图第一步,代码第二步,运行第三步。细心、耐心、恒心即可把玩双链表。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。