循环链表
单链表和双链表
如果遍历到最后一个节点, 需要返回到第一个节点, 则需要重新遍历一次
循环链表 "首尾相连"
单链表:
最后一个节点的next 指向 第一个节点 --> 单向循环链表
双向链表
最后一个节点的next 指向 第一个节点
第一个节点的prev 指向 最后一个节点 --> 双向循环链表
1) 单向循环链表
构造:
typedef int ElemType; //数据元素中 数据的类型
typedef struct node //数据元素的类型
{
ElemType data; //数据域 --> 存储数据
struct node * next; //指针域 --> 保存逻辑上的关系(下一个)
} Node;//链表的头结点的类型
typedef struct List
{
Node * first; //指向链表的第一个节点(head)
Node * last; //指向链表的最后一个节点(tail)
int num; //保存链表的数据元素的个数
//...
} List ;
基本操作: 增删改查
注意:
1) 首尾相连
if( list->num != 0 ) //链表不为空
{
list->last->next = list->first;
}2)遍历时, 循环条件变为节点个数
int n = list->num;
while( n-- ) //遍历
{
}
练习:
1)根据用户输入的数据的顺序, 创建一个带头结点的单向循环链表
并将新链表返回, 然后再打印输出
CircleLinkedListWithHead.c / CircleLinkedListWithHead.h
List * create_Circle_list()
{
//1.创建一个头结点, 并初始化
List * list = (List*)malloc( sizeof(List) );
list->first = NULL;
list->last = NULL;
list->num = 0;
ElemType d;
while(1)
{
//2.从键盘上获取数据
scanf("%d", &d );
if( d == 0 )
{
break;
}
//创建新数据节点 并初始化
Node * pnew = (Node*)malloc( sizeof(Node) );
pnew->data = d;
pnew->next = NULL;
//3.把新节点 加入到链表中
if( list->first == NULL ) //从无到有
{
list->first = pnew;
list->last = pnew;
}
else //从少到多
{
//尾插法
list->last->next = pnew;
list->last = pnew;
//头插法
//pnew->next = list->first;
//list->first = pnew;
}
list->num ++; //元素个数+1
}
//4.首尾相连 ☆☆☆
if( list->num != 0 ) //链表不为空
{
list->last->next = list->first;
}
//5.将新链表返回(返回头结点)
return list;
}
void print_list( List * list )
{
if( list == NULL )
{
printf("list is NULL \n");
return ;
}
Node * p = list->first; //遍历指针
int n = list->num ;
while( n-- )
{
printf("%d ", p->data );
p = p->next;
}
putchar('\n');
printf("num = %d\n\n", list->num );
}
main.c
#include "CircleLinkedListWithHead.h"
int main()
{}
2)根据用户输入的数据的顺序, 逆序创建一个带头结点的单链表
并将新链表返回
//头插法
3) 创建一个带头结点的升序链表
List *create_sort_Circle_list()
{
//1.创建一个头结点, 并初始化
List * list = (List*)malloc( sizeof(List) );
list->first = NULL;
list->last = NULL;
list->num = 0;
ElemType d;
while(1)
{
//2.从键盘上获取数据
scanf("%d", &d );
if( d == 0 )
{
break;
}
//创建新数据节点 并初始化
Node * pnew = (Node*)malloc( sizeof(Node) );
pnew->data = d;
pnew->next = NULL;
//3.把新节点 加入到链表中
if( list->first == NULL ) //从无到有
{
list->first = pnew;
list->last = pnew;
}
else //从少到多
{
//(找到第一个比它大的数的前面进行插入,没有找到就尾插)
Node * p = list->first; //遍历指针
Node * pre = NULL; //记录p的前一个
int n = list->num;
while( n-- )
{
if( p->data > pnew->data )
{
break; //找到了
}
pre = p;
p = p->next;
}
if( n == -1 ) //没有找到
{
//尾插
list->last->next = pnew;
list->last = pnew;
}
else //找到了
{
if( p == list->first ) //比第一个节点还要小,那么就头插
{
//头插
pnew->next = list->first;
list->first = pnew;
}
else
{
//中间插入
pre->next = pnew;
pnew->next = p;
}
}
}
list->num ++; //元素个数+1
}
//4.首尾相连 ☆☆☆
if( list->num != 0 ) //链表不为空
{
list->last->next = list->first;
}
//5.将新链表返回(返回头结点)
return list;
}
4)在链表中查找所有值为x的节点, 将其全部删除
若没有找到就不删除, 将新链表返回
List * delete_all_x_node( List * list , ElemType x )
{
if( list == NULL )
{
printf("list is NULL \n");
return NULL;
}
//1.查找值为x的节点
Node * p = list->first; //遍历指针
Node * pre = NULL; //记录p的前一个
int n = list->num;
while( n-- ) //遍历, 查找值为x的节点
{
if( p->data == x )
{
//找到了
list->num --; //元素个数 -1
//2.执行删除操作
if( p == list->first ) //删除第一个
{
list->first = list->first->next;
p->next = NULL;
free( p );
if( list->num == 0 ) // list->first == NULL
{
//把链表仅剩的一个节点都删除了
list->last = NULL;
}
p = list->first;
}
else if( p == list->last ) //删除最后一个
{
list->last = pre;
pre->next = NULL;
free( p );
p = NULL;
}
else //删除中间
{
pre->next = p->next;
p->next = NULL;
free( p );
p = pre->next;
}
}
else
{
pre = p;
p = p->next;
}
}
//3.首尾相连 ☆☆☆
if( list->num != 0 ) //链表不为空
{
list->last->next = list->first;
}
//4.将新链表返回(返回头结点)
return list;
}
5)在链表中查找值为x的节点, 将所有值为x的节点的值 修改成a
若没有找到就不修改, 将新链表返回
List * update_node( List * list , ElemType x , ElemType a )
{
if( list == NULL )
{
printf("list is NULL \n");
return NULL;
}
Node * p = list->first; //遍历指针
int n = list->num;
while( n-- )
{
if( p->data == x )
{
p->data = a;
}
p = p->next;
}
return list;
}
6)销毁一条链表
遍历 先释放每一个数据节点
再释放头结点
void destroy_list( List * list )
{
if( list == NULL )
{
printf("list is NULL \n");
return ;
}
Node * p = list->first; //遍历指针
int n = list->num;
while( n-- )
{
//遍历 先释放每一个数据节点
list->first = list->first->next;
p->next = NULL;
free( p );
p = list->first;
}
//再释放头结点
list->num = 0;
//list->first = NULL; //上面的循环 已经把first置NULL了
list->last = NULL;
free( list );
}
2)双向循环链表
构造:
//数据元素的类型
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)首尾相连
if( list->num != 0 ) //链表不为空
{
list->last->next = list->first;
list->first->prev = list->last;
}(2)遍历时,循环条件变为节点个数
int n = list->num;
while( n-- ) //遍历
{
}
练习:
1) 根据用户输入的数据的顺序, 创建一个带头结点的双向循环链表
将新链表返回, 然后再打印输出
CircleBothwayLinkedListWithHead.c / CircleBothwayLinkedListWithHead.h
DList * create_Circle_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.首尾相连
if( list->num != 0 ) //链表不为空
{
list->last->next = list->first;
list->first->prev = list->last;
}
//6.将新链表返回
return list;
}
void print_list( DList * list )
{
if( list == NULL ) //链表不存在
{
printf("list is NULL \n");
return ;
}
//正向
DNode * p = list->first; //遍历指针
int n = list->num;
while( n-- )
{
printf("%d ", p->data );
p = p->next;
}
putchar('\n');
//倒序
p = list->last;
n = list->num;
while( n-- )
{
printf("%d ", p->data );
p = p->prev;
}
putchar('\n');
printf("num = %d\n\n", list->num );
}
main.c
#include "CircleBothwayLinkedListWithHead.h"int main()
{
}
2)根据用户输入的数据的顺序, 逆序创建一个带头结点的双向链表
将新链表返回, 然后再打印输出
//头插法
3)根据用户输入的数据, 创建一个有序的带头结点的双向链表 (升序), 将新链表返回
/*
分情况讨论:
比第一个还要小, 头插法
比最后一个还要大, 尾插法
中间插入, 找到第一个比它大的数的前面进行插入
注意: 中间插入时, 先赋值pnew的成员, 再去修复前后的关系
☆☆☆
*/
DList * create_sort_Circle_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; //遍历指针
int n = list->num;
while( n-- )
{
if( p->data > pnew->data )
{
break; //找到了
}
p = p->next;
}
if( n == -1 ) //没找到
{
//尾插法
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.首尾相连
if( list->num != 0 ) //链表不为空
{
list->last->next = list->first;
list->first->prev = list->last;
}
//6.将新链表返回
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; //遍历指针
int n = list->num;
while( n-- )
{
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;
}
}
//5.首尾相连
if( list->num != 0 ) //链表不为空
{
list->last->next = list->first;
list->first->prev = list->last;
}
//将新链表返回
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; //遍历指针
int n = list->num;
while( n-- )
{
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; //遍历指针
int n = list->num;
//先释放每一个数据节点
while( n-- )
{
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 );
}