线性表的链式存储结构---用链式存储结构实现的线性表就叫(单向)链表
链表:结点和结点相连
结点:包含数据域和指针域
头指针:保存第一个结点地址的指针,头指针通过标记第一个结点,从而标记了整个链表,因此用头指针来命名整个链表。
链表分为带头结点的链表和不带头结点的链表
头结点:带头结点的链表的第一个结点(头结点数据域有数据但不是程序员放的,是随机的,只要分配了空间就是有数据的)
首元结点:保存线性表中第一个数据的结点
第一个节点:头指针指向的结点
链表包含:头指针和结点(或头结点)
链表操作:增删改查
单链表操作:
结构体作为结点
//定义一个结构体结点
typedef struct Node
{
int data;//存储数据
struct Node *next;//下一个结点地址
}Node,*LinkList;
(1)初始化
//初始化单链表,定义一个头结点
LinkList InitLinkList(){
Node *node = (Node*)malloc(sizeof(Node));
if(node!=NULL)
{
node->next = NULL;
return node;
}else{
printf("内存申请失败\n");
return node;
}
/*
这样行吗?
Node d;
d.next = NULL;
return &d;
答案是不行的,
因为d是一个变量,存放在栈区,当函数调用完毕之后该变量内存会被系统回收,此时&d = NULL;
*/
}
(2)添加数据
头插法:
//头插法(在头结点后一个位置添加数据k)
LinkList headInsert(LinkList l,int k){
//1.新结点s
Node *s = (Node*)malloc(sizeof(Node));
//2.加入数据
if(s!=NULL)
{
s->data = k;
//3.指针域指向首元结点,因为是在头结点和首元结点之间插入数据
s->next = l->next;//
//4.头结点指向s;
l->next = s;
return l;
}else{
printf("内存申请失败\n");
return s;
}
}
尾插法:
//尾插法
LinkList rearInsert(LinkList l,int k){
Node *s = (Node*)malloc(sizeof(Node));
if(s!=NULL)
{
s->data = k;
s->next = NULL;
Node *n = l;
while(n->next!=NULL)
{
n = n->next;
}
n->next = s;
return l;
}else{
printf("内存申请失败\n");
return s;
}
}
中间插入(在数据x后面插入数据k):
//指定位置插入在x后面插入k
LinkList middleInsert(LinkList l,int x,int k){
Node *s = (Node*)malloc(sizeof(Node));
if(s == NULL)
{
printf("内存申请失败\n");
return l;
}
s->data = k;
Node *n = Find(l,x);
if(n!=NULL)
{
s->next = n->next;
n->next = s;
return l;
}else{
printf("查无此数据\n");
return l;
}
}
(3)查找
//查找
Node *Find(LinkList l,int k){
Node *p = l->next;//从首元结点开始找
while(p!=NULL)
{
if(p->data == k)
{
return p;
}else{
p = p->next;
}
}
return p;
}
(4)删除
改正:第三行是从首元结点开始
//删除结点x
LinkList delete(LinkList l,int x){
Node *s = l->next;//从头结点开始
Node *preS = l;//指向前一个结点
while(s!=NULL&&s->data!=x)
{
preS = s;
s = s->next;
}
if(s == NULL)
{
printf("%d不存在\n",x);
return l;
}
preS->next = s->next;
free(s);//释放空间,但地址值依然保存
s = NULL;//防止野指针情况出现
return l;
}
(5)输出
//输出
void printff(LinkList l){
Node *s = l->next;
if(s == NULL)
{
printf("空指针\n");
}
while(s!=NULL)
{
printf("%d ",s->data);
}
}
(6)修改
//修改结点数据y为k
LinkList changeNode(LinkList l ,int y,int k){
Node *n = Find(l,y);
if(n == NULL)
{
printf("查无此数据\n");
return l;
}
n->data = k;
return l;
链表优缺点:
优点:灵活,便捷,因为要修改链表中的某一个数据时,不用移动其他的数据
缺点: 不支持随机存取,只能从头指针开始查找。