C语言 16.链表

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提供一个函数,实现对任意类型的数组进行排序,排序规则利用选择排序,排序的顺序用户可以自己指定

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值