C++的数据结构(二)

一、链表的基本概念

        链表(Linked List)是一种物理存储单元上非连续的、非顺序的线性数据结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列节点(链表中每一个元素称为节点)组成,节点可以在运行时动态生成。每个节点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针域。相比于线性数组,链表的好处在于不需要事先分配固定大小的存储空间,并且在插入和删除数据时不需要移动其他元素。

        最适合比喻链表的就是火车了,如下图所示。

二、链表的类型

        C++中常见的链表类型包括单向链表、双向链表和循环链表。

        1. 单向链表(Singly Linked List)

                单向链表是最简单的链表结构,每个节点只包含一个指向下一个节点的指针。单向链表只能从头节点开始顺序访问到尾节点。

                示例:在链表头部插入节点,遍历链表并打印节点的数据,代码如下。

#include <iostream>
// 定义单向链表的节点结构体
struct Node {int data;           // 数据域Node* next;        // 指针域,指向下一个节点// 构造函数,用于初始化节点Node(int data) : data(data), next(nullptr) {}
};
// 单向链表类
class SinglyLinkedList {
private:Node* head; // 头节点指针public:SinglyLinkedList() : head(nullptr) {}// 在链表头部插入节点void insertAtBeginning(int data) {Node* newNode = new Node(data);newNode->next = head;head = newNode;}// 遍历链表并打印节点的数据void printList() {Node* temp = head;while (temp != nullptr) {std::cout << temp->data << " ";temp = temp->next;}std::cout << std::endl;}// 销毁链表~SinglyLinkedList() {Node* temp;while (head != nullptr) {temp = head;head = head->next;delete temp;}}
};
int main() {SinglyLinkedList list;list.insertAtBeginning(5);list.insertAtBeginning(3);list.insertAtBeginning(1);list.printList();return 0;
}

                结果如下所示:

        2. 双向链表(Doubly Linked List)

                双向链表包含两个指针,一个指向前一个节点,另一个指向下一个节点。这使得双向链表可以从任何一个节点向前或向后遍历。

                 示例:在链表头部插入节点,遍历链表并打印节点的数据,代码如下。

#include <iostream>
// 定义双向链表的节点结构体
struct Node {int data;           // 数据域Node* prev;         // 指针域,指向前一个节点Node* next;         // 指针域,指向下一个节点// 构造函数,用于初始化节点Node(int data) : data(data), prev(nullptr), next(nullptr) {}
};
// 双向链表类
class DoublyLinkedList {
private:Node* head; // 头节点指针
public:DoublyLinkedList() : head(nullptr) {}// 在链表头部插入节点void insertAtBeginning(int data) {Node* newNode = new Node(data);if (head != nullptr) {head->prev = newNode;}newNode->next = head;head = newNode;}// 遍历链表并打印节点的数据void printList() {Node* temp = head;while (temp != nullptr) {std::cout << temp->data << " ";temp = temp->next;}std::cout << std::endl;}// 销毁链表~DoublyLinkedList() {Node* temp;while (head != nullptr) {temp = head;head = head->next;if (temp->next != nullptr) {temp->next->prev = temp->prev;}delete temp;}}
};
int main() {DoublyLinkedList list;list.insertAtBeginning(5);list.insertAtBeginning(3);list.insertAtBeginning(1);list.printList(); return 0;
}

                在上述2个示例中,我们定义了节点结构体,并为单向链表和双向链表创建了类。这些类包含了插入节点到链表头部、遍历链表并打印节点数据以及销毁链表的方法。对于双向链表,我们还需要处理`prev`指针以确保链表结构的正确性。

                请注意,以上代码只是一个简单的示例,并没有处理错误情况和异常。在实际应用中,你可能需要添加额外的错误检查和边界情况处理,例如当插入节点失败时返回错误码或抛出异常。此外,对于大型数据集,你可能还需要考虑性能优化和内存管理。

        3. 循环链表(Circular Linked List)

                循环链表与单向链表和双向链表的主要区别在于,循环链表的尾节点指向头节点,形成一个环状结构。这使得循环链表可以从任何节点开始遍历整个链表。循环链表是链表结构的一个变种,它的特点是链表的最后一个节点指向链表的第一个节点,形成一个环状结构。在实际开发中,循环链表常用于实现某些特殊的数据结构,如循环队列、循环双端队列等。

                我们举一个实例:假设我们正在开发一个音乐播放器应用,需要实现一个音乐播放列表,这个播放列表支持添加、删除、循环播放音乐的功能。循环链表非常适合用来实现这样的播放列表。

                首先,我们定义一个循环链表的节点类,每个节点包含一个音乐信息和一个指向下一个节点的指针。代码如下。

#include <iostream>
#include <string>
class MusicNode {
public:std::string title;    // 音乐标题MusicNode* next;       // 指向下一个节点的指针MusicNode(const std::string& title) : title(title), next(nullptr) {}
};

                接着,我们定义一个循环链表类,包含插入、删除、遍历等基本操作。代码如下。

