链表
特点:内存不连续,通过指针进行连接
解决: 长度固定的问题,插入删除麻烦的问题
1)逻辑结构:线性结构
2)存储结构:链式存储
3)操作:增删改查
2.1单向链表
链表结构体:
struct node_t
{
int data;
struct node_t *next;
};
2.1.1分类
1)有头单向链表
存在一个头节点,数据域无效,指针域有效
遍历有头单向链表:
#include <stdio.h>
struct node_t
{
int data;
struct node_t *next;
};
int main(int argc, char const *argv[])
{
// 1.定义节点并初始化
struct node_t a = {1, NULL};
struct node_t b = {2, NULL};
struct node_t c = {3, NULL};
// 2.将节点进行连接
a.next = &b;
b.next = &c;
// 3.定义一个头节点,指向第一个节点
struct node_t s;
s.next = &a;
// 4.定义头指针指向头节点
struct node_t *h = &s;
// 5.遍历有头单向链表,方法1:
// while (h->next != NULL)
// {
// h = h->next;
// printf("%d ", h->data);
// }
//方法2:
h=h->next;
while (h != NULL)
{
printf("%d ",h->data);
h=h->next;
}
printf("\n");
return 0;
}
2)无头单向链表
所有节点的指针域和数据域都有效
遍历无头单向链表:
#include <stdio.h>
struct node_t
{
int data;
struct node_t * next;
};
int main(int argc, char const *argv[])
{
//1.定义节点并初始化
struct node_t a = {1,NULL};
struct node_t b = {2,NULL};
struct node_t c = {3,NULL};
//2.将节点进行连接
a.next = &b;
b.next = &c;
//3.定义头指针指向第一个节点
struct node_t *h = &a;
//4.遍历无头单向链表
while(h!=NULL)
{
printf("%d ",h->data);
h = h->next;
}
printf("\n");
return 0;
}
2.1.2操作函数
linklist.h
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#include<stdio.h>
#include<stdlib.h>
typedef int datatype;
typedef struct node_t
{
datatype data;//数据域
struct node_t *next;//指针域,指向自身结构体的指针
}link_node_t,*link_list_t;
//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList();
//2.向单向链表的指定位置插入数据
//p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p,int post, datatype data);
//3.遍历单向链表
void ShowLinkList(link_node_t *p);
//4.求单向链表长度的函数
int LengthLinkList(link_node_t *p);
//5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post);
//6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p);
//7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data);
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data);
//9.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data);
//10.转置链表
void ReverseLinkList(link_node_t *p);
//11.清空单向链表
void ClearLinkList(link_node_t *p);
#endif
1. 创建一个空的(有头)单向链表
2.向post位置插入一个数据
1、 先遍历找到要插入节点的前一个节点,假设这个节点为A;A的下一个节点为B;将C插入A与B之间;
2、 先让C的指针域指向B;
3、 再让A的指针域指向C;
(注意:顺序不可以调换)
3)删除指定位置的数据
1、先遍历找到要删除节点的前一个节点,假设为A;
2、找一个临时指针指向要删除的节点;
3、 将A的指针域指向删除节点的下一个节点;
4、 释放被删除节点
4)单向链表的转置
转置的思想:
(1) 将头节点与当前链表断开,断开前保存下头节点的下一个节点,保证后面链表能找得到,定义一个q保存头节点的下一个节点,断开后前面相当于一个空的链表,后面是一个无头的单向链表
(2) 遍历无头链表的所有节点,将每一个节点当做新节点插入空链表头节点的下一个节点(每次插入的头节点的下一个节点位置)
linklist.c
#include "linklist.h"
// 1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList()
{
// 1.开辟一个结构体大小的空间存放头节点
link_list_t p = (link_list_t)malloc(sizeof(link_node_t));
if (p == NULL)
{
perror("CreateEpLinkList err");
return NULL;
}
// 2.初始化头节点
p->next = NULL;
return p;
}
// 4.求单向链表长度的函数
int LengthLinkList(link_node_t *p)
{
int len = 0;
while (p->next != NULL)
{
len++;
p = p->next;
}
return len;
}
// 2.向单向链表的指定位置插入数据
// p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p, int post, datatype data)
{
// 1.容错处理
if (post < 0 || post > LengthLinkList(p))
{
perror("InsertIntoPostLinkList err");
return -1;
}
// 2.移动头指针,指向插入位置前一个节点
for (int i = 0; i < post; i++)
p = p->next;
// 3.创建一个新节点,保存即将插入的数据
link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
if (pnew == NULL)
{
perror("pnew malloc err");
return -1;
}
pnew->data = data;
pnew->next = NULL;
// 4.将新节点插入链表中(先连后面,再连前面)
pnew->next = p->next;
p->next = pnew;
return 0;
}
// 6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p)
{
return p->next == NULL;
}
// 5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post)
{
// 1.容错判断
if (post < 0 || post >= LengthLinkList(p) || IsEpLinkList(p))
{
perror("DeletePostLinkList err");
return -1;
}
// 2.移动头指针,让它指向被删除位置的前一个节点
for (int i = 0; i < post; i++)
p = p->next;
// 3.进行删除操作
// 1)定义一个指针pdel指向被删除节点
link_list_t pdel = p->next;
// 2)跨过被删除节点
p->next = pdel->next;
// 3)释放被删除节点
free(pdel);
pdel = NULL;
return 0;
}
// 3.遍历单向链表
void ShowLinkList(link_node_t *p)
{
while (p->next != NULL)
{
p = p->next;
printf("%d ", p->data);
}
printf("\n----------------------\n");
}
// 7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data)
{
// 1.容错判断
if (post < 0 || post >= LengthLinkList(p) || IsEpLinkList(p))
{
perror("ChangePostLinkList err");
return -1;
}
// 2.移动头指针,让它指向被修改的节点
for(int i = 0;i<=post;i++)
p=p->next;
// 3.修改数据
p->data=data;
return 0;
}
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data)
{
//1.定义一个变量记录查找的位置
int post = 0;
//2.遍历有头单向链表
while(p->next != NULL )
{
p=p->next;
if(p->data == data)
return post;
post++;
}
return -1;//不存在
}
//9.删除单向链表中出现的指定数据data,代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data)
{
//1.定义一个指针q,始终指向p的下一个节点
link_list_t q = p->next;
//2.用q遍历无头单向链表
while(q != NULL)
{
if(q->data == data)//找到了要删除的节点
{
p->next = q->next;//跨过被删除节点
free(q);//释放被删除节点
q=p->next;//q重新指向p的下一个节点(新)
}
else
{
p=p->next;//将头指针指向下一个节点
q=p->next;//始终保证q指向p的下一个节点
}
}
return 0;
}
//11.清空单向链表
void ClearLinkList(link_node_t *p)
{
link_list_t pdel = NULL;
while (p->next != NULL)
{
//(1)将pdel指向被删除节点即头节点下一个节点
pdel = p->next;
//(2)跨过被删除节点
p->next = pdel->next;
//(3)释放被删除节点
free(pdel);
pdel = NULL;
}
}
//10.转置链表
void ReverseLinkList(link_node_t *p)
{
//临时保存q节点的下一个节点
link_list_t temp = NULL;
//1.断开前,保存头节点的下一个节点的地址(此时q相当于指向无头单向链表的头指针)
link_list_t q = p->next;
//2.断开链表
p->next = NULL;
//3.遍历无头链表
while (q != NULL)
{
//提前保存q的下一个节点
temp=q->next;
//先连后面再连前面(头插)
q->next = p->next;
p->next = q;
//让q重新指向当前无头单向链表的第一个节点
q = temp;
}
}
main.c
#include"linklist.h"
int main(int argc, char const *argv[])
{
link_list_t p =CreateEpLinkList();
InsertIntoPostLinkList(p,0,1);
InsertIntoPostLinkList(p,1,2);
InsertIntoPostLinkList(p,2,3);
ShowLinkList(p);//1 2 3
ReverseLinkList(p);
ShowLinkList(p);//3 2 1
printf("len is %d\n",LengthLinkList(p));//3
DeletePostLinkList(p,0);
ShowLinkList(p);// 2 1
printf("%d post is %d\n",2,SearchDataLinkList(p,2));//2 0
DeleteDataLinkList(p,2);
ShowLinkList(p);//1
DeletePostLinkList(p,0);
if(IsEpLinkList(p))
printf("IsEpLinkList\n");
free(p);
p = NULL;
return 0;
}
Makefile
CC=gcc
CFLAGS=-c -g -o
OBJS=main.o linklist.o
linklist:$(OBJS)
$(CC) $^ -o $@
%.o:%.c
$(CC) $(CFLAGS) $@ $<
.PHONY:clean
clean:
rm *.o linklist -f