数据结构——链表自实现


❀保持低旋律节奏->个人主页

专栏链接:《C++学习》《Linux学习》


文章目录

头文件实现

c 复制代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
// 补充bool类型头文件(C语言需手动定义或包含stdbool.h)
#include<stdbool.h>

// 修正:C语言中结构体指针必须加struct(原代码漏写导致编译报错)
struct ListNode
{
    struct ListNode* next;
    int val;
};

//1.Print函数
void Print(struct ListNode* phead)
{
    struct ListNode* cur = phead;
    if (cur == NULL)
    {
        printf("NULL\n");
    }
    else
    {
        while (cur)
        {
            printf("%d->", cur->val);
            cur = cur->next;
        }
        printf("NULL\n");
    }
}

// 创建节点函数
struct ListNode* CreateNode(int x)
{
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (newNode == NULL)
    {
        perror("malloc error!");
        exit(1);
    }
    newNode->val = x;
    newNode->next = NULL;
    return newNode;
}

//1.InsHead头插函数
// 二级指针使用原因:要修改原头指针的指向(一级指针传参是值拷贝,无法修改外部指针)
struct ListNode* InsHead(struct ListNode** pphead, int n)
{
    struct ListNode* newheadNode = CreateNode(n);
    //先把 头节点拼上去
    newheadNode->next = *pphead;
    //然后在把头节点的职位 交给newheadNode
    *pphead = newheadNode;
    Print(*pphead);
    return *pphead;
}

//2.DelHead头删函数
// 二级指针使用原因:删除头节点后需要修改原头指针的指向(比如从h0改为h1),一级指针无法修改外部变量
struct ListNode* DelHead(struct ListNode** pphead)
{
    struct ListNode* newHead;
    if (*pphead == NULL)
    {
        perror("DelHead error!");
        exit(1);
    }
    else
    {
        newHead = (*pphead)->next;
        (*pphead)->next = NULL;
        free(*pphead);
        *pphead = NULL; // 释放后置空,避免野指针
    }
    Print(newHead);
    return newHead;
}

//3.InsTail尾插
// 注:尾插用一级指针即可(无需修改原头指针指向,仅遍历链表),但空链表时返回新节点更合理
struct ListNode* InsTail(struct ListNode* phead, int n)
{
    struct ListNode* newNode = CreateNode(n);
    struct ListNode* cur = phead;
    if (phead == NULL)
    {
        Print(newNode);
        return newNode;
    }
    else
    {
        while (cur->next != NULL)
        {
            cur = cur->next;
        }
        cur->next = newNode;
    }
    Print(phead);
    return phead;
}

//4.DelTail尾删
// 优化:尾删也可封装为二级指针版本(避免返回值覆盖,保持接口一致性),此处保留原逻辑+修复bug
struct ListNode* DelTail(struct ListNode* phead)
{
    struct ListNode* head = phead;
    if (head == NULL)
    {
        perror("DelTail error: empty list"); // 补充错误提示,而非直接exit
        exit(1);
    }
    else if (head->next == NULL)
    {
        printf("NULL\n");
        free(head); // 原代码先置next再free,顺序不影响,但free后head已成野指针,无需置next
        return NULL;
    }
    else
    {
        struct ListNode* tmp = head;
        while (tmp->next->next != NULL)
        {
            tmp = tmp->next;
        }
        free(tmp->next);
        tmp->next = NULL;
        // 修正:C语言无nullptr,统一用NULL(原代码混用导致编译报错)
        // tmp->next = nullptr; 
    }
    Print(head);
    return head;
}

// 查找函数
bool Find(struct ListNode* phead, int n)
{
    struct ListNode* pcur = phead;
    if (pcur == NULL)
    {
        return false;
    }
    while (pcur)
    {
        if (pcur->val == n) return true;
        pcur = pcur->next;
    }
    return false;
}

// 新增:链表销毁函数(避免内存泄漏,必补!)
// 二级指针使用原因:销毁后需要将原头指针置空,防止外部使用野指针
void DestroyList(struct ListNode** pphead)
{
    if (pphead == NULL || *pphead == NULL)
    {
        return;
    }
    struct ListNode* cur = *pphead;
    while (cur)
    {
        struct ListNode* tmp = cur;
        cur = cur->next;
        free(tmp);
    }
    *pphead = NULL; // 销毁后置空原头指针
}

