双向链表
一个数据节点 保存的数据
保存逻辑上的下一个(后继节点)
也保存逻辑上的上一个(前驱节点)
不带头结点的双向链表
带头结点的双向链表
代码实现: "带头结点的双向链表"
//数据元素的类型
typedef int ElemType; //数据元素中数据的类型
typedef struct node
{
ElemType data; //数据域 --> 存储数据
struct node * next; //指针域 --> 保存逻辑上的下一个
struct node * prev; //保存逻辑上的上一个
} DNode;//头结点的类型
typedef struct List
{
struct node * first; //指向链表的第一个节点
struct node * last; //指向链表的最后一个节点
int num; //记录链表中元素的个数
//...
} DList;
基本操作: 增删改查
练习:
1)根据用户输入的数据的顺序, 创建一个带头结点的双向链表
将新链表返回, 然后再打印输出
BothwayLinkedListWithHead.c / BothwayLinkedListWithHead.h
DList * create_bothway_list()
{
//1.创建一个头结点, 并初始化
DList * list = (DList *)malloc( sizeof(DList) );
list->first = NULL;
list->last = NULL;
list->num = 0;
ElemType d;
while(1)
{
//2.获取数据
scanf("%d", &d );
if( d == 0 )
{
break;
}
//3.创建数据节点,并初始化
DNode * pnew = (DNode*)malloc( sizeof(DNode) );
pnew->data = d;
pnew->next = NULL;
pnew->prev = NULL;
//4.把新节点 加入到链表中
if( list->first == NULL ) //从无到有
{
list->first = pnew;
list->last = pnew;
}
else //从少到多
{
//尾插法
list->last->next = pnew;
pnew->prev = list->last;
list->last = pnew;
//头插法
//pnew->next = list->first;
//list->first->prev = pnew;
//list->first = pnew;
}
list->num ++; //元素个数 +1
}
//5.将新链表返回
return list;
}
//打印链表
void print_list( DList * list )
{
if( list == NULL ) //链表不存在
{
printf("list is NULL \n");
return ;
}
//正向
DNode * p = list->first; //遍历指针
while( p )
{
printf("%d ", p->data );
p = p->next;
}
putchar('\n');
//倒序
p = list->last;
while( p )
{
printf("%d ", p->data );
p = p->prev;
}
putchar('\n');
printf("num = %d\n\n", list->num );
}
main.c
#include "BothwayLinkedListWithHead.h"int main()
{
}
2)根据用户输入的数据的顺序, 逆序创建一个带头结点的双向链表
将新链表返回, 然后再打印输出
//头插法
3)根据用户输入的数据, 创建一个有序的带头结点的双向链表 (升序), 将新链表返回
/*
分情况讨论:
比第一个还要小, 头插法
比最后一个还要大, 尾插法
中间插入, 找到第一个比它大的数的前面进行插入
注意: 中间插入时, 先赋值pnew的成员, 再去修复前后的关系
☆☆☆
*/
DList * create_sort_bothway_list()
{
//1.创建一个头结点, 并初始化
DList * list = (DList *)malloc( sizeof(DList) );
list->first = NULL;
list->last = NULL;
list->num = 0;
ElemType d;
while(1)
{
//2.获取数据
scanf("%d", &d );
if( d == 0 )
{
break;
}
//3.创建数据节点,并初始化
DNode * pnew = (DNode*)malloc( sizeof(DNode) );
pnew->data = d;
pnew->next = NULL;
pnew->prev = NULL;
//4.把新节点 加入到链表中
if( list->first == NULL ) //从无到有
{
list->first = pnew;
list->last = pnew;
}
else //从少到多
{
/*
分情况讨论:
比第一个还要小, 头插法
比最后一个还要大, 尾插法
中间插入, 找到第一个比它大的数的前面进行插入
*/
DNode * p = list->first; //遍历指针
while( p )
{
if( p->data > pnew->data )
{
break; //找到了
}
p = p->next;
}
if( p == NULL ) //没找到
{
//尾插法
list->last->next = pnew;
pnew->prev = list->last;
list->last = pnew;
}
else //找到了
{
if( p == list->first )
{
//头插法
pnew->next = list->first;
list->first->prev = pnew;
list->first = pnew;
}
else
{
//中间插入
//注意: 先赋值pnew的成员, 再去修复前后的关系
pnew->next = p;
pnew->prev = p->prev;
p->prev->next = pnew;
p->prev = pnew;
}
}
}
list->num ++; //元素个数 +1
}
//5.将新链表返回
return list;
}
4)在双向链表中找到值为x的节点, 将其全部删除
如果没有找到 就不删除, 将新链表返回
注意: 删除中间节点时, 先去修复前后的关系, 再去断开自身的连接
☆☆☆
DList * delete_all_x_node( DList * list, ElemType x )
{
if( list == NULL ) //链表不存在
{
printf("list is NULL \n");
return NULL;
}
//1.找到值为x的节点
DNode * p = list->first; //遍历指针
while( p )
{
if( p->data == x )
{
//找到了,就删除(分情况讨论)
list->num --; //元素个数 -1
if( p == list->first ) //删除第一个节点 (要考虑只剩下一个节点的情况)
{
list->first = list->first->next;
if( list->first != NULL ) // list->num != 0
{
list->first->prev = NULL;
}
p->next = NULL;
free( p );
p = list->first; //p从下一个开始再去找
//当唯一的一个节点也删除了
if( list->first == NULL ) // list->num == 0
{
list->last = NULL;
}
}
else if( p == list->last ) //删除最后一个节点
{
list->last = list->last->prev;
if( list->last != NULL )
{
list->last->next = NULL;
}
p->prev = NULL;
free( p );
p = NULL; //已经删到末尾了,就不需要继续了
}
else //删除中间的节点
{
DNode * temp = p->next;
//先去修复前后的关系, 再去断开自身的连接
p->prev->next = p->next;
p->next->prev = p->prev;
p->next = NULL;
p->prev = NULL;
free( p );
p = temp; //继续往下找
}
}
else
{
//没找到. 就继续往下找
p = p->next;
}
}
//将新链表返回
return list;
}
5)在双向链表中找到值为x的节点, 将所有值为x的节点的值 修改成a
没有找到就不修改, 将新链表返回
DList * update_node( DList * list, ElemType x , ElemType a )
{
if( list == NULL ) //链表不存在
{
printf("list is NULL \n");
return NULL;
}
DNode * p = list->first; //遍历指针
while( p )
{
if( p->data == x ) //找到了
{
p->data = a;
}
p = p->next;
}
return list;
}
6)销毁一条双向链表
先释放每一个数据节点
再去释放头结点
void destroy_list( DList * list )
{
if( list == NULL ) //链表不存在
{
printf("list is NULL \n");
return ;
}
DNode * p = list->first; //遍历指针
//先释放每一个数据节点
while( p )
{
list->first = list->first->next;
if( list->first != NULL )
{
//如果是删除最后一个节点时, first再往下走就已经为NULL了,
//再去访问这个prev就会产生"段错误"
list->first->prev = NULL;
}
p->next = NULL;
free(p);
p = list->first;
}
//再去释放头结点
list->first = NULL;
list->last = NULL;
list->num = 0;
free( list );
}