一。链表概念
1.链表定义
由于顺序存储的结构,如数组,插入和删除时要移动大量元素,人们发明了链表
链表由一个个结点组成,每个结点每个 结点 之间通过 链接关系 串联起来,每个 结点 都有一个 后继节点,最后一个 结点 的 后继结点 为 空结点。
- 链表 分为 单向链表、双向链表、循环链表 等等,本文要介绍的链表是 单向链表
2.结点结构体定义
typedef int datatype;
struct ListNode
{
datatype data;
ListNode *next;//next指针也指向listnode这种结构体
}
因此包括数据域和指针域
3.结点的创建
Listnode* create(datatype data)
{
ListNode * node=(ListNode*)malloc(sizeof(listnode));
//对比int*ret=(int*)malloc(sizeof(int));
即申请内存空间存放listnode
node->data=data;
node->next=NULL;
return node;//返回这个结点的指针
}

二。链表的创建-尾插法
即每插入一个结点,让其成为新的尾结点


头结点不变
ListNode *ListCreateListByTail(int n, int a[])//n为结点个数,a[]存放数据
{
ListNode*head,*vtx,*tail;//vtx为当前插入的结点
int idx=0;
vtx=listnode create(a[0]);
head=tail=vtx;
while(++index<n)
{
vtx=listnode create(a[index]);
tail->next=vtx;
tail=vtx;
}
return head;
}
注意while循环里tail指的是特定的某个结点,而不是会更新的一直指向尾部的神奇结点

三。链表的创建-头插法
头插法,顾名思义,就是每次从头结点前面进行插入,但是这样一来,就会导致插入的数据元素是 逆序 的,所以我们需要 逆序访问数组 执行插入,此所谓 负负得正 的思想。

上图所示的是 头插法 的整个插入过程,其中:
head 代表链表头结点,即动图中的 绿色结点,每新加一个结点,头结点就变成了新加入的结点;
tail 代表链表尾结点,创建完一个结点以后,它就保持不变了;
vtx 代表正在插入链表头部的结点,即动图中的 橙色结点,插入完毕以后,vtx 变成 head;
ListNode* create(int n inta[])
{
Listnode* head=NULL;
ListNode*vtx;
while(n--)
{
vtx=Listcreate[a[n]];//因为是逆序的所以从开始
vtx->next=head;
head=vtx;
}
return head;
}

无论是头插法还是尾插法,核心就三句
给vtx创建新结点
让原有头或尾成为vtx的next或pre
vtx成为新的头或尾
四。链表的打印
void Listprint(ListNode*head)//只需要头结点
{
ListNode*vtx=head;
while(vtx)
{
printf("%d->",vtx->data);
vtx=vtx->next;
}
printf("NULL\n");
}
从头结点开始,让vtx变成vtx->next,最后vtx变为null结束,输出一个null
五。链表元素的索引
给定一个链表头结点head,并且给定一个索引值 i (i \ge 0)i(i≥0),求这个链表的第 ii 个结点(为了和 C语言 的数组下标保持一致,我们假定链表头结点代表第 0 个结点)。
ListNode* Listget(int i,ListNode*head)//调用时listget(2,head)
{
ListNode*temp=head;
while(temp&&j<i)
{
temp=temp->next;
j++;
}
if(!temp||j>i)//说明i超过了链表长度
{
return NULL;
}
return temp;//返回所求结点
}
注意头结点为第0个结点

索引时间复杂度o(n)
六。链表元素的查找
*ListNode find(datatype v,ListNode*head)
{
ListNode*vtx=head;//都先初始化一个vtx=head,从head开始遍历
while(vtx)
{
if(vtx->data==v)
{
return vtx;
}
vtx=vtx->next;
}
return NULL;
}
查到就返回结点,没有就null

时间复杂度o(n)
七。链表结点的插入

ListNode* charu(datatype v,ListNode*head,int i)
{
ListNode* after;
ListNode*pre=head;
ListNode*pre=Listcreatenode(v);//创建要插入的结点
while(j<i&&pre)
{
pre=pre->next;
j++;
}//先使pre遍历到第i个即插入的位置
if(!pre)
{
return NULL;
}
after=pre->next;//重要的插入环节就这三句,第一句其实就是给after赋值,让after好看点
vtx->next=after;
pre->next=vtx;//实现插入
return vtx;
}
八。链表结点的删除

ListNode* dele(ListNode*head,int i)//删除i号结点
{
ListNode*pre,del,aft;
if(head==NULL)
{
return NULL;
}
if(i==0)
{
del=head;
head=head->next;
free(del);
return head;
}
pre=head;
while(pre&&n<i)
{
pre=pre->next;
n++;
}
if(!pre||!pre->next)//pre->next的原因是pre现在是删除结点的前驱结点,next才是删除结点
{
return NULL;
}
del=pre->next;//第一步都是给del赋值
aft=del->next;
pre->next=aft;
free(del);
return head;
}
九。链表的销毁

传参和初定义时要多加一个*,用二级指针,因为要改变实参使其成为null

本文详细介绍了单向链表的基础概念,包括结构体定义、节点创建、头尾插入方法,以及链表打印、元素索引、查找、插入和删除等操作。重点讲解了头插法和尾插法的区别,以及如何利用这些技术进行数据结构处理。
9245

被折叠的 条评论
为什么被折叠?



