1链表
1.1引出- 数组缺陷
1.1.1数组是一个静态空间,一旦分配内存,就不可以动态扩展,空间可能分配多或者分配的少,操作不精准
1.1.2对于头部的插入删除效率低
1.2链表
1.2.1由节点组成
1.2.2而节点由 数据域和 指针域组成
1.2.2.1数据域是维护数据的
1.2.2.2指针域 维护下一个节点的位置
1.2.3链表可以解决数组的缺陷
1.2.4链表的分类
1.2.4.1静态链表、动态链表
1.2.4.2单向链表、双向链表、单向循环链表、双向循环链表
2静态链表和动态链表
2.1静态链表分配在栈上
2.2动态链表分配到堆区
2.3实现链表的初始化以及遍历功能
/**************************************静态链表**************************/
// 节点的结构体
struct LinkNode
{
int num; // 数据域
struct LinkNode* next;// 指针域
};
void test01()
{
// 创建节点
struct LinkNode node1 = { 10, NULL };
struct LinkNode nodel = {10,NULL};
struct LinkNode node2 = { 20,NULL };
struct LinkNode node3 = { 30,NULL };
struct LinkNode node4 = { 40,NULL };
struct LinkNode node5 = { 50,NULL };
// 建立关系
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
node4.next = &node5;
// 遍历链表
struct LinkNode* pCurrent = &node1;
while (pCurrent != NULL)
{
printf("%d\n", pCurrent->num);
pCurrent = pCurrent->next;
}
}
/*********************************动态链表***************************/
void test02()
{
// 创建节点
struct LinkNode* node1 = malloc(sizeof(struct LinkNode));
struct LinkNode* node2 = malloc(sizeof(struct LinkNode));
struct LinkNode* node3 = malloc(sizeof(struct LinkNode));
struct LinkNode* node4 = malloc(sizeof(struct LinkNode));
struct LinkNode* node5 = malloc(sizeof(struct LinkNode));
// 给数据赋值
node1->num = 100;
node2->num = 200;
node3->num = 300;
node4->num = 400;
node5->num = 500;
// 建立关系
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = node5;
node5->next = NULL;
// 遍历链表
struct LinkNode* pCurrent = node1;
while (pCurrent != NULL)
{
printf("%d\n", pCurrent->num);
pCurrent = pCurrent->next;
}
free(node1);
free(node2);
free(node3);
free(node4);
free(node5);
node1 = NULL;
node2 = NULL;
node3 = NULL;
node4 = NULL;
node5 = NULL;
}
3链表基本使用
3.1带头节点链表和不带头节点链表
3.1.1带头好处:带着头节点的链表永远固定了头节点的位置
3.2初始化链表 init_LinkList
3.3遍历链表 foreach_LinkList
3.4插入链表 insert_LinkList 利用两个辅助指针实现插入
3.5删除链表 delete_LinkList 利用两个辅助指针实现删除
3.6清空链表 clear_LinkList 将所有有数据节点释放掉,可以在使用
3.7销毁链表 destroy_LinkList 将整个链表释放掉,不可以再使用
main.c
void test01()
{
//初始化链表 10 20 30
struct LinkNode* pHeader = initLinkList();
// 遍历列表
printf("遍历链表的结构为:\n");
foreach_LinkList(pHeader);
// 插入链表
// 10 1000 2000 20 3000 30 500
insert_LinkList(pHeader, 20, 1000);
insert_LinkList(pHeader, 20, 2000);
insert_LinkList(pHeader, -1, 500);
insert_LinkList(pHeader, 30, 3000);
printf("插入链表后,遍历链表结果为:\n");
foreach_LinkList(pHeader);
// 删除列表 10 20 500
dele_LinkList(pHeader, 2000);
dele_LinkList(pHeader, 3000);
dele_LinkList(pHeader, 1000);
dele_LinkList(pHeader, -1);
printf("删除链表后,遍历链表结果为:\n");
foreach_LinkList(pHeader);
// 清空链表
clear_Linklist(pHeader);
printf("清空链表后,遍历链表结果为:\n");
insert_LinkList(pHeader,111,111);
insert_LinkList(pHeader, 222, 222);
insert_LinkList(pHeader, 333, 333);
foreach_LinkList(pHeader);
// 销毁列表
destory_LinkList(pHeader);
pHeader = NULL;
}
linklist.h
struct LinkNode
{
int num;
struct LinkNode* next;
};
// 初始化列表
struct LinkNode* initLinkList();
// 遍历列表
void foreach_LinkList(struct LinkNode* pHeader);
// 插入链表
void insert_LinkList(struct LinkNode * pHeader,int oldVal,int new);
// 删除列表
void dele_LinkList(struct LinkNode* pHeader, int val);
// 清空列表
void clear_Linklist(struct LinkNode* pHeader);
// 销毁列表
void destory_LinkList(struct LinkNode* pHeader);
linklist.c
#include "linklist.h"
//初始化链表
struct LinkNode* initLinkList()
{
//创建头节点
struct LinkNode* pHeader = malloc(sizeof(struct LinkNode));
if (pHeader == NULL)
{
return NULL;
}
//初始化头节点
//pHeader->num = -1; //头节点 不维护数据域
pHeader->next = NULL;
//记录尾节点位置,方便插入新的数据
struct LinkNode* pTail = pHeader;
int val = -1;
while (1)
{
//让用户初始化几个节点,如果用户输入的是-1,代表插入结束
printf("请初始化链表,如果输入-1代表结束\n");
scanf("%d", &val);
if (val == -1)
{
break;
}
//如果输入不是-1 插入节点到链表中
struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
newNode->num = val;
newNode->next = NULL;
//更改指针的指向
pTail->next = newNode;
//更新新的尾节点的指向
pTail = newNode;
}
return pHeader;
}
// 遍历列表
void foreach_LinkList(struct LinkNode * pHeader)
{
if (pHeader == NULL)
{
return;
}
struct LinkNode* pCurrent = pHeader->next; // 指向第一个有真实数据的节点
while (pCurrent != NULL)
{
printf("%d\n", pCurrent->num);
pCurrent = pCurrent->next;
}
}
// 插入链表
void insert_LinkList(struct LinkNode* pHeader, int oldVal, int newVal)
{
if (pHeader == NULL)
{
return;
}
// 创建两个临时的节点
struct LinkNode* pProve = pHeader;
struct LinkNode* pCurrent = pHeader->next;
while (pCurrent != NULL)
{
if (pCurrent->num == oldVal)
{
break;
}
// 如果没找到对应的位置,辅助指针向后移动
pProve = pCurrent;
pCurrent = pCurrent->next;
}
// 创建新节点
struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
newNode->num = newVal;
newNode->next = NULL;
// 建立关系
newNode->next = pCurrent;
pProve->next = newNode;
}
// 删除列表
void dele_LinkList(struct LinkNode* pHeader, int val)
{
if (pHeader == NULL)
{
return;
}
// 创建两个辅助指针变量
struct LinkNode* pPrev = pHeader;
struct LinkNode* pCurrent = pHeader->next;
while (pCurrent != NULL)
{
if (pCurrent->num == val)
{
break;
}
// 如果没有找到数据,赋值指针向后移动
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
if (pCurrent == NULL) // 没有找到用户要删除的数据
{
return;
}
// 更改指针的指向进行删除
pPrev->next = pCurrent->next;
// 删除掉待删除的节点
free(pCurrent);
pCurrent = NULL;
}
// 清空列表
void clear_Linklist(struct LinkNode* pHeader)
{
if (pHeader == NULL)
{
return;
}
struct LinkNode* pCurrent = pHeader->next;
while (pCurrent != NULL)
{
// 先保存住下一个节点的位置
struct LinkNode* nextNode = pCurrent->next;
free(pCurrent);
pCurrent = nextNode;
}
pHeader->next = NULL;
}
// 销毁列表
void destory_LinkList(struct LinkNode* pHeader)
{
if (pHeader == NULL)
{
return;
}
// 先清空列表
clear_Linklist(pHeader);
// 再释放头节点
free(pHeader);
pHeader=NULL;
}
作业布置
4.函数指针
4.1函数名本质就是一个函数指针
4.2可以利用函数指针调用函数
5.函数指针定义方式
5.1 //1、先定义出函数类型,再通过类型定义函数指针
5.1.1typedef void(FUNC_TYPE)(int, char);
5.2 //2、定义出函数指针类型,通过类型定义函数指针变量
5.2.1typedef void( * FUNC_TYPE2)(int, char);
5.3 //3、直接定义函数指针变量
5.3.1void(*pFunc3)(int, char) = func;
5.4函数指针和指针函数
5.4.1 //函数指针 指向了函数的指针
5.4.2 //指针函数 函数返回值是指针的函数
5.5函数指针数组
5.5.1 void(*pArray[3])();
void func(int a ,char c)
{
printf("hello world\n");
}
void test01()
{
//1、先定义出函数类型,再通过类型定义函数指针
typedef void(FUNC_TYPE)(int, char);
FUNC_TYPE * pFunc = func;
//pFunc(10, 'a');
//2、定义出函数指针类型,通过类型定义函数指针变量
typedef void( * FUNC_TYPE2)(int, char);
FUNC_TYPE2 pFunc2 = func;
//pFunc2(20, 'b');
//3、直接定义函数指针变量
void(*pFunc3)(int, char) = func;
pFunc3(30, 'c');
//函数指针 和 指针函数 区别?
//函数指针 指向了函数的指针
//指针函数 函数返回值是指针的函数
}
//函数指针的数组
void func1()
{
printf("func1 调用了\n");
}
void func2()
{
printf("func2 调用了\n");
}
void func3()
{
printf("func3 调用了\n");
}
void test02()
{
void(*pArray[3])();
pArray[0] = func1;
pArray[1] = func2;
pArray[2] = func3;
for (int i = 0; i < 3;i++)
{
pArray[i]();
}
}
6.函数指针做函数参数(回调函数)
通过提供回调函数来保证打印函数的通用性。对于不同的数据类型,对应不同的回调函数,就可以通过同一个打印函数来进行打印
6.1利用回调函数实现打印任意类型数据
//提供一个打印函数,可以打印任意类型的数据
void printText( void * data , void(*myPrint)(void *) )
{
myPrint(data);
}
void myPrintInt(void * data)
{
int * num = data;
printf("%d\n", *num);
}
void test01()
{
int a = 10;
printText(&a, myPrintInt);
}
struct Person
{
char name[64];
int age;
};
void myPrintPerson(void * data)
{
struct Person * p = data;
printf("姓名: %s 年龄: %d\n", p->name, p->age);
}
void test02()
{
struct Person p = { "Tom", 18 };
printText(&p, myPrintPerson);
}
6.2提供能够打印任意类型数组函数
6.3利用回调函数 提供查找功能
//提供一个函数,实现可以打印任意类型的数组
void printAllArray(void * pArray , int eleSize, int len , void(*myPrint)(void*) )
{
char * p = pArray;
for (int i = 0; i < len;i++)
{
//获取数组中每个元素的首地址
char * eleAddr = p + eleSize * i;
//printf("%d\n", *(int *)eleAddr);
//交还给用户做打印操作
myPrint(eleAddr);
}
}
void myPrintInt(void * data)
{
int * num = data;
printf("%d\n", *num);
}
void test01()
{
int arr[5] = { 1, 2, 3, 4, 5 };
int len = sizeof(arr) / sizeof(int);
printAllArray(arr, sizeof(int), len, myPrintInt);
}
struct Person
{
char name[64];
int age;
};
void myPrintperson(void * data)
{
struct Person * p = data;
printf("姓名:%s 年龄:%d \n", p->name, p->age);
}
//查找数组中的元素是否存在
//参数1 数组首地址 参数2 每个元素的大小 参数3 数组元素个数 参数4 查找数据
int findArrayEle(void * pArray, int eleSize, int len, void * data , int(*myCompare)(void* ,void* ) )
{
char * p = pArray;
for (int i = 0; i < len;i++)
{
//每个元素的首地址
char * eleAddr = p + eleSize * i;
//if ( 数组中的变量的元素 == 用户传入的元素)
if ( myCompare(eleAddr,data) )
{
return 1;
}
}
return 0;
}
int myComparePerson(void * data1,void * data2)
{
struct Person * p1 = data1;
struct Person * p2 = data2;
//if ( strcmp( p1->name , p2->name) == 0 && p1->age == p2->age)
//{
// return 1;
//}
//return 0;
return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age;
}
void test02()
{
struct Person personArray[] =
{
{ "aaa", 10 },
{ "bbb", 20 },
{ "ccc", 30 },
{ "ddd", 40 },
};
int len = sizeof(personArray) / sizeof(struct Person);
printAllArray(personArray, sizeof(struct Person), len, myPrintperson);
//查找数组中指定的元素是否存在
struct Person p = { "ccc", 30 };
int ret = findArrayEle(personArray, sizeof(struct Person), len, &p, myComparePerson);
if (ret)
{
printf("找到了元素\n");
}
else
{
printf("未找到\n");
}
}
7.作业:超难
7.1提供一个函数,实现对任意类型的数组进行排序,排序规则利用选择排序,排序的顺序用户可以自己指定