class CircularMusicPlaylist {
private:MusicNode* head;       // 头节点int size;               // 链表大小public:CircularMusicPlaylist() : head(nullptr), size(0) {}~CircularMusicPlaylist() {clear();}// 插入音乐到播放列表void addMusic(const std::string& title) {MusicNode* newNode = new MusicNode(title);if (isEmpty()) {head = newNode;newNode->next = head; // 构成循环} else {MusicNode* current = head;while (current->next != head) {current = current->next;}current->next = newNode;newNode->next = head; // 新节点指向头节点,形成闭环}size++;}// 从播放列表中删除音乐void removeMusic(const std::string& title) {if (isEmpty()) {return;}MusicNode* prev = nullptr;MusicNode* current = head;do {if (current->title == title) {if (prev == nullptr) { // 删除的是头节点while (current->next != head) {current = current->next;}head = head->next;delete current;head->next = head; // 更新头节点的指针} else {prev->next = current->next;if (current == head) {head = prev->next;}delete current;}size--;return;}prev = current;current = current->next;} while (current != head); // 遍历整个链表std::cout << "Music not found in the playlist." << std::endl;}// 遍历播放列表void play() {if (isEmpty()) {std::cout << "Playlist is empty." << std::endl;return;}MusicNode* current = head;do {std::cout << "Playing: " << current->title << std::endl;current = current->next;} while (current != head); // 循环播放}// 判断播放列表是否为空bool isEmpty() const {return head == nullptr;}// 清除播放列表void clear() {MusicNode* current = head;while (current != nullptr) {MusicNode* toDelete = current;current = current->next;delete toDelete;}head = nullptr;size = 0;}// 获取播放列表大小int getSize() const {return size;}
};

                现在,我们可以使用这个循环链表播放列表了。代码如下。

int main() {CircularMusicPlaylist playlist;// 添加音乐到播放列表playlist.addMusic("Song 1");playlist.addMusic("Song 2");playlist.addMusic("Song 3");// 打印播放列表大小std::cout << "Playlist size: " << playlist.getSize() << std::endl;// 播放整个播放列表playlist.play();// 删除一个歌曲playlist.removeMusic("Song 2");// 再次打印播放列表大小std::cout << "Playlist size after removal: " << playlist.getSize() << std::endl;// 再次播放整个播放列表playlist.play();// 清除播放列表playlist.clear();// 检查播放列表是否为空if (playlist.isEmpty()) {std::cout << "Playlist is now empty." << std::endl;}return 0;
}

                上述代码中,我们首先创建了一个`CircularMusicPlaylist`对象,然后向其中添加了几首歌曲。我们打印了播放列表的大小,并播放了整个播放列表。之后,我们删除了一个歌曲,并再次打印了播放列表的大小。最后,我们清除了播放列表,并检查它是否为空。

                通过实现一个音乐播放列表,我们展示了循环链表的基本操作,包括添加元素、删除元素、遍历元素以及检查链表是否为空。在实际应用中,循环链表还可以用于实现各种需要循环访问数据结构的场景,如循环队列、环形缓冲区等。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

py黑帽子学习笔记_网络编程工具

tcp客户端 socket.AF_INET表示使用标准IPV4地址和主机名 SOCK_STREAM表示这是一个TCP客户端 udp客户端 udp无需连接&#xff0c;因此不需要client.connect这种代码 socket.SOCK_DGRAM是udp的 tcp服务端 server.listen(5)表示设置最大连接数为5 发现kill server后端口仍占用…

牛客NC404 最接近的K个元素【中等 二分查找+双指针 Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/b4d7edc45759453e9bc8ab71f0888e0f 知识点 二分查找&#xff1b;找到第一个大于等于x的数的位置idx;然后从idx开始往两边扩展Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、…

Shell编程规范与变量

Shell 什么是Shell&#xff1f; 就是与内核沟通的界面、应用程序等等。比如你要播放音乐&#xff0c;你的计算机通过你在Shell输入的打开音乐的命令&#xff0c;Shell在告诉操作系统的内核用户希望打开音乐&#xff0c;内核在通过cpu调度、内存管理、磁盘输入输出等工作&#…

JAVA课程设计

一&#xff1a;Java连接mysql数据库 1.1点击进入mysql jar包下载官网 MySQL :: MySQL Community Downloads 将下载好的压缩包进行解压 解压之后下图就是连接数据库所用到的jar包&#xff1a; 将jar包复制到IDEA所用的项目下&#xff0c;放置jar包的目录为lib&#xff0c;需要…

NSS刷题

[SWPUCTF 2021 新生赛]jicao 类型&#xff1a;PHP、代码审计、RCE 主要知识点&#xff1a;json_decode()函数 json_decode()&#xff1a;对JSON字符串解码&#xff0c;转换为php变量 用法&#xff1a; <?php $json {"ctf":"web","question"…

安装Nox夜神模拟器关闭了HyperV后Docker运行不了怎么办?

1.背景 为了模拟真机&#xff0c;尝试安装了Nox夜神模拟器&#xff0c; 安装过程要求关闭Hyper-V。当时只是在程序安装卸载中关闭了系统服务。以为到时勾选上就好了。操作路径&#xff1a;控制面板\所有控制面板项\程序和功能\启用或关闭Windows功能\Hyper-V。 后来卸载掉了夜神…