测试文件实现

c 复制代码
#include"list.h"


void Init()
{
	ListNode* h0 = (ListNode*)malloc(sizeof(ListNode));
	ListNode* h1 = (ListNode*)malloc(sizeof(ListNode));
	ListNode* h2 = (ListNode*)malloc(sizeof(ListNode));
	ListNode* h3 = (ListNode*)malloc(sizeof(ListNode));
	h0->val = 0;
	h1->val = 1;
	h2->val = 2;
	h3->val = 3;
	h0->next = h1;
	h1->next = h2;
	h2->next = h3;
	h3->next = NULL;
	Print(h0);
}


void test1()
{
	ListNode* phead = NULL;
	phead = InsHead(&phead, 1);
	phead = InsHead(&phead, 2);
	phead = InsHead(&phead, 3);
	phead = InsHead(&phead, 4);
}

void test2()
{
	ListNode* phead = NULL;
	phead = InsHead(&phead, 1);
	phead = InsHead(&phead, 2);
	phead = InsHead(&phead, 3);
	phead = InsHead(&phead, 4);

	phead = DelHead(&phead);
	phead = DelHead(&phead);
	phead = DelHead(&phead);
	phead = DelHead(&phead);
}

void test3()
{
	ListNode* head = NULL;
	head = InsTail(head,3);
	head = InsTail(head, 2);
	head = InsTail(head, 1);
	head = InsTail(head, 0);

}

void test4()
{
	ListNode* phead = NULL;
	phead = InsHead(&phead, 1);
	phead = InsHead(&phead, 2);
	phead = InsHead(&phead, 3);
	phead = InsHead(&phead, 4);

	phead = DelTail(phead);
	phead = DelTail(phead);
	phead = DelTail(phead);
	phead = DelTail(phead);
}

void test5()
{
	ListNode* head = NULL;
	head = InsTail(head, 3);
	head = InsTail(head, 2);
	head = InsTail(head, 1);
	head = InsTail(head, 0);


	if (Find(head, 2) == true)printf("找到");
	else
	{
		printf("未找到");
	}

}

int main()
{
	printf("----Init函数----\n");
	Init();
	printf("----InsHead----\n");
	test1();
	printf("----DelHead----\n");
	test2();
	printf("----InsTail----\n");
	test3();
	printf("----DelTail----\n");
	test4();
	printf("----Find----\n");
	test5();

	return 0;
}

易错汇总

1.解释为什么头插参数为二级指针?并且思考总结什么情况下C语言需要用二级指针?

2.思考C语言中的 malloc检验机制与C++中的检验机制有什么不同? C语言检验机制使用的弊端在哪里?

3.思考在上述代码中为了防止内存泄漏 都做了free野指针、补充销毁函数。请阐述它们的实现方法。

4.思考在上述语法中 C语言和C++的语法上面的不同

1.关于空NULL与nullptr

2.关于结构体 内的成员函数

struct ListNode* 与struct ListNode*

相关推荐
zmzb01032 小时前
C++课后习题训练记录Day53
数据结构·c++·算法
LYFlied3 小时前
【每日算法】131. 分割回文串
前端·数据结构·算法·leetcode·面试·职场和发展
夏乌_Wx3 小时前
练题100天——DAY30:下一个更大的元素+键盘行
数据结构·算法
长安er3 小时前
LeetCode 300/152/416/32 动态规划进阶题型总结(最长递增子序列→最长有效括号)
数据结构·算法·leetcode·动态规划·剪枝
天赐学c语言3 小时前
12.18 - 有效的括号 && C语言中static的作用
数据结构·c++·算法·leecode
2401_876221343 小时前
数据结构-绪论
数据结构
别动哪条鱼3 小时前
SDL 函数对各对象缓冲区的影响
网络·数据结构·ffmpeg
jianfeng_zhu4 小时前
添加逗号问题
数据结构
前端小L4 小时前
贪心算法专题(二):波动中的智慧——只取极值「摆动序列」
数据结构·算法·贪心算法