数据结构-单链表(linked list)

一.单链表的基本概念

单链表是一种线性数据结构,由一系列节点组成,每个节点包含数据域和指针域。指针域存储下一个节点的地址,最后一个节点的指针域指向空(NULL)。与数组不同,单链表的节点在内存中不必连续存储。

二.链表的优缺点:

优点:

1.动态分配内存内存:无需预先分配固定大小,可动态扩展收缩

2.高效插入删除数据:任意位置插入删除节点时间复杂度为O(1)

3.对内存的利用率高:只占用实际需要的空间,无内存浪费

缺点:

1.随机访问效率低:访问第n个元素要从头遍历时间复杂度O(n)

2.额外存储指针:每个节点存储指针,占用额外内存空间

3.缓存:节点在内存中不连续,缓存命中率低

三.数组(顺序表)和链表的区别:

数组(顺序表):分配连续内存,固定大小,随机访问相对快,插入删除相对慢。

链表:非连续内存,需要动态申请空间大小,随机访问相对慢,插入删除相对快。

四.单向链表的代码实现:

链表实现的设计思想:

1.设计结构体:实现数据的存储,并通过指针链接节点;

2.创建结构体:初始化节点,存放数据;

3.插入数据(头插法)(尾插法):对创建节点,通过头节点查找链表来插入数据;

4.删除数据:根据输入数据,通过头节点查找存储该数据节点的位置;

5.显示数据:通过头节点访问链表;

头文件代码:
#ifndef LIST_H
#define LIST_H

#include <stdbool.h>

//结构体初始化
struct List
{
	int data;
	struct List* next;
};//!!!!结构体的定义加分号
//创建结构体
struct List* create_list(int data);

//头插法插入数据
bool insert_list_head(struct List* head,int data);

//尾插法插入数据
bool insert_list_tail(struct List* head,int data);

//删除数据
bool delete_list(struct List* head,int data);

//显示数据
void display_list(struct List* head);

//注意结束符
#endif           

实现代码部分展示:

1.设计结构体

        实现原理:

     定义一个简单的链表节点结构体 List,包含两个成员:

  • int data       :用于存储节点的数据(整数类型)
  • struct List* next :指向下一个节点的指针
struct List                    //结构体名List
{
	int data;
	struct List* next;
};

