链表基础学习

链表的基本定义:

        链表是一种数据结构,用于存储和组织数据。它由一系列称为结点的元素组成,每个结点包含数据和一个指向下一个结点的指针。结点通过指针的链接顺序连接在一起,形成一个链式结构。

        与数组不同,链表的元素在内存中不是连续存放的,而是通过指针相互连接。这种特性使得链表具有动态性,可以在运行时快速地插入和删除元素,而无需移动其他元素。

        链表可以分为单向链表和双向链表。单向链表中,每个结点只有一个指向下一个结点的指针;双向链表中,每个结点既有一个指向下一个结点的指针,又有一个指向前一个结点的指针。

链表的组成:

        链表的基本单位是结点,链表的结点一般由两个域组成:数据域(data filed)和指针域(pointer filed)。

        1、数据域:存储链表所持有的数据。它可以是任意类型的数据,如整形、字符、对象等,根据实际需求而定。

        2、指针域:指针域存储指向下一个结点(或上一个结点,如果是双向链表)的指针。它保存了连接结点之间关系的信息,使得链表能够按顺序访问各个结点。如果指针为 null(空指针),表示没有下一个结点,链表在此处结束。

链表的构造:

        数据结构的基本单位一般由结构体来构造完成,这里分别用两个不同的结构体来构造链表的结点和链表本身。

        1、结点定义:data指向数据域,*next指向下一个结点

typedef struct node
{
    int data;
    struct node* next;
}Node;

        2、单链表定义:head指向头结点,end指向尾结点,length为链表的长度

typedef struct linklist
{
    Node* head;
    Node* end;
    int length;
}LL;

链表功能的实现:

        如今链表的基本单位已经构造完成,下面开始实现单链表的基本功能。

        先定义单链表的基本功能函数。

        下面是单链表的功能函数头文件 linklist.h。

//linklist.h
//用C语言实现单链表基本功能
#pragma once

#ifndef LINKLIST
#define LINKLIST

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

typedef int Type;//数据域的数据类型
#define TYPE_P "%d->"
#define TYPE_S "%d"

typedef struct node{
	Type data;
	struct node * next;
}Node;

typedef struct linklist{
	Node *head;
	Node *end;
	int length;
}LL;

//1.单链表的创建
LL* list_init();//链表的初始化函数

//链表结点的初始化函数
Node* node_init(Type val);

//2.单链表数据的结点插入
//①向单链表尾部插入数据(尾插)
void list_insert_end(LL* list, Type val);

//②向单链表指定位置插入数据
void list_insert(LL* list, Type val, int index);

//3.单链表结点的删除,并返回删除的数据
Type list_delete_Node(LL* list, int index);//根据位置删除

//4.单链表的输出
void list_print(LL* list);

//5.单链表的销毁
void list_destroy(LL* list);

#endif

        接着开始实现链表的基本功能。

        以下是单链表的功能函数源文件 linklist.c。

//linklist.c
//单链表功能实现
#include "linklist.h"

//1.单链表的创建
LL* list_init()//链表的初始化函数
{
	//创建一个单链表结构体的动态内存
	LL* temp = (LL*)malloc(sizeof(LL));

	if (NULL == temp)
	{//如果动态内存开辟失败,指针指向了NULL
		puts("动态内存开辟失败!\n");
		return NULL;
	}

	temp->head = temp->end = NULL;//单链表头结点和尾结点指向空
	temp->length = 0;//单链表的长度初始化为0

	return temp;//返回单链表结构体的动态内存
}

//链表结点的初始化函数
Node* node_init(Type val)
{
	//创建一个单链表结点结构体的动态内存
	Node* New = (Node*)malloc(sizeof(Node));

	if (NULL == New)
	{//如果单链表结点动态内存开辟失败,New指针指向了NULL
		puts("动态内存开辟失败!\n");
		return NULL;
	}

	New->data = val;
	New->next = NULL;

	return New;//返回单链表结点结构体的动态内存
}

//2.单链表数据的结点插入
//①向单链表尾部插入数据(尾插)
void list_insert_end(LL* list, Type val)
{
	if (NULL == list)
	{//如果链表不存在,则不允许插入数据
		puts("链表不存在,数据插入失败!\n");
		return;
	}

	if (NULL == list->head)
	{//如果是创建链表的第一个结点,它既是链表的头部也是链表的尾部
		list->head = list->end = node_init(val);
		list->length++;//链表长度+1
	}
	else
	{//如果不是创建链表的第一个结点,就需要将新节点连接上链表的上一个结点
		list->end->next = node_init(val);//创建一个新结点,并连接上链表的尾部
		list->end = list->end->next;//新结点成为链表尾部
		list->length++;//链表长度+1
	}
}

