C++实现单链表【每一步详细深入讲解,代码清晰、简单、易懂】

这篇具有很好参考价值的文章主要介绍了C++实现单链表【每一步详细深入讲解,代码清晰、简单、易懂】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、单链表的结构(以数据为不重复整数为例)

0、overview

链表元素由数据和指针构成,因此链表的节点可以由一个包含这两种元素的结构体代表:

// 单链表节点
struct SingleListNode {
    int data;
    SingleListNode* next;
    SingleListNode(int val, SingleListNode* ptr = nullptr);
};

链表包含插入、删除操作,而插入、删除又必须先查找元素是否存在,因此查找方法也必不可少。

//单链表
class SingleList {
public:
    SingleListNode* dummy;
    SingleList();
    ~SingleList();
public:
    void add(int);
    void remove(int);
    SingleListNode* find(int);
};

1、插入操作

c++单链表,数据结构与算法,c++,链表,数据结构

例如:我们需要在伪头节点(不包含数据)和含有1的节点之间插入一个节点,发现,只需要改变dummy的指针和需要插入的指针即可。那么很容易想到下面的步骤:

  • 将dummy的指针指向要添加的节点

  • 将要添加节点的指针指向之前的下一个节点

c++单链表,数据结构与算法,c++,链表,数据结构 c++单链表,数据结构与算法,c++,链表,数据结构

这样我们就添加了一个节点。

但是发现了一个问题:因为单向链表只能通过前一个的指针寻找下一个元素,如果该指针指向改变了,我们就永远也找不到下一个元素了,因此,上面步骤2是错误的。那我们应该如何添加元素?

正确的添加顺序

  • 先将要添加的元素的指针指向dummy节点的后一个节点

  • 再将dummy节点的指针指向要添加的节点

c++单链表,数据结构与算法,c++,链表,数据结构 c++单链表,数据结构与算法,c++,链表,数据结构

插入的情况讨论

  • 插入时这里为了方便统一在dummy节点之后插入,也即每次插入一个新元素都在开头插入

图例:

初始 插入2 插入3
c++单链表,数据结构与算法,c++,链表,数据结构 c++单链表,数据结构与算法,c++,链表,数据结构 c++单链表,数据结构与算法,c++,链表,数据结构

插入的代码逻辑

  • 读取find函数返回值判读元素是否已存在
  • 存在则输出插入失败,并返回
  • 不存在则执行插入操作
void SingleList::add(int val) {
    auto ptr = find(val);
    if (ptr) {
        std::cerr << val << " exists, add failed!" << std::endl;
        return;
    } else {
        auto temp = new SingleListNode(val);
        temp->next = dummy->next;
        dummy->next = temp;
    }
}

2、删除操作

相比于插入,删除要简单一些

c++单链表,数据结构与算法,c++,链表,数据结构

例如我们要删除上图包含2的节点,只需执行下面的步骤

  • 将需要删除的节点的前一个节点的指针指向需要删除的节点的下一个节点,也即dummy的节点指向1
  • 再将需要删除的节点释放掉
c++单链表,数据结构与算法,c++,链表,数据结构 c++单链表,数据结构与算法,c++,链表,数据结构

删除的代码逻辑

  • 读取find函数的返回值判断元素是否存在
  • 不存在则输出错误提示并返回
  • 存在则执行删除操操作
void SingleList::remove(int val) {
    auto ptr = find(val);
    if (ptr == nullptr) {
        std::cerr << val << " does not exist, remove failed!" << std::endl;
        return;
    } else {
        auto temp = ptr->next;
        ptr->next = ptr->next->next;
        delete temp;
    }
}

3、查找操作

通过对插入和删除的讨论,我们了解到,插入调用find函数时,并不需要操作其返回值,所以,返回值具体是该元素的节点还是其前面的节点并没有影响;但是,由于单链表只能通过前一个的指针寻找下一个元素,因此,删除操作时我们必须得到需要删除节点的前一个节点,因此find的返回值必须设为需要寻找的节点的前一个节点,而第一个节点的前一个节点是dummy,这也是我们需要dummy这个伪头节点的原因之一。

查找代码的逻辑

  • 从第一个含有元素的节点开始寻找,依次向后
  • 如果找到该节点,则返回之
  • 如果链表遍历完也没有找到,就返回nullptr
SingleListNode* SingleList::find(int val) {
    auto prev = dummy;
    auto temp = dummy->next;
    while (temp) {
        if (temp->data == val)
            return prev;
        prev = prev->next;
        temp = temp->next;
    }
    return nullptr;
}

循环链表的查找

当单链表为循环链表时,最后一个元素的next指针不是指向nullptr,而是dummy节点,因此,查找代码有一些小变化,同时构造、析构以及测试代码都围绕dummy的next指针有相应的小变化(详见代码)

SingleListNode* SingleList::find(int val) {
    auto prev = dummy;
    auto temp = dummy->next;
    while (temp != dummy) {	//循环条件变为temp不等于dummy
        if (temp->data == val)
            return prev;
        prev = prev->next;
        temp = temp->next;
    }
    return nullptr;
}