2.创建结构体

        实现原理:

        (1).函数返回值类型为struct List*,说明存在一个自定义的链表节点结构体;

        (2).动态分配内存:使用malloc动态分配内存空间(注意头文件:#include <stdlib.h>);

        (3).节点初始化:

                存储传入数据

                将next指针置空(为什么置空?)

        确保链表正确终止,避免未定义行为和内存访问错误。

        如果置空:

    next 指针会包含随机值(垃圾值)

              遍历链表时无法确定终点,可能导致无限循环或内存访问错误

        (4).调用该函数后需检查返回值是否为NULL(malloc可能失败)。

        代码:
//创建结构体
struct List* create_list(int data)
{
	struct List* head = (struct List*)malloc(sizeof(struct List));
	head->data = data;
	head->next = NULL;
    return head;//!!!返回值
}

3.插入数据(头插法)(尾插法)

        头插法是一种在链表头部插入新节点的方法,新节点成为链表的第一个节点。这种方法的时间复杂度为 O(1),因为它不需要遍历链表。

        函数名为insert_list_head,接收两个参数:链表头节点指针head和要插入的数据data

        实现原理:

        (1)检查头结点指针是否为空,为空则直接返回false表示插入失败。处理头结点为空的边界情况;

        (2)调用create_list函数创建一个新结点;

        (3)将新结点的next指针指向头结点的下一个结点;

        (4)头结点的next指针指向新结点。 

        图示:

        代码:
//头插法插入数据
bool insert_list_head(struct List* head,int data)
{
	if(head == NULL)return false;
	struct List* node = create_list(data);
	node->next = head->next;
	head->next = node;
	return true;
}

         尾插法是一种在链表尾部插入新节点的方法,新节点成为链表的最后一个节点。如果链表为空,新节点成为头节点。这种方法的时间复杂度为 O(n),因为需要遍历到链表末尾。

        实现原理:

        (1)接收链表头指针head和待插入数据data作为参数;

        (2)检查头指针是否为空,若为空返回false代表插入失败;

        (3)通过create_list函数创建包含data的新节点node;

        (4)使用临时指针p遍历链表直到找到最后一个节点(p->next为NULL);

        (5)将新节点node链接到最后一个节点的next指针上。

        图示:

        代码:
//尾插法插入数据
bool insert_list_tail(struct List* head,int data)
{
	if(head == NULL)return false;
	
	struct List* node = create_list(data);
    struct List* p = head;
	while(p->next !=NULL)
	{
	p = p->next;
	}
	p->next = node;
	return true;
}

4.删除数据

        函数接收链表头指针和要删除的数据作为参数,返回操作是否成功的布尔值。

        实现原理:

        (1)检查链表是否为空(head == NULL),如果是则直接返回false;

        (2)创建临时指针p指向头节点,while循环遍历链表,寻找数据匹配的节点;

        (3)循环条件检查p->next是否为NULL,确保不访问空指针;

        (4)如果找到匹配的节点(p->next->data == data),循环提前终止;

        (5)遍历结束后检查是否找到目标节点(p->next == NULL);

        (6)找到目标节点后,先用临时指针q保存要删除的节点地址,然后修改前驱节点的next指针跳过被删除节点;

        (7)最后释放被删除节点的内存。

        代码:
//删除数据
bool delete_list(struct List* head,int data)
{
	if(head == NULL)return false;
	struct List* p = head;
	while(p->next != NULL)
	{
		if(p->next->data == data)
		{break;}
		p = p->next;
	}
	if(p->next == NULL)return false;
	struct List* q = p->next;
	p->next = q->next;
    free(q);
	return true;
}

5.显示数据

        打印链表中除头节点外的所有节点的数据值

        实现原理:

        (1)边界条件检查:如果链表为空(head == NULL)或只有头节点(head->next == NULL),直接返回不执行后续操作。

        (2)初始化指针p指向头节点的下一个节点(即第一个实际数据节点),跳过头节点。

        (3)循环遍历链表:从p指向的节点开始,依次打印每个节点的data值,直到链表末尾(p == NULL)。

        代码:
//显示数据
void display_list(struct List* head)
{
	if(head ==NULL || head->next == NULL)return;
	struct List* p = head->next;//不赋值head,因为后面输出是会输出head
	while(p != NULL)
	{
		printf("%d ",p->data);
		p = p->next;
	}printf("\n");
}

完整代码:

list.c

//list.c文件
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "list.h"

//创建结构体
struct List* create_list(int data)
{
	struct List* head = (struct List*)malloc(sizeof(struct List));
	head->data = data;
	head->next = NULL;
    return head;//!!!返回值
}
//头插法插入数据
bool insert_list_head(struct List* head,int data)
{
	if(head == NULL)return false;
	struct List* node = create_list(data);
	node->next = head->next;
	head->next = node;
	return true;
}
//尾插法插入数据
bool insert_list_tail(struct List* head,int data)
{
	if(head == NULL)return false;
	
	struct List* node = create_list(data);
    struct List* p = head;
	while(p->next !=NULL)
	{
	p = p->next;
	}
	p->next = node;
	return true;
}
//删除数据
bool delete_list(struct List* head,int data)
{
	if(head == NULL)return false;
	struct List* p = head;
	while(p->next != NULL)
	{
		if(p->next->data == data)
		{break;}
		p = p->next;
	}
	if(p->next == NULL)return false;
	struct List* q = p->next;
	p->next = q->next;
    free(q);
	return true;
}
//显示数据
void display_list(struct List* head)
{
	if(head ==NULL || head->next == NULL)return;
	struct List* p = head->next;//不赋值head,因为后面输出是会输出head
	while(p != NULL)
	{
		printf("%d ",p->data);
		p = p->next;
	}printf("\n");
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "list.h"


int main()
{
	struct List* head = create_list(0);        // 创建头指针

	for(int i = 0; i < 10; i++)                // 插入数据
	{

		//insert_list_head(head, data);        // 头插法
		insert_list_head(head, i);		       // 尾插法
	}

	display_list(head);                        // 显示数据
	printf("请输入需要删除的数据:\n");         
	int num = 0;
	scanf("%d", &num);//!!!输入取地址
	
	delete_list(head,num);                     // 删除数据
	display_list(head);
}

四.单链表的应用场景

  • 实现栈、队列等抽象数据类型。
  • 内存管理中的动态分配。
  • 需要频繁插入/删除的场景(如任务调度)。
### 数据结构实验:单链表的实现方法与示例代码 #### 单链表简介 单链表是一种常见的线性数据结构,其中每个节点包含两部分:存储的数据项以及指向下一个节点的指针。这种结构允许动态分配内存来保存任意数量的元素。 #### 改进后的单链表设计思路 为了克服传统单链表长时间使用后可能出现大量堆空间碎片化的问题,在“单链表”的内部增加了一片预留的空间,所有的`Node`对象都在这片空间中动态创建和销毁[^1]。此改进有助于减少因频繁增删操作而产生的内存碎片,从而提高程序性能。 #### Python中的单链表实现 下面是一个简单的Python版本单链表类定义及其基本功能: ```python class Node: def __init__(self, data=None): self.data = data # 节点储存的数据 self.next = None # 下一节点链接 class SingleLinkedList: def __init__(self): self.head = None def append(self, new_data): newNode = Node(new_data) if not self.head: self.head = newNode else: last_node = self.head while last_node.next: last_node = last_node.next last_node.next = newNode def display(self): current_node = self.head while current_node is not None: print(current_node.data,end=" -> ") current_node = current_node.next print("None") # 创建并测试单链表实例 linked_list = SingleLinkedList() elements_to_add = ['a', 'b', 'c'] for element in elements_to_add: linked_list.append(element) linked_list.display() # 输出 a -> b -> c -> None ``` 上述代码展示了如何构建一个具有追加新节点(`append`)和遍历显示所有节点(`display`)能力的基础版单向链表。通过这种方式可以有效地管理一系列相互关联的对象,并支持高效的插入/移除操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值