基本概念
顺序表:顺序存储的线性表。
链式表:链式存储的线性表,简称链表。
既然顺序存储中的数据因为挤在一起而导致成片移动,那很容易像到的解决方案是将数据离散地存储在不同的内存块中,然后在用来指针将它们串起来。这种朴素地思路所形成地链式线性表,就是所谓的链表。
使用线性逻辑(数据与数据之间的关系)+链式存储
链表的分类:
根据链表中各个节点之间使用指针的个数,以及首尾节点是否相连,可以将链表细分为如下种类:
1.单向链表
2.单向循环链表
3.双向循环链表
4.内核链表(由系统内核提供的链表操作数)
这些不同的链表的操作都是差不多的,只是指针数数目的异同。
在链表中,指针所处的代码块一般称之为节点,而节点在c语言中是用结构体来实现的,代码如下:
typedef int TYPEDATA; //取别名,为以后更换数据类型方便
typedef struct LinkListNode
{
//数据域
TYPEDATA data; //节点所带的数据
//指针域
strct LinkListNode *next; //后继指针
}Node, *P_Node;//为结构体定义别名,方便定义参数
链表头设计:
链表头分为带头节点和不带头节点,其中不带头节点的直接令头指针指向空就可以初始化了;
带头节点的链表需要定义一个新的节点,里面的数据域为空,指针域指向下一的节点;
下面为定义新节点的代码:
/****************
功能:创建一个新的节点并初始化
函数名:NewNode(TYPEDATA data)
参数:TYPEDATA data 传递一个TYPEDATA类型的参数,用于给节点赋值
返回值:返回一个P_Node类型的结构体指针,也就是返回一个新节点
****************/
P_Node NewNode(TYPEDATA data)
{
//申请一块Node大小的堆空间并让newnode指向该空间
P_Node newnode = calloc(1, sizeof(Node));
//初始化数据域
newnode->data = data;
//初始化指针
new->next = NULL;
//返回堆空间的地址(新节点的入口地址)
return newnode;
}
创建链表头之后需要增加节点,删除节点,移动节点等操作,也就是所谓的增删改查;
而增加节点在只有头节点位置的情况下,有头部插入节点和尾部插入节点两种基本操作
代码如下:
头插法
/****************
功能:从已知节点使用头插法插入到链表中
函数名:AddToList(const P_Node head, TYPEDATA data)
参数:const P_Node head为只读类型的节点,TYPEDATA data为TYPEDATA类型的数据
返回值: 成功返回真true,失败返回假false
****************/
bool AddToList(const P_Node head, TYPEDATA data)
{
//判断头节点是否为空
if(hrad == NULL)
{
printf("list hrad error\n");
return false;
}
//使用当前data创建一个节点
P_Node newdata = NewNode(data);
//将新节点插入到链表中去
//1.新节点指向头节点的后继节点
newdata->next = head->next;
//2.头节点的后继节点指向新节点
head->next = newdata;
return true;
}
尾插法:
/****************
功能:从已知节点使用尾插法插入到链表中
函数名:AddForList(const P_Node head, TYPEDATA data)
参数:const P_Node head为只读类型的节点,TYPEDATA data为TYPEDATA类型的数据
返回值: 成功返回真true,失败返回假false
****************/
bool AddForList(const P_Node head, TYPEDATA data)
{
//判断头节点是否为空
if(hrad == NULL)
{
printf("list hrad error\n");
return false;
}
//使用当前data创建一个节点
P_Node newdata = NewNode(data);
//尾插法需要先到链表的末尾
P_Node tmp = head;
for( ; tmp->next != NULL; tmp = tmp->next);
//让后继节点指向新节点
tmp->next = newdata;
return true;
}
当插入链表数据后,想要查看链表数据时,可以重头到尾遍历链表,将链表中的数据打印出来
代码如下:
遍历显示链表
/****************
功能:从已知节点遍历后继节点的数据并且打印出来
函数名:ShowList(const P_Node head)
参数:const P_Node head为只读类型的节点
返回值: 成功返回真true,失败返回假false
****************/
bool ShowList(const P_Node head)
{
if(head == NULL)
{
printf("list head error...\n");
return false;
}
//通过头节点开始遍历链表
for(P_Node tmp = head->next; tmp != NULL; tmp = tmp->next)
{
printf("data:%d\n", tmp->data);
}
return true;
}
当链表数据太多时,想要寻找想要的那个节点,就需要查找节点了;
代码如下:
/****************
功能:从已知节点遍历查找需要的节点数据并且打印出来
函数名:FindForList(const P_Node head, TYPEFATA data)
参数:const P_Node head为只读类型的节点,TYPEFATA data为要查找的数据
返回值: 成功返回要查找的节点,失败返回NULL;
****************/
P_Node FindForList(const P_Node head, TYPEFATA data)
{
if(head == NULL)
{
printf("list head error...\n");
return NULL;
}
//通过头节点开始遍历链表
for(P_Node tmp = head->next; tmp != NULL; tmp = tmp->next)
{
if(tmp->data == data)
{
printf("find data:%d\n", tmp->data);
return tmp;//返回查找到的节点
}
}
printf("no data\n");
return NULL;//没有找到节点返回NULL;
}
当碰到链表中有不需要的数据要删除时,可以将节点删除,首先找到节点,再将节点删除,方法有很多,以下为最笨方法:
代码如下:
/****************
功能:从已知节点遍历查找需要删除的节点数据并且打印出来,将节点删除
函数名:RemoveNode(const P_Node head, P_Node del)
参数:const P_Node head为只读类型的节点,P_Node del为要删除的节点数据
返回值: 成功返回要删除的节点,失败返回NULL;
****************/
P_Node RemoveNode(const P_Node head, P_Node del)
{
if(head == NULL)
{
printf("list head error...\n");
return NULL;
}
//通过头节点开始遍历链表
for(P_Node tmp = head->next; tmp != del; tmp = tmp->next)
{
if(tmp == del)
{
printf("find data:%d\n", tmp->data);
tmp-next = del->next;
del->next = NULL;
return del;//返回查找到的节点
}
}
printf("no data\n");
return NULL;//没有找到节点返回NULL;
}
当想要销毁链表时,可以销毁链表,代码如下:
递归销毁
P_Node RecursionDestructionList( P_Node tmp )
{
if ( tmp == NULL )
{
return NULL ;
}
tmp->next = RecursionDestructionList( tmp->next );
printf("当前即将删除:%d\n" , tmp->data );
free(tmp);
return NULL ;
}
双指针销毁
P_Node DestructionList( P_Node head )
{
if ( head->next == NULL && head != NULL )
{
free(head);
return NULL ;
}
// 搞两个指针先指向第一个和第二个有效数据
P_Node tmp = head->next ;
P_Node pos = tmp->next ;
// 删除结点
for ( ; tmp != NULL ; )
{
printf("tmp:%d\n" , tmp->data );
free(tmp);
tmp=pos;
if (pos != NULL )
{
pos = pos->next;
}
}
return NULL ;
}