4、完整代码

// SingleList.h 单链表类定义

// 单链表节点
struct SingleListNode {
    int data;
    SingleListNode* next;
    SingleListNode(int val, SingleListNode* ptr = nullptr);
};

//单链表
class SingleList {
public:
    SingleListNode* dummy;
    SingleList();
    ~SingleList();
public:
    void add(int);
    void remove(int);
    SingleListNode* find(int);
};
// SingleList.cpp 单链表类的函数实现及静态成员定义
#include <iostream>
#include "SingleList.h"

SingleListNode::SingleListNode(int val, SingleListNode* ptr)
    : data(val), next(ptr) { }

SingleList::SingleList() : dummy(new SingleListNode(-1)) { }

SingleList::~SingleList() {
    auto ptr = dummy->next;
    while (ptr) {
        auto temp = ptr;
        ptr = ptr->next;
        delete temp;
        std::cout << "Element deleted!" << std::endl;
    }
    delete dummy;
    std::cout << "SingleList Destroyed!" << std::endl;
}

void SingleList::add(int val) {
    auto ptr = find(val);
    if (ptr) {
        std::cerr << val << " exists, add failed!" << std::endl;
        return;
    } else {
        auto temp = new SingleListNode(val);
        temp->next = dummy->next;
        dummy->next = temp;
    }
}

void SingleList::remove(int val) {
    auto ptr = find(val);
    if (ptr == nullptr) {
        std::cerr << val << " does not exist, remove failed!" << std::endl;
        return;
    } else {
        auto temp = ptr->next;
        ptr->next = ptr->next->next;
        delete temp;
    }
}

SingleListNode* SingleList::find(int val) {
    auto prev = dummy;
    auto temp = dummy->next;
    while (temp) {
        if (temp->data == val)
            return prev;
        prev = prev->next;
        temp = temp->next;
    }
    return nullptr;
}
// SingleListTest.cpp 对该单链表的测试代码

#include <iostream>
#include "SingleList.h"

int main(void) {
	SingleList list;

	list.add(0);
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(4);	// 添加已存在元素
	list.remove(0); // 末尾元素
	list.remove(2); // 中间元素
	list.remove(4); // 开头元素
	list.remove(4); // 删除不存在元素
	for (auto temp = list.dummy->next; temp; temp = temp->next)
		std::cout << temp->data << " ";
	std::cout << std::endl;
	/*期望的输出
	4 exists, add failed!
	4 does not exist, remove failed!
	3 1
	Element deleted!
	Element deleted!
	SingleList Destroyed!
	*/
	return 0;
}

5、循环链表的代码

链表的结构仅有查找函数有变化同时,测试的代码也有相应的变化文章来源地址https://www.toymoban.com/news/detail-716894.html

// SingleList.h 单循环链表类定义

// 单循环链表节点
struct SingleListNode {
    int data;
    SingleListNode* next;
    SingleListNode(int val, SingleListNode* ptr = nullptr);
};

//单循环链表
class SingleList {
public:
    SingleListNode* dummy;
    SingleList();
    ~SingleList();
public:
    void add(int);
    void remove(int);
    SingleListNode* find(int);
};
// SingleList.cpp 单循环链表类的函数实现及静态成员定义
#include <iostream>
#include "SingleList.h"

SingleListNode::SingleListNode(int val, SingleListNode* ptr)
    : data(val), next(ptr) { }

SingleList::SingleList() 
    : dummy(new SingleListNode(-1)) { // 构造dummy时,需要把next指向本身
    dummy->next = dummy; 
}

SingleList::~SingleList() {
    auto ptr = dummy->next;
    while (ptr != dummy) { // 析构条件变为ptr != dummy
        auto temp = ptr;
        ptr = ptr->next;
        delete temp;
        std::cout << "Element deleted!" << std::endl;
    }
    delete dummy;
    std::cout << "SingleList Destroyed!" << std::endl;
}

void SingleList::add(int val) {
    auto ptr = find(val);
    if (ptr) {
        std::cerr << val << " exists, add failed!" << std::endl;
        return;
    } else {
        auto temp = new SingleListNode(val);
        temp->next = dummy->next;
        dummy->next = temp;
    }
}

void SingleList::remove(int val) {
    auto ptr = find(val);
    if (ptr == nullptr) {
        std::cerr << val << " does not exist, remove failed!" << std::endl;
        return;
    } else {
        auto temp = ptr->next;
        ptr->next = ptr->next->next;
        delete temp;
    }
}

SingleListNode* SingleList::find(int val) {
    auto prev = dummy;
    auto temp = dummy->next;
    while (temp != dummy) { //循环条件变为temp不等于dummy
        if (temp->data == val)
            return prev;
        prev = prev->next;
        temp = temp->next;
    }
    return nullptr;
}
// SingleListTest.cpp 对该单循环链表的测试代码

#include <iostream>
#include "SingleList.h"

