数据结构——线性表②(链表)

news2024/11/15 15:49:27

《数据结构——线性表①(顺序表)》一文中已经讲了线性表顺序存储–顺序表相关内容,
这篇文章一起来学习 线性表的链式存储–链表↓↓↓↓↓

一、链表的定义

线性表的链式存储称为链表,那什么是链式存储呢
在这里插入图片描述
其实理解起来就和火车差不多,一节一节的火车车厢被链条连在一起,每节车厢都是独立存在的,根据不同场景的需求,火车的车厢数可以增加或者减少,这是动态可变的;

而链表就拥有以上的这些属性,一节一节的车厢就是链表中的一个一个的节点,这些节点都是独立存在的,而指向下一个节点的指针就是连接下一节车厢的链条。
在这里插入图片描述
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表其实在内存中的存储并不是连续的,而是随机的,而这里线性意思是,在逻辑上,表中的所有节点都串在一起了,像一条线,可以通过节点的指针域找到其他节点。

二、链表的分类

2.1 单链表

上面介绍链表引用的图片就是一个单链表。下面这也是一个单链表
单链表的每个节点存储有两个元素,一个数据域,一个指针域。
在这里插入图片描述

  • 单链表的特点是:单链表只能从头节点开始遍历,无法直接访问中间或尾部的节点。

2.2 双链表

双链表的每个节点有三个元素,两个指针域(分别是指向前一个节点和指向下一个节点的指针),一个数据域。
在这里插入图片描述
双链表的特点是:双链表打破了单链表只能向后访问的局限性,双链表在除头尾节点之外的节点处,可以向前访问。

2.3 循环链表

  • 单向循环链表↓↓↓
    在这里插入图片描述
    链表的最后一个节点指向头节点的单向链表叫 – 单向循环链表

  • 双向循环链表↓↓↓
    在这里插入图片描述

2.4 头节点和头指针

  • 头节点:链表中的一个特殊节点,它不存储任何数据,只是作为链表的起始位置
  • 头指针:指向链表第一个元素的指针

所以,对于带头结点的链表,头指针指向的是头节点; 对于不带头节点的链表,头指针指向的是链表的第一个节点。

头节点在链表数据结构中有很多好处。

  1. 头节点可以防止链表为空时指针指向NULL,从而导致程序出错。在带头结点的链表中,当链表为空时,头结点的指针域指向头结点本身,而不是NULL

  2. 头节点方便了单链表的特殊操作,例如在表头插入或删除节点。如果不存在头节点,当进行这些操作时,就需要考虑处理空链表的情况,这会增加代码的复杂性和出现bug的机会。

  3. 头节点统一了空表和非空表的处理方式。无论链表是否为空,头指针始终指向头结点,这使得对第一个节点的操作与对中间节点的操作保持一致,减少了代码量,并降低了出现错误的可能性。

链表的结构非常多样,以下情况组合起来就有8种 (2×2×2)链表结构:
在这里插入图片描述

三、单链表的基本操作及实现函数

3.1 单链表的类型定义

typedef int elemType;
struct SqList
{
	elemType data;
	struct SqList* next;
};
typedef struct SqList SList;

3.2 带头结点的单链表代码实例

创建和初始化:创建一个SList类型的结构体变量,数据域不存储有效数据,指针域设置为空

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

typedef int elemType;
typedef struct SqList
{
	elemType data;
	struct SqList* next;
}SqList,*pList;
typedef struct SqList SList;

pList CreatList();//创建一个带头结点的单向链表
pList BuyNode(elemType x);//创建一个节点
void HeadAdd(pList list,elemType x);//向链表中插入数据x,头插法
void Delete(pList list,elemType x);//删除链表中的数据x
void Change(pList list, elemType x,elemType newdata);//修改数据
size_t Find(pList list, elemType x);//查找数据X,返回节点位序
void Destroy(pList list);//销毁链表
void Print(pList list);//打印链表中的数据
#include"SqList.h"

pList CreatList()
{
	pList list = (pList)malloc(sizeof(SqList));
	assert(list);
	list->data = -1;//头节点的数据域不存储数据,这里我初始化为-1
	list->next = NULL;
	return list;
}

pList BuyNode(elemType x)
{
	pList node = (pList)malloc(sizeof(SqList));
	assert(node);
	node->data = x;
	node->next = NULL;
	return node;
}

void HeadAdd(pList list, elemType x)
{
	assert(list);
	pList node = BuyNode(x);
	node->next = list->next;
	list->next = node;
}