//②向单链表指定位置插入数据
void list_insert(LL* list, Type val, int index)
{
	if (NULL == list)
	{//如果链表不存在,则不允许插入数据
		puts("链表不存在,数据插入失败!\n");
		return;
	}

	if (index <= 0 || index > list->length + 1)
	{//如果插入的位置小于0或者大于链表长度+1,则不允许插入数据
		puts("插入数据的位置错误,数据插入失败!\n");
		return;
	}

	if (1 == index)
	{//如果插入的位置在头结点之前
		Node* New = node_init(val);//创建一个新结点
		New->next = list->head;//新结点链接上头结点
		list->head = New;//使新结点成为新的头结点
		list->length++;
		return;
	}

	if (index == list->length + 1)
	{//如果插入结点在尾结点后一个位置
		//Node* New = node_init(val);//创建一个新结点
		//list->end->next = New;//链表的尾部链接上新的结点
		//list->end = New;//新结点成为链表的尾结点
		//list->length++;
		list_insert_end(list,val);//调用链表的尾插函数完成尾部插入
		return;
	}

	else
	{//在链表的中间插入结点
		Node *temp = list->head;
		for (int i = 1; i < index - 1; ++i)
		{//找到插入位置的前一个结点
			temp = temp->next;
		}

		Node* New = node_init(val);//创建一个新结点
		New->next = temp->next;//新结点连接上插入之后的结点(先连接后面的结点,防止先连接前面后无法再找到改位置后面的位置索引)
		temp->next = New;//插入位置之前的结点连接上新结点
		list->length++;//链表长度 + 1
	}
}

//3.单链表结点的删除,并返回删除的数据
Type list_delete_Node(LL* list, int index)
{//根据位置删除
	if (NULL == list)
	{//如果链表不存在,则不允许删除数据
		puts("链表不存在,数据删除失败!\n");
		return -1;
	}

	if (NULL == list->head)
	{//如果链表的头结点不存在,则不允许删除数据
		puts("链表头结点不存在,数据删除失败!\n");
		return -1;
	}

	if (index <= 0 || index > list->length + 1)
	{//如果插入的位置小于0或者大于链表长度+1,则不允许删除数据
		puts("删除数据的位置错误,数据删除失败!\n");
		return -1;
	}

	if (1 == index)
	{//删除头结点
		Node* temp = list->head;//记录被删除的头结点
		list->head = list->head->next;//头结点的下一个结点成为新的头部
		Type val = temp->data;//记录被删除的结点数据
		free(temp);
		list->length--;
		return val;
	}

	if (index == list->length)
	{//如果删除尾结点
		Node* temp = list->head;//创建一个临时链表
		for (int i = 1; i < index; ++i)
		{//找到被删除位置的前一个结点
			temp = temp->next;
		}
		Type val = temp->data;//记录被删除的数据
		free(list->end);//释放尾部结点
		list->end = temp;//尾部结点前的一个结点成为新的尾部
		list->length--;
		return val;
	}

	else
	{//删除中间结点
		Node* temp1 = list->head;//创建一个临时链表1
		for (int i = 1; i < index - 1; ++i)
		{//找到删除位置的前一个结点
			temp1 = temp1->next;
		}
		Node* temp2 = temp1->next;//记录被删除结点的位置
		Type val = temp2->data; //记录被删除结点的数据
		temp1->next = temp2->next;//删除位置的前一个结点跳过删除位置,指向下一个结点
		free(temp2);
		list->length--;
		return val;//返回被删除的结点数据
	}
}
//4.单链表的输出
void list_print(LL* list)
{
	if (NULL == list)
	{//如果链表不存在,则不允许输出数据
		puts("链表不存在,数据输出失败!\n");
		return;
	}

	if (NULL == list->head)
	{//如果链表的头结点不存在,则不允许输出数据
		puts("链表头结点不存在,数据输出失败!\n");
		return;
	}

	//单链表存在,正常输出
	for (Node* temp = list->head; temp != NULL; temp = temp->next)
	{
		printf(TYPE_P, temp->data);
	}
	puts("NULL");
}

//5.单链表的销毁
void list_destroy(LL* list)
{
	if (NULL == list)
	{//如果链表不存在,则不用再销毁链表
		return;
	}

	Node* currNode = list->head;//创建一个链表记录当前要释放的结点
	Node* nextNode;//创建一个链表记录下一个结点

	while (currNode != NULL)//遍历链表释放每个结点的内存
	{//当当前链表的结点不为空时,删除当前结点并记录下一个结点
		nextNode = currNode->next;//暂存下一个结点的数据
		free(currNode);//释放当前结点的数据
		currNode = nextNode;//将刚才暂存的结点值赋给当前结点
	}

	free(list);//释放链表结构体的内存
	return;
}

链表的功能测试:

        编写主函数来测试linklist功能。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#include "linklist.h"

void linklist_test();//链表功能测试函数

int main()
{
	linklist_test();

	system("pause");
	return 0;
}

void linklist_test()//链表功能测试函数
{
	LL* LL1 = list_init();//单链表的创建

	Type values;//创建临时变量

	puts("请输入单链表的数据(以回车结束):");

	do{
		scanf(TYPE_S, &values);//输入数据进临时变量
		list_insert_end(LL1,values);//链表尾插变量
	} while ('\n' != getchar());

	puts("单链表当前的数据为:");
	list_print(LL1);//输出链表数据
	list_destroy(LL1);//使用完成以后销毁链表
}

        运行结果:输入1 2 3 5 6 4

        链表测试成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值