单链表实现全解析

代码大纲

单链表实现分析

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

链表节点创建

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 小时前
栈实现表达式求值
数据结构·算法·图论
满天星83035774 小时前
【C++】右值引用和移动语义
开发语言·c++·redis·visual studio
消失的旧时光-19434 小时前
c语言 内存管理(malloc, calloc, free)
c语言·开发语言
degen_4 小时前
注册协议通知
c语言·笔记
蜕变的土豆4 小时前
三、cmake语法-提高篇
c++·软件构建
Yupureki5 小时前
从零开始的C++学习生活 19:C++复习课(5.4w字全解析)
c语言·数据结构·c++·学习·1024程序员节
ゞ 正在缓冲99%…6 小时前
leetcode1312.让字符串成为回文串的最少插入次数
数据结构·算法·leetcode·动态规划·记忆化搜索
laocooon5238578866 小时前
寻找使a×b=c成立的最小进制数(2-16进制)
数据结构·算法
默默的流星雨6 小时前
TARJAN相关
c++·算法·深度优先·图论