void Print(pList list)
{
	assert(list);
	pList pcur = list->next;
	while (pcur)
	{
		printf("%d->",pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

void Delete(pList list, elemType x)
{
	assert(list);
	pList pcur = list->next;
	pList prev = list;
	while (pcur)
	{
		if (pcur->data == x)
		{
			prev->next = pcur->next;
			free(pcur);
			pcur = NULL;
			return;
		}
		prev = prev->next;
		pcur = pcur->next;
	}
	printf("Delete fail\n");
}

void Change(pList list, elemType x, elemType newdata)
{
	assert(list);
	pList pcur = list->next;
	while (pcur)
	{
		if (pcur->data == x)
		{
			pcur->data = newdata;
			return;
		}
		pcur = pcur->next;
	}
	printf("Change fail\n");
}

size_t Find(pList list, elemType x)
{
	assert(list);
	int count = 1;
	pList pcur = list->next;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return count;
		}
		pcur = pcur->next;
		count++;
	}
	printf("Find fail\n");
}

void Destroy(pList list)
{
	pList pcur = list->next;
	pList next = NULL;
	while (pcur)
	{
		next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(list);
	list = NULL;
}

四、其他链表

3.1 双链表

以下是一个简单的C语言实现双链表的示例代码:

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

// 定义链表节点结构体
struct Node {
    int data;               // 存储数据
    struct Node* prev;       // 指向前一个节点的指针
    struct Node* next;       // 指向后一个节点的指针
};

// 创建新节点
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}

// 在链表尾部插入新节点
void appendNode(struct Node** headRef, int data) {
    struct Node* newNode = createNode(data);
    if (*headRef == NULL) {
        *headRef = newNode;
        return;
    }
    struct Node* curr = *headRef;
    while (curr->next != NULL) {
        curr = curr->next;
    }
    curr->next = newNode;
    newNode->prev = curr;
}

// 在链表中删除指定节点
void deleteNode(struct Node** headRef, int data) {
    struct Node* curr = *headRef;
    struct Node* prev = NULL;
    while (curr != NULL && curr->data != data) {
        prev = curr;
        curr = curr->next;
    }
    if (curr == NULL) {
        printf("Node with data %d not found.\n", data);
        return;
    }
    if (prev == NULL) {
        *headRef = curr->next;
    } else {
        prev->next = curr->next;
    }
    if (curr->next != NULL) {
        curr->next->prev = prev;
    }
    free(curr);
}

// 打印链表中的所有节点数据
void printList(struct Node* node) {
    while (node != NULL) {
        printf("%d ", node->data);
        node = node->next;
    }
    printf("\n");
}

int main() {
    struct Node* head = NULL;  // 定义链表头指针
    appendNode(&head, 1);       // 在链表尾部插入节点1
    appendNode(&head, 2);       // 在链表尾部插入节点2
    appendNode(&head, 3);       // 在链表尾部插入节点3
    printList(head);            // 打印链表中的所有节点数据,输出应为:1 2 3 
    deleteNode(&head, 2);       // 删除节点2,输出应为:1 3 
    printList(head);            // 打印链表中的所有节点数据,输出应为:1 3 
    return 0;
}

3.2 循环链表

以下是C语言实现循环链表的示例代码:

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

// 定义链表节点结构体
struct Node {
    int data;               // 存储数据
    struct Node* next;       // 指向下一个节点的指针
};

// 创建新节点
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

// 在链表尾部插入新节点
void appendNode(struct Node** headRef, int data) {
    struct Node* newNode = createNode(data);
    if (*headRef == NULL) {
        *headRef = newNode;
        return;
    }
    struct Node* curr = *headRef;
    while (curr->next != NULL) {
        curr = curr->next;
    }
    curr->next = newNode;
    newNode->next = *headRef;
}

// 打印链表中的所有节点数据
void printList(struct Node* node) {
    while (node != NULL) {
        printf("%d ", node->data);
        node = node->next;
        if (node == NULL) {
            printf("\n");
        }
    }
}

int main() {
    struct Node* head = NULL;  // 定义链表头指针
    appendNode(&head, 1);       // 在链表尾部插入节点1
    appendNode(&head, 2);       // 在链表尾部插入节点2
    appendNode(&head, 3);       // 在链表尾部插入节点3
    printList(head);            // 打印链表中的所有节点数据,输出应为:1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
    return 0;
}

3.3 静态链表

静态链表是一种使用数组模拟链表的数据结构。它通过将数组中的每个元素拆分为两个部分,一个存储数据,一个存储指向下一个元素的索引,来实现链表的功能。

以下是一个简单的C语言实现静态链表的示例代码:

#include <stdio.h>
#define MAXSIZE 100

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

Node array[MAXSIZE];
int head = -1;
int tail = -1;

void init() {
    for (int i = 0; i < MAXSIZE; i++) {
        array[i].next = -1;
    }
}

void insert(int data) {
    Node* newNode = &array[tail];
    newNode->data = data;
    if (head == -1) {
        head = tail = 0;
    } else {
        tail = (tail + 1) % MAXSIZE;
    }
    newNode->next = head;
    head = (head + 1) % MAXSIZE;
}

int search(int data) {
    Node* curr = &array[head];
    while (curr != &array[head]) {
        if (curr->data == data) {
            return (curr - array) % MAXSIZE;
        } else {
            curr = &array[curr->next];
        }
    }
    return -1; // 数据不存在
}

void delete(int data) {
    Node* prev = &array[head];
    Node* curr = &array[head];
    while (curr != &array[head]) {
        if (curr->data == data) {
            prev->next = (curr->next + 1) % MAXSIZE;
            return;
        } else {
            prev = curr;
            curr = &array[curr->next];
        }
    }
    printf("Data not found\n"); // 数据不存在
}

五、顺序表和链表的比较

顺序表和链表是两种不同的线性数据结构,它们各有优缺点,适用于不同的应用场景。

他们在以下几个方面有较大的区别

  1. 内存空间:顺序表在内存中占据连续的空间,而链表则可以非连续。这使得顺序表在空间利用率上较优,因为它不需要为每个节点额外分配存储空间来存储指针。
  2. 插入和删除操作:在顺序表中,插入和删除操作需要移动元素来保持连续性,因此时间复杂度通常为O(n)。然而,在链表中,插入和删除操作仅需要更改指针,因此时间复杂度为O(1)。链表在插入和删除操作上更高效。
  3. 查找操作:在顺序表中,查找操作的时间复杂度通常为O(1),因为我们可以直接通过索引访问元素。而在链表中,查找操作需要从头部开始遍历,时间复杂度为O(n)。
  4. 动态增长:顺序表通常只能在固定大小的内存空间中操作,而链表可以通过动态分配内存来增长。这意味着链表可以更灵活地处理更多的数据。
  5. 适用场景:顺序表更适合于小规模数据,因为它在内存中占据连续空间,可以快速访问元素。而链表更适合于大规模数据,因为它可以动态增长并且插入和删除操作更高效。

其实顺序表和链表各有优缺点,选择使用哪种数据结构取决于具体的应用场景和需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1151447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Jetpack:023-Jetpack中的事件二

文章目录 1. 知识回顾2. 使用方法2.1 单击事件2.2 双击事件2.3 长按事件2.4 滑动事件 3. 示例代码4. 内容总结 我们在上一章回中介绍了 Jetpack中事件相关的内容&#xff0c;本章回中继续介绍这方面的内容。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧&#xff01;…

【Java数据结构重点知识】第一节:认识数据结构与算法、集合框架

一&#xff1a;数据结构与算法 1.数据结构 数据结构是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的集合 2.算法 算法就是定义良好的计算过程。他取一个或一组的值为输入&#xff0c;并产生一个或一组作为输出。简单来说就是一系列的…

Linux】centos安装配置及远程连接工具的使用

【Linux】centos安装配置及远程连接工具的使用 1.使用vmware创建虚拟机&#xff0c;因为过程比较简单就没有截图了&#xff0c;根据下面步骤来就行。2.网络配置3.MobaXterm连接CentOS1.new session2.点击ssh&#xff0c;输入虚拟机的IP地址即可 4.进行阿里云换源1.进入2.下载wg…

AMD:抢占AI芯片宝座

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;AMD受益于AI芯片的出口限制&#xff0c;使其能够获得更多的中国市场份额&#xff0c;并增强其在AI芯片市场的地位。 &#xff08;2&#xff09;AMD的处理器&#xff0c;特别是E…

ctfshow-web入门命令执行29-36

29 源代码给了禁用flag 使用tac、nl ?cecho nl f*; ?cecho tac f*; 30 多禁用了system和php 和上题区别不大&#xff0c;使用上一题命令就能解 ?cecho nl f*; ?cecho tac f*; 31 禁用了空格使用%09代替 ?cecho%09tac%09f*; 32 禁用了echo 使用php伪协议 ?cinclud…

从零开始学习搭建量化平台笔记

从零开始学习搭建量化平台笔记 本笔记由纯新手小白开发学习记录&#xff0c;欢迎大佬请教指点留言&#xff0c;有空的话还可以认识一下&#xff0c;来上海请您喝咖啡~~ 2023/10/30&#xff1a;上份工作辞职并休息了几个月后&#xff0c;打算开始找个关于量化投资相关的工作。面…

云原生之使用Docker部署slash书签共享平台

云原生之使用Docker部署slash书签共享平台 一、slash介绍1.1 slash简介1.2 slash特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载slash镜像五、部署slash书签共享平台5…

2模块和包的一些总结:导入库和模块

感觉这块好零散呀&#xff0c;应该怎么去写呢&#xff1f;截图吧 1、导入多个模块的方法 #导入多个包有几种办法 # 1.用点号 # 2.用逗号import p1.Tool import p1.sub_p.sub_xxxprint(p1.Tool.num) print(p1.sub_p.sub_xxx.num)import p1.Tool,p1.sub_p.sub_xxx print(p1.Tool…

基于纵横交叉算法的无人机航迹规划-附代码

基于纵横交叉算法的无人机航迹规划 文章目录 基于纵横交叉算法的无人机航迹规划1.纵横交叉搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用纵横交叉算法来优化无人机航迹规划。 …

【2023.10.30练习】C语言-循环右移字符

计算机能力挑战初赛2020.19题 题目描述&#xff1a; 现要对一个由字符a-z和A-Z组成的字符串进行解密&#xff0c;已知加密规则是&#xff1a; 字符串中所有字符分别在大写或小写的字母表中被循环左移5位(fGh-->aBc)&#xff0c; 输入&#xff1a;一个加密过的字符串&#…

【MySQL索引与优化篇】数据库优化及性能分析工具使用

数据库优化及性能分析工具使用 文章目录 数据库优化及性能分析工具使用1. 数据库服务器优化的步骤2. 查询系统性能参数3. 定位执行满的SQL&#xff1a;慢查询日志4. 查看SQL执行成本&#xff1a;show profile5. 分析查询工具&#xff1a;explain5.1 id5.2 type 6. explain进一步…

【AI视野·今日Sound 声学论文速览 第三十一期】Mon, 23 Oct 2023

AI视野今日CS.Sound 声学论文速览 Mon, 23 Oct 2023 Totally 9 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Two-Stage Triplet Loss Training with Curriculum Augmentation for Audio-Visual Retrieval Authors Donghuo Zeng, Kazushi Ikeda跨模态…

dp三步问题

三步问题 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 class Solution { public:int waysToStep(int n) {vector<int> dp(n1,1);if(n1) return 1;dp[1]1;dp[2]2;for(int i3; i<n1; i){dp[i] ((dp[i-1]dp[i-2])%1000000007dp[i-3])%100…

类变量/方法、main语法、代码块

一.类变量和方法 思维导图概览&#xff1a; 1.1类变量&#xff08;静态变量&#xff09; 1.什么叫做类变量/方法&#xff1f; ——给类中的成员属性或成员方法加上static关键字进行修饰&#xff0c;类变量/方法也叫做静态变量/方法&#xff0c;静态变量/方法被类的自身所有对…

C/C++ 飞翔的小鸟

载入问题&#xff1a; 解决之后效果&#xff1a; 放在main函数里面进行封装&#xff1a; 效果展示: 实现下坠 放进while&#xff08;1&#xff09;里面不断进入循环&#xff0c;每次进入循环&#xff0c;鸟的y坐标值就会发生变化&#xff0c;以此实现下下坠效果 效果展示&#…

取消Excel打开密码的两种方法

Excel设置了打开密码&#xff0c;想要取消打开密码是由两种方法的&#xff0c;今天分享这两种方法给大家。 想要取消密码是需要直到正确密码的&#xff0c;因为只有打开文件才能进行取消密码的操作 方法一&#xff1a; 是大家常见的取消方法&#xff0c;打开excel文件之后&a…

【python入门篇——7】循环控制(if、while、for、range)

目录 一、条件和 If 语句 1.Python 支持来自数学的常用逻辑条件 2.缩进 3.Elif 4.Else 5.pass语句 二、While 循环 1.else 语句 三、for 循环 1.循环遍历字符串 2.Else 3.嵌套循环 4.pass 语句 四、range() 函数 1.range() 函数 2.起始参数 3.递增序列 一、条…

Elasticsearch-8.10.4安装

1.官网下载 https://www.elastic.co/cn/downloads/elasticsearch#ga-release 下载的是Linux x86_64 包名为elasticsearch-8.10.4-linux-x86_64.tar.gz 2.服务器解压 tar -xvf elasticsearch-8.10.4-linux-x86_64.tar.gz 3.修改配置 编辑配置文件config/elasticsearch.y…

ChineseChess.2023.10.30.01

中国象棋模拟器&#xff1a;黑子一步即杀的棋&#xff0c;红要解棋&#xff0c;不断将军慢慢把老帅边上棋子走开才有可能离开底线 【可以不上“士”的&#xff0c;将上去完事。】 不上【士】就没了&#xff0c;这局不知道咋破&#xff0c;哈哈 修改缺陷吧 中国象棋残局模拟器C…

0-1背包问题【穷举法+二维dp数组】

问题描述&#xff1a; 使用穷举法解决0/1背包问题。问题描述&#xff1a;给定n个重量为{w1, w2, … ,wn}、价值为{v1, v2, … ,vn} 的物品和一个容量为C的背包&#xff0c;求这些物品中的一个最有价值的子集&#xff0c;且要能够装到背包中。 穷举法&#xff1a;每件物品装还是…