int main(void) {
	SingleList list;

	list.add(0);
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(4);	// 添加已存在元素
	list.remove(0); // 末尾元素
	list.remove(2); // 中间元素
	list.remove(4); // 开头元素
	list.remove(4); // 删除不存在元素
	for (auto temp = list.dummy->next; temp != list.dummy; temp = temp->next) // 循环链表循环条件为temp != list.dummy
		std::cout << temp->data << " ";
	std::cout << std::endl;
	/*期望的输出
	4 exists, add failed!
	4 does not exist, remove failed!
	3 1
	Element deleted!
	Element deleted!
	SingleList Destroyed!
	*/
	return 0;
}

到了这里,关于C++实现单链表【每一步详细深入讲解,代码清晰、简单、易懂】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【计算机基础】Git从安装到使用,详细每一步!扩展Github\Gitlab

    📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍 收藏 ⭐不迷路🙉 📢:内容若有错误,敬请留言 📝指正!原创文,转载请注明出处 📝 诞生 :2005年

    2024年02月09日
    浏览(37)
  • Centos7下的DNS服务器部署(每一步图文结合超详细,适用于初学者)

    关于DNS服务,网上都有很多很详细很专业的讲解,但是对于大部分初学者可能看的比较懵懂,用白话来说就是起初人们因为对大量用于访问服务器的IP地址难以记住,所以就逐渐出现了域名的形式(诸如:www.baidu.com 之类的),但是计算机本身只能识别出像192.168.10.112之类的

    2024年02月07日
    浏览(55)
  • 【数据结构】单链表 | 详细讲解

    无须为了表示中间的元素之间的逻辑关系而增加额外的存储空间; 因为以数组形式存储,可以快速地存取表中任一位置的元素。 插入和删除操作需要移动大量元素,时间复杂度为O(N); 当线性表长度变化较大时,难以确定存储空间的容量; 造成存储空间的“碎片”。 其实顺

    2024年02月05日
    浏览(37)
  • 高精度除法【c++实现】超详细讲解

    高精度算法分为两种,高精除以低精和高精除以高精。不要看都是除法,就认为原理类似,其实是有很大差距的。让我们一起来学习吧! 有句话说在前面,如果除数等于0,就不要算了,不成立。( 如果你忘了这个知识,小学数学老师饶不了你 ) 高精度除低精度,原理是模

    2024年02月13日
    浏览(22)
  • UniApp 中的路由守卫与拦截器:守护应用的每一步

    正文: 路由守卫和拦截器在前端开发中扮演着重要的角色,它们可以用来控制页面访问权限、全局请求拦截等。在 UniApp 中,路由守卫和拦截器同样具有强大的功能,能够保护应用的安全和稳定性。本文将深入探讨 UniApp 中的路由守卫和拦截器,带你领略它们的魔法与神奇。

    2024年04月25日
    浏览(27)
  • 二分查找算法讲解及其C++代码实现

    二分查找算法是一种常用的查找算法,也被称为折半查找。它可以在有序的数组或列表中快速查找需要的元素。 算法描述: 首先确定数组的中间位置mid=(left+right)/2; 然后将要查找的值key与中间位置的值进行比较; 如果key等于中间位置的值,则查找成功,返回mid; 如果key小

    2024年02月01日
    浏览(34)
  • 网络流的C++代码实现与过程讲解

    网络流是一种非常重要的图论算法,它在许多实际问题中得到广泛应用。本文将介绍网络流算法的C++代码实现与过程讲解。 网络流算法是通过将图中的边看作流量通道,将图的点看作流量的起点或终点,来求解图中的最大或最小流量的问题。它是一种非常重要的最优化算法,

    2023年04月21日
    浏览(23)
  • 如何仿写简易tomcat 实现思路+代码详细讲解

    仿写之前,我们要搞清楚都要用到哪些技术 自定义注解,比如Tomcat使用的是@Servlet,我们可以定义一个自己的@MyServlet 构造请求体和返回体,比如tomcat使用HttpRequest,我们可以自己定义myHttpRequest java去遍历一个指定目录,然后获取到.java文件,再获取到带有@MyServlet注解的类 然

    2024年02月12日
    浏览(24)
  • [C++]:万字超详细讲解多态以及多态的实现原理(面试的必考的c++考点)

    文章目录 前言 一、多态的定义及实现 1.多态的构成条件 2.c++11的override和final 3.重载,重写,重定义的比较 4.抽象类 5.多态的原理 6.多继承中的虚函数表 7.动态绑定和静态绑定 总结 多态的概念: 多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的

    2023年04月22日
    浏览(49)
  • 基于SpringBoot实现文件上传和下载(详细讲解And附完整代码)

    目录 一、基于SpringBoot实现文件上传和下载基于理论 二、详细操作步骤 文件上传步骤: 文件下载步骤: 三、前后端交互原理解释  四、小结  博主介绍:✌专注于前后端领域开发的优质创作者、秉着互联网精神开源贡献精神,答疑解惑、坚持优质作品共享。本人是掘金/腾讯

    2024年04月11日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包