链表
引出- 数组缺陷
1. 数组是一个静态空间,一旦分配内存,就不可以动态扩展。
空间可能分配多或者分配的少,操作不精准
2. 对于头部的插入删除效率低
链表
1. 由节点组成
而节点由 数据域 和 指针域组成
数据域是维护数据的
指针域 维护下一个节点的位置
2. 链表可以解决数组的缺陷
链表的分类
第一种分类方式:
1. 静态链表:在栈上分配内存
2. 动态链表:在堆区分配内存
第二种分类方式:
1. 单向链表:指针域只有后面的指针
2. 双向链表:指针域有后面和前一个节点的指针
3. 单向循环列表:最后一个节点的指针域记录第一个节点
4. 双向循环列表:第一个节点和最后一个节点可以互相链接

静态链表和动态链表
静态链表分配在栈上
示例
//节点结构体
struct LinkNode
{
int num
struct LinkNode* next
}
void test01()
{
//创建节点
struct LinkNode node1 = { 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
}
}
int main(void)
{
test01()
system("pause")
return EXIT_SUCCESS
}
动态链表分配到堆区
实现链表的初始化以及遍历功能
示例
//节点结构体
struct LinkNode
{
int num
struct LinkNode* next
}
void test01()
{
//创建节点 在堆上分配内存 是动态链表
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
//遍历列表
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
}
int main(void)
{
test01()
system("pause")
return EXIT_SUCCESS
}
链表基本使用
带头节点链表和不带头节点链表

带头好处:带着头节点的链表永远固定了头节点的位置
初始化链表 init_LinkList

遍历链表 foreach_LinkList
插入链表 insert_LinkList 利用两个辅助指针 实现插入


