单链表实现全解析

代码大纲

单链表实现分析

以下代码实现了单链表的基本操作,包括创建、插入、删除、查找和修改等功能。每个函数的功能和实现方式如下:

链表节点创建

c 复制代码
SLNode* SLBuNode(Etype x)
{
    SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
    if (newnode == NULL)
    {
        perror("Malloc");
        exit(1);
    }
    newnode->data = x;
    newnode->next = NULL;
    return newnode;
}

该函数用于动态分配一个新节点,并初始化其数据和指针字段。如果内存分配失败,程序会报错并退出。

头指针初始化

c 复制代码
void SLHandInit(SLNode* L)
{
    assert(L);
    L->data = 0;
    L->next = NULL;
}

初始化头指针,设置链表长度为0,并清空next指针。

链表销毁

c 复制代码
void SLDestroy(SLNode* L)
{
    assert(L);

    SLNode* p = L->next;
    while (p != NULL)
    {
        SLNode* temp = p;
        p = p->next;
        free(temp);
    }
    L->next = NULL;
    L->data = 0;
}

释放链表所有节点内存,重置头指针状态。

链表打印

c 复制代码
void SLPrint(SLNode* L)
{
    assert(L);
    SLNode* p = L->next;
    while (p != NULL)
    {
        printf("%d->", p->data);
        p = p->next;
    }
    printf("NULL\n");
}

遍历链表并打印每个节点的数据值。

数据插入操作

c 复制代码
void SLPushFront(SLNode* L, Etype x)
{
    assert(L);
    SLNode* p = L->next;
    SLNode* q = SLBuNode(x);
    q->next = p;
    L->next = q;
    L->data++;
}

在链表头部插入新节点。

c 复制代码
void SLPushBack(SLNode* L, Etype x)
{
    assert(L);
    SLNode* p = L->next;
    SLNode* q = SLBuNode(x);
    if (L->next == NULL)
    {
        L->next = q;
    }
    else
    {
        while (p->next != NULL)
        {
            p = p->next;
        }
        p->next = q;
    }
    L->data++;
}

在链表尾部插入新节点。

c 复制代码
void SLPushN(SLNode* L, Etype x, int n)
{
    assert(L);
    if (L->next == NULL)
    {
        printf("L NULL!\n");
        return;
    }
    if (n > L->data)
    {
        printf("n超过了SList长度\n");
        return;
    }
    else if (n == 0)
    {
        SLPushFront(L, x);
        return;
    }
    else if (n < 0)
    {
        printf("n < 0\n");
        return;
    }

    SLNode* p = L->next;
    SLNode* q = SLBuNode(x);
    SLNode* pre = p->next;
    for (int i = 1; i < n; i++)
    {
        p = pre;
        pre = pre->next;
    }
    p->next = q;
    q->next = pre;
    L->data++;
}

在指定位置后插入新节点,包括边界条件检查。

数据删除操作

c 复制代码
void SLPopFront(SLNode* L)
{
    assert(L);
    SLNode* p = L->next;
    if (p == NULL)
    {
        printf("L NULL!\n");
        return;
    }
    L->next = p->next;
    L->data--;
    free(p);
}

删除链表头部节点。

c 复制代码
void SLPopBack(SLNode* L)
{
    assert(L);
    SLNode* p = L->next;
    if (p == NULL)
    {
        printf("L NULL!\n");
        return;
    }
    SLNode* pre = p->next;
    while (pre->next != NULL)
    {
        p = pre;
        pre = p->next;
    }
    p->next = NULL;
    L->data--;
    free(pre);
}

删除链表尾部节点。

c 复制代码
void SLPopN(SLNode* L, int n)
{
    assert(L);
    if (L->next == NULL)
    {
        printf("L NULL!\n");
        return;
    }
    if (n > L->data)
    {
        printf("n超过了SList长度\n");
        return;
    }
    else if (n == 0)
    {
        SLPopFront(L);
        return;
    }
    else if (n < 0)
    {
        printf("n < 0\n");
        return;
    }

    SLNode* p = L->next;
    SLNode* pre = p->next;
    for (int i = 1; i < n; i++)
    {
        p = pre;
        pre = pre->next;
    }
    p->next = pre->next;
    L->data--;
    free(pre);
}

删除指定位置后的节点,包括边界条件检查。

数据查找和修改

c 复制代码
int Compare(Etype x, Etype y)
{
    return x == y;
}