FreeRTOS的列表和列表项 list.c文件详解

列表、列表项的定义以及初始化 列表相当于链表&#xff0c;列表项相当于节点&#xff0c;FreeRTOS中的列表相当于一个双向环形链表。 列表使用指针指向列表项。一个列表&#xff08;list&#xff09;下面可能有很多个列表项&#xff08;list item&#xff09;&#xff0c;每个…

vivado仿真readmemb函数相对路径

目前常用的vivado工程的结构如下所示 prj-name|-xxx|-prj.sim|-sim_1|-behav|-modelsim|-tb_prj.do|-xsim|-prj.srcs|-sim_1|-new|-tb_prj.v|-tb_prj_mem.txt一般来说我们创建的testbench文件和新建的txt文件都会放在srcs->sim_1->new这个路径下面&#xff0c;但是我们在…

【PHP【实战版】系统性学习】——登录注册页面的教程,让编写PHP注册变成一个简单的事情

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

最美博客POETIZE个人博客系统源码

源码说明&#xff1a; POETIZE个人博客系统源码 | 最美博客 这是一个基于SpringBoot、Vue2和Vue3的开源项目&#xff0c;支持移动端自适应&#xff0c;并具备完善的前台和后台管理功能。 网站分为两个模块&#xff1a; 1. 博客系统&#xff1a;包括文章、表白墙、图片墙、收…

SpringBoot实现图片验证码

引入依赖 <dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId><version>1.6.2</version> </dependency>代码实现 package com.qiangesoft.captcha.controller;import com.wf.captcha.*…

【Linux】基础命令:进程、网络

systemctl命令 控制内置服务 systemctl start | stop | status | enable | disable 服务名 start | stop开启关闭&#xff0c;status状态&#xff0c;enable | disable开启关闭开机自启 date命令 查看系统时间 date [-d] [格式化字符串] date -d “1 day” %Y-%m-%d 修改时区…

Stable Diffusion:AI绘画的新纪元

摘要&#xff1a; Stable Diffusion&#xff08;SD&#xff09;作为AI绘画领域的新星&#xff0c;以其开源免费、强大的生成能力和高度的自定义性&#xff0c;正在引领一场艺术与技术的革命。本文旨在为读者提供Stable Diffusion的全面介绍&#xff0c;包括其原理、核心组件、安…

链表的经典面试题(数据结构详解)+顺序表和链表之间区别+计算机存储体系

前言 首先这里已经正式步入数据结构的知识&#xff0c;之前我们已经讲解了链表的使用&#xff0c;接下来我们需要的就是大量的练习&#xff0c;熟练掌握数据结构。下面的题型我们选择的都是链表的经典题型&#xff0c;面试题型&#xff0c;包含快慢指针&#xff0c;数形结合&am…

【qt】设计器实现界面

设计器实现界面 一.总体思路二.具体操作1.创建项目2.粗略拖放3.水平布局4.垂直布局5.修改名字6.转到槽7.实现槽函数 一.总体思路 创建项目粗略拖放水平布局垂直布局修改名称转到槽实现槽函数 二.具体操作 1.创建项目 这次咱们一定要勾选Generate form哦。 因为我们要使用设…

R语言数据探索与分析-碳排放分析预测

# 安装和加载需要的包 install.packages("readxl") install.packages("forecast") install.packages("ggplot2") library(readxl) library(forecast) library(ggplot2)# 数据加载和预处理 data <- read_excel("全年数据.xlsx") co…

感知机和神经网络

引入 什么是神经网络&#xff1f; 我们今天学习的神经网络&#xff0c;不是人或动物的神经网络&#xff0c;但是又是模仿人和动物的神经网络而定制的神经系统&#xff0c;特别是大脑和神经中枢&#xff0c;定制的系统是一种数学模型或计算机模型&#xff0c;神经网络由大量的人…

FANUC机器人工具坐标偏移的用法

一、工具坐标偏移的使用场景 在机器人位置不改变的情况下&#xff0c;工业机器人使用默认工具坐标系示教的一系列运动点位&#xff0c;要保持原本点位位置不变的情况下&#xff0c;改变机器人工具坐标的参数&#xff0c;就要用到机器人坐标转化的功能。在FANUC机器人上体现为机…

通过mvn archetype 创建一个spring boot start 工程

mvn archetype https://maven.apache.org/archetype/index.html 遇到的问题 对于想自定义一个spring-boot-start的同学,比如 Springboot自定义Starter启动器 整个过程很繁琐。 定义属性开关增加 spring boot test start插件定义自动装载 spring.factories or org.springfra…

关于一致性,你该知道的事儿(上)

关于一致性&#xff0c;你该知道的事儿&#xff08;上&#xff09; 前言一、缓存一致性二、内存模型一致性三、事务一致性四、分布式事务一致性4.1 分布式系统的一些挑战4.2 关于副本的一些概念4.3 分布式事务之共识问题4. 3.1 PC(two-phase commit, 2PC)4.3.2 Raft 三、后记参…