示例
//初始化链表
struct LinkNode* initLinkList()
{
//创建头节点
struct LinkNode* pHeader = malloc(sizeof(struct LinkNode))
if (pHeader == NULL)
{
return
}
//初始化头节点
//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 foreachLinkList(struct LinkNode* pHeader)
{
if (pHeader == NULL)
{
return
}
struct LinkNode* pCurrent = pHeader->next
while (pCurrent != NULL)
{
printf("%d\n", pCurrent->num)
pCurrent = pCurrent->next
}
}
//插入链表
struct LinkNode* insertLinkList(struct LinkNode* pHeader, int oldVal, int newVal)
{
if (pHeader == NULL)
{
return
}
//创建两个临时的节点
struct LinkNode* pPre = pHeader
struct LinkNode* pCurrent = pHeader->next
while(pCurrent != NULL)
{
if (pCurrent->num == oldVal)
{
break
}
//如果没找到对应的位置,辅助指针向后移动
pPre = pCurrent
pCurrent = pCurrent->next
}
//创建新节点
struct LinkNode* newNode = malloc(sizeof(struct LinkNode))
newNode->num = newVal
newNode->next = NULL
//建立关系
newNode->next = pCurrent
pPre->next = newNode
}
删除链表 delete_LinkList 利用两个辅助指针 实现删除
示例
//删除链表
void deleteLinkList(struct LinkNode* pHeader, int val)
{
if (pHeader == NULL)
{
return
}
struct LinkNode* pPre = pHeader
struct LinkNode* pCurrent = pHeader->next
while (pCurrent != NULL)
{
if (pCurrent->num == val)
{
break
}
//没哟找到数据。辅助指针向后移动
pPre = pCurrent
pCurrent = pCurrent->next
}
if (pCurrent == NULL) //没有找到用户要删除的数据
{
return
}
//更改指针指向进行删除
pPre->next = pCurrent->next
//删除掉待删除的节点
free(pCurrent)
pCurrent = NULL
}
清空链表
clear_LinkList 将所有有数据节点释放掉,可以再使用
//清空链表,清空后还是可以插入的
void clearLinkList(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
}
销毁链表
destroy_LinkList 将整个链表释放掉,不可以再使用
void destroyLinkList(struct LinkNode* pHeader)
{
if (pHeader == NULL)
{
return;
}
clearLinkList(pHeader);
free(pHeader);
pHeader = NULL;
}
反转链表

示例
//反转链表
reverseLinkList(struct LinkNode* pHeader)
{
if (pHeader == NULL)
{
return
}
struct LinkNode* pPre = NULL
struct LinkNode* pCurrent = pHeader->next
struct LinkNode* nextNode =NULL
while (pCurrent != NULL)
{
nextNode = pCurrent->next
pCurrent->next =pPre
pPre = pCurrent
pCurrent =nextNode
}
pHeader->next = pPre
}
//返回链表节点个数
int sizeLinkList(struct LinkNode* pHeader)
{
if (pHeader == NULL)
{
return -1
}
struct LinkNode* pCurrent = pHeader->next
int num = 0
while (pCurrent != NULL)
{
num++
pCurrent = pCurrent->next
}
return num
}
函数指针
函数名本质就是一个函数指针
可以利用函数指针 调用函数
函数指针定义方式
1、先定义出函数类型,再通过类型定义函数指针
typedef void(FUNC_TYPE)(int, char);
2、定义出函数指针类型,通过类型定义函数指针变量
typedef void( * FUNC_TYPE2)(int, char);
3、直接定义函数指针变量
void(*pFunc3)(int, char) = func;
函数指针和指针函数
函数指针 指向了函数的指针
指针函数 函数返回值是指针的函数
函数指针数组
void(*pArray[3])();
示例
void func(int a, char c)
{
printf("hello world\n");
}
void test01()
{
typedef void (FUNC_TYPE)(int, char);
FUNC_TYPE* pFunc = func;
typedef void (* FUNC_TYPE2)(int, char);
FUNC_TYPE2 pFunc2 = func;
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 (size_t i = 0; i < 3; i++)
{
pArray[i]();
}
}
int main(void)
{
test02();
system("pause");
return EXIT_SUCCESS;
}
函数指针做函数参数(回调函数)
利用回调函数实现打印任意类型数据
示例
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("name =%s, age=%d\n", p->name, p->age);
}
void test02()
{
struct Person p = { "Tom",18 };
printText(&p, myPrintPerson);
}
int main(void)
{
test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
提供能够打印任意类型数组函数
示例
void printAllArray(void * pArray, int eleSize, int len,void(*myPrint) (void*))
{
char* p = pArray;
for (size_t i = 0; i < len; i++)
{
char* eleAddr = p + eleSize * i;
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 myPrintStruct(void *data)
{
struct Person* p = data;
printf("name = %s age =%d\n", p->name, p->age);
}
void test02()
{
struct Person personArray[] =
{
{"aaa",10},
{"bbb",12},
{"ccc",30},
{"ddd",40}
};
int len = sizeof(personArray) / sizeof(struct Person);
printAllArray(personArray, sizeof(struct Person), len, myPrintStruct);
}
int main(void)
{
test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
利用回调函数 提供查找功能
示例
int findArrayEle(void * pArray,int eleSize,int len,void *data,int(*myCompare)(void *,void *))
{
char* p = pArray;
for (size_t i = 0; i < len; i++)
{
char* eleAddr = p + eleSize * i;
if (myCompare(eleAddr, data))
{
return 1;
}
}
return 0;
}
int myComparePerson(void* data1, void* data2)
{
struct Person* p1 = data1;
struct Person* p2 = data2;
return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age;
}
void test02()
{
struct Person personArray[] =
{
{"aaa",10},
{"bbb",12},
{"ccc",30},
{"ddd",40}
};
int len = sizeof(personArray) / sizeof(struct Person);
printAllArray(personArray, sizeof(struct Person), len, myPrintStruct);
struct Person p = { "bbb",12 };
int ret = findArrayEle(personArray, sizeof(struct Person), len, &p,myComparePerson);
if (ret)
{
printf("found the element\n");
}
else
{
printf("unfound the element\n");
}
}
作业:超难
提供一个函数,实现对任意类型的数组进行排序,排序规则利用选择排序,排序的顺序 用户可以自己指定。
示例
void selectSort(void * pAddr,int eleSize,int len,int (*myCompare)(void *,void *))
{
char* temp = malloc(eleSize)
for (size_t i = 0
{
int minOrMax = i
for (size_t j = i+1
{
//定义出j元素的地址
char* pJ =(char*)pAddr + eleSize * j
char* pMinOrMax = (char*)pAddr + eleSize * minOrMax
//if(pAddr[j]<pAddr[minOrMax])
if (myCompare(pJ,pMinOrMax))
{
minOrMax = j
}
}
if (i != minOrMax)
{
//交换i和minOrMin 下标元素
char* pI = (char*)pAddr + i * eleSize
char* pMinOrMax = (char*)pAddr + eleSize * minOrMax
memcpy(temp, pI, eleSize)
memcpy(pI, pMinOrMax, eleSize)
memcpy(pMinOrMax, temp, eleSize)
}
}
if (temp != NULL)
{
free(temp)
temp = NULL
}
}
int myCompareInt(void * data1,void *data2)
{
int* num1 = data1
int* num2 = data2
if (*num1 > *num2)//从大到小排序
{
return 1
}
return 0
}
void test01()
{
int arr[] = { 10,30,20,40,50,60 }
int len = sizeof(arr) / sizeof(int)
selectSort(arr, sizeof(int), len, myCompareInt)
for (size_t i = 0
{
printf("%d\n", arr[i])
}
}
struct Person
{
char name[64]
int age
}
int myCompareStruct(void* data1, void* data2)
{
struct Person* p1 = data1
struct Person* p2 = data2
if (p1->age < p2->age)
{
return 1
}
return 0
}
void test02()
{
struct Person pArray[] =
{
{"aaa",10},{"bbb",30},{"ccc",20},{"ddd",40}
}
int len = sizeof(pArray) / sizeof(struct Person)
selectSort(pArray, sizeof (struct Person), len, myCompareStruct)
for (size_t i = 0
{
printf(" name = %s age = %d\n", pArray[i].name, pArray[i].age);
}
}
int main(void)
{
test01();
test02();
system("pause");
return EXIT_SUCCESS;
}