int SLFind(SLNode* L, Etype x)
{
    assert(L);
    SLNode* p = L->next;
    int num = 0;
    while (num < L->data)
    {
        num++;
        if (Compare(p->data, x))
            return num;
        p = p->next;
    }
    return -1;
}

查找特定值在链表中的位置。

c 复制代码
void SLModify(SLNode* L, Etype x, int n)
{
    assert(L);
    if (L->data < n)
    {
        printf("n超过了SList长度\n");
        return;
    }
    if (L->next == NULL)
    {
        printf("L NULL!\n");
        return;
    }
    SLNode* p = L->next;
    SLNode* q = SLBuNode(x);
    SLNode* pre = p->next;
    if (n == 1)
    {
        L->next = q;
        q->next = p->next;
        return;
    }
    else if(n < 1)
    {
        printf("n < 1\n");
        return;
    }
    for (int i = 2; i < n; i++)
    {
        p = pre;
        pre = pre->next;
    }
    p->next = q;
    q->next = pre->next;
    free(pre);
}

修改链表中指定位置的节点值。

特殊操作

c 复制代码
SLNode* SLReEy(SLNode* L, Etype x)
{
    assert(L);
    SLNode* p = L->next;
    SLNode* prv = L;
    SLNode* pre = p->next;
    int num = 0;
    while (1)
    {
        if (Compare(p->data, x))
        {
            prv->next = pre;
            free(p);
            p = pre;
            if (pre == NULL)
                break;
            pre = pre->next;
            continue;
        }
        num++;
        prv = prv->next;
        p = pre;
        if (pre == NULL)
            break;
        pre = pre->next;
    }
    L->data = num;
}

删除链表中所有包含特定值的节点。

c 复制代码
void SLNewHand(SLNode* L)
{
    assert(L);
    if (L->next == NULL)
        return;
    SLNode* p = L->next;
    SLNode* pre = p->next;
    SLNode* prv = NULL;
    while(1)
    {
        p->next = prv;
        prv = p;
        if (pre == NULL)
            break;
        p = pre;
        pre = pre->next;
    }
    L->next = p;
}

反转链表顺序。

完整代码

SList.c

cpp 复制代码
#include"SList.h"

//创建链表项
SLNode* SLBuNode(Etype x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("Malloc");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

//创建头指针
void SLHandInit(SLNode* L)
{
	assert(L);
	L->data = 0;
	L->next = NULL;
}

//销毁单链表
void SLDestroy(SLNode* L)
{
	assert(L);

	SLNode* p = L->next;
	while (p != NULL)
	{
		SLNode* temp = p;
		p = p->next;
		free(temp);
	}
	L->next = NULL;
	L->data = 0;
}

//打印单链表
void SLPrint(SLNode* L)
{
	assert(L);
	SLNode* p = L->next;
	while (p != NULL)
	{
		printf("%d->", p->data);
		p = p->next;
	}
	printf("NULL\n");
}

//头插插入数据
void SLPushFront(SLNode* L, Etype x)
{
	assert(L);
	SLNode* p = L->next;
	SLNode* q = SLBuNode(x);
	q->next = p;
	L->next = q;
	L->data++;
}

//尾插插入数据
void SLPushBack(SLNode* L, Etype x)
{
	assert(L);
	SLNode* p = L->next;
	SLNode* q = SLBuNode(x);
	if (L->next == NULL)
	{
		L->next = q;
	}
	else
	{
		while (p->next != NULL)
		{
			p = p->next;
		}
		p->next = q;
	}
	L->data++;
}

//头插删除数据
void SLPopFront(SLNode* L)
{
	assert(L);
	SLNode* p = L->next;
	if (p == NULL)
	{
		printf("L NULL!\n");
		return;
	}
	L->next = p->next;
	L->data--;
	free(p);
}

//尾插删除数据
void SLPopBack(SLNode* L)
{
	assert(L);
	SLNode* p = L->next;
	if (p == NULL)
	{
		printf("L NULL!\n");
		return;
	}
	SLNode* pre = p->next;
	while (pre->next != NULL)
	{
		p = pre;
		pre = p->next;
	}
	p->next = NULL;
	L->data--;
	free(pre);
}

//在SList的第n位之后添加数据
void SLPushN(SLNode* L, Etype x , int n)
{
	assert(L);
	if (L->next == NULL)
	{
		printf("L NULL!\n");
		return;
	}
	if (n > L->data)
	{
		printf("n超过了SList长度\n");
		return;
	}
	else if (n == 0)
	{
		SLPushFront(L, x);
		return;
	}
	else if (n < 0)
	{
		printf("n < 0\n");
		return;
	}

	SLNode* p = L->next;
	SLNode* q = SLBuNode(x);
	SLNode* pre = p->next;
	for (int i = 1; i < n; i++)
	{
		p = pre;
		pre = pre->next;
	}
	p->next = q;
	q->next = pre;
	L->data++;
}


//删除SList上第n位之后的数据
void SLPopN(SLNode* L, int n)
{
	assert(L);
	if (L->next == NULL)
	{
		printf("L NULL!\n");
		return;
	}
	if (n > L->data)
	{
		printf("n超过了SList长度\n");
		return;
	}
	else if (n == 0)
	{
		SLPopFront(L);
		return;
	}
	else if (n < 0)
	{
		printf("n < 0\n");
		return;
	}

	SLNode* p = L->next;
	SLNode* pre = p->next;
	for (int i = 1; i < n; i++)
	{
		p = pre;
		pre = pre->next;
	}
	p->next = pre->next;
	L->data--;
	free(pre);
}

//比较两个Etype类型数据是否相同
int Compare(Etype x, Etype y)
{
	return x == y;
}

//查找SList上数据位置
int SLFind(SLNode* L, Etype x)
{
	assert(L);
	SLNode* p = L->next;
	int num = 0;
	while (num < L->data)
	{
		num++;
		if (Compare(p->data, x))
			return num;
		p = p->next;
	}
	return -1;
}

//修改SList上第n位数据
void SLModify(SLNode* L, Etype x, int n)
{
	assert(L);
	if (L->data < n)
	{
		printf("n超过了SList长度\n");
		return;
	}
	if (L->next == NULL)
	{
		printf("L NULL!\n");
		return;
	}
	SLNode* p = L->next;
	SLNode* q = SLBuNode(x);
	SLNode* pre = p->next;
	if (n == 1)
	{
		L->next = q;
		q->next = p->next;
		return;
	}
	else if(n < 1)
	{
		printf("n < 1\n");
		return;
	}
	for (int i = 2; i < n; i++)
	{
		p = pre;
		pre = pre->next;
	}
	p->next = q;
	q->next = pre->next;
	free(pre);
}

//去除SList上的所有x
SLNode* SLReEy(SLNode* L, Etype x)
{
	assert(L);
	SLNode* p = L->next;
	SLNode* prv = L;
	SLNode* pre = p->next;
	int num = 0;
	while (1)
	{
		if (Compare(p->data, x))
		{
			prv->next = pre;
			free(p);
			p = pre;
			if (pre == NULL)
				break;
			pre = pre->next;
			continue;
		}
		num++;
		prv = prv->next;
		p = pre;
		if (pre == NULL)
			break;
		pre = pre->next;
	}
	L->data = num;
}

//反转SList
void SLNewHand(SLNode* L)
{
	assert(L);
	if (L->next == NULL)
		return;
	SLNode* p = L->next;
	SLNode* pre = p->next;
	SLNode* prv = NULL;
	while(1)
	{
		p->next = prv;
		prv = p;
		if (pre == NULL)
			break;
		p = pre;
		pre = pre->next;
	}
	L->next = p;
}

SList.h

cpp 复制代码
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

#define MAX 100

typedef int Etype;
struct SListNode
{
	Etype data;
	struct SListNode* next; //指向下一个节点的指针
};
typedef struct SListNode SLNode;

void SLHandInit(SLNode* L);//创建头指针

void SLDestroy(SLNode* L);//销毁单链表

SLNode* SLBuNode(Etype x);//创建链表项

void SLPushFront(SLNode* L, Etype x);//头插插入数据

void SLPushBack(SLNode* L, Etype x);//尾插插入数据

void SLPushN(SLNode* L, Etype x, int n);//在SList的第n位之后添加数据

void SLPopFront(SLNode* L);//头插删除数据

void SLPopBack(SLNode* L);//尾插删除数据

void SLPopN(SLNode* L, int n);//删除SList上第n位的数据

int Compare(Etype x, Etype y);//比较两个Etype类型数据是否相同

int SLFind(SLNode* L, Etype x);//查找SList上数据位置

void SLModify(SLNode* L, Etype x, int n);//修改SList上第n位数据

SLNode* SLReEy(SLNode* L, Etype x);//去除SList上的所有x

void SLNewHand(SLNode* L);//反转SList

void SLPrint(SLNode* L);//打印单链表

测试代码

test.c

cpp 复制代码
#include "SList.h"

void test()
{
	//创建指针
	SLNode L;
	SLHandInit(&L);
	
	printf("1.尾插法在SList中添加数据:\t");
	//尾插法添加数据
	SLPushBack(&L, 1);
	SLPushBack(&L, 2);
	SLPushBack(&L, 3);
	SLPushBack(&L, 4);
	SLPushBack(&L, 5);
	//输出单链表
	SLPrint(&L);
	printf("\n");

	printf("2.头插法在SList中添加数据:\t");
	//头插法添加数据
	SLPushFront(&L, 1);
	SLPushFront(&L, 2);
	SLPushFront(&L, 3);
	SLPushFront(&L, 4);
	SLPushFront(&L, 5);
	//输出单链表
	SLPrint(&L);
	printf("\n");

	printf("3.头插法删除SList中数据:\t");
	//头删
	SLPopFront(&L);
	SLPopFront(&L);
	SLPopFront(&L);
	//输出单链表
	SLPrint(&L);
	printf("\n");

	printf("4.尾插法删除SList中数据:\t");
	//尾删
	SLPopBack(&L);
	SLPopBack(&L);
	SLPopBack(&L);
	//输出单链表
	SLPrint(&L);
	printf("\n");

	printf("5.在SList的第n位之后添加数据:\t");
	///在SList的第n位之后添加数据
	SLPushN(&L, 9, 1);
	SLPushN(&L, 9, 2);
	SLPushN(&L, 9, 3);
	//输出单链表
	SLPrint(&L);
	printf("\n");

	printf("6.删除SList上第n位之后的数据:\t");
	//删除SList上第n位的数据
	SLPopN(&L, 1);
	SLPopN(&L, 2);
	SLPopN(&L, 3);
	//输出单链表
	SLPrint(&L);
	printf("\n");

	printf("7.在SList上查找数据位置:\t");
	//查找SList上数据位置
	int a = SLFind(&L, 9);
	int b = SLFind(&L, 1);
	int c = SLFind(&L, 2);
	printf("%d %d %d\n", a, b, c);
	printf("\n");

	printf("8.在SList上修改第n位数据:\t");
	//修改SList上第n位数据
	SLModify(&L, 8, a);
	SLModify(&L, 8, b);
	SLModify(&L, 8, c);
	//输出单链表
	SLPrint(&L);
	printf("\n");

	printf("9.去除SList上的所有与x相等数据:\t");
	//去除SList上的所有x
	SLReEy(&L, 8);
	//输出单链表
	SLPrint(&L);
	printf("\n");
	printf("现在SList上所存剩余数据量为:\t%d\n", L.data);
	printf("\n");

	printf("11.尾插法重新在SList中添加数据:\t");
	//尾插法添加数据
	SLPushBack(&L, 1);
	SLPushBack(&L, 2);
	SLPushBack(&L, 3);
	SLPushBack(&L, 4);
	SLPushBack(&L, 5);
	//输出单链表
	SLPrint(&L);
	printf("\n");

	printf("反转NList中的全部数据数据:\t");
	//反转SList
	SLNewHand(&L);
	//输出单链表
	SLPrint(&L);

	//销毁指针
	SLDestroy(&L);
}


int main()
{
	test();

	return 0;
}

该实现覆盖了单链表的基本操作,结构清晰,适合作为基础数据结构的教学示例。

相关推荐
为何创造硅基生物4 小时前
C语言 结构体内存对齐规则(通俗易懂版)
c语言·开发语言
仰泳之鹅5 小时前
【C语言】自定义数据类型2——联合体与枚举
c语言·开发语言·算法
jolimark5 小时前
C语言自学攻略:小白入门三步走
c语言·编程入门·学习路线·实践项目·自学攻略
于小猿Sup6 小时前
VMware在Ubuntu22.04驱动Livox Mid360s
linux·c++·嵌入式硬件·自动驾驶
cen__y6 小时前
Linux12(Git01)
linux·运维·服务器·c语言·开发语言·git
社交怪人7 小时前
【算平均分】信息学奥赛一本通C语言解法(题号2071)
c语言·开发语言
卢锡荣7 小时前
单芯通吃,盲插标杆 —— 乐得瑞 LDR6020,Type‑C 全场景互联 “智慧芯”
c语言·开发语言·计算机外设
Mr. zhihao8 小时前
深入解析redis基本数据结构
数据结构·数据库·redis
念何架构之路8 小时前
Go语言加密算法
数据结构·算法·哈希算法
AI科技星8 小时前
《数学公理体系·第三部·数术几何》(2026 年版)
c语言·开发语言·线性代数·算法·矩阵·量子计算·agi