双向链表的增删改查C++完整实现

2023-05-16


tags: C++ DSA LinkedList

写在前面

写一下双向链表的增删改查, 用C++实现.

完整代码可以看我的GitHub;

节点类-链表类

节点

class ListNode {
public:
    int val;
    ListNode* prev; // 前驱结点
    ListNode* next; // 后继结点
    ListNode() : ListNode(0, nullptr, nullptr) {}
    ListNode(int x) : ListNode(x, nullptr, nullptr) {}
    ListNode(int x, ListNode* _next) : ListNode(x, nullptr, _next) {}

    // 委托构造
    ListNode(int x, ListNode* _prev, ListNode* _next)
        : val(x), prev(_prev), next(_next) {}
};

链表

class DoubleLinkedList {
public:
    DoubleLinkedList() : head(nullptr) {}
    DoubleLinkedList(int head_val) : head(new ListNode(head_val)) {}
    DoubleLinkedList(int head_val, vector<int> rest);
    DoubleLinkedList(vector<int> nodes);
    ~DoubleLinkedList();

    void print();
    ListNode* first();
    ListNode* last();
    int len();

    // visit by pos
    /* int& operator[](int pos); */
    ListNode* operator[](int pos);
    ListNode* visit(int pos);
    ListNode* rvisit(int pos);

    // insert by pos
    void insert(int pos, int val);
    void append(int val);
    void add2head(int val);

    // delete by pos
    void pop(int pos);
    void pop_back();
    void pop_front();
    // delete by value
    void remove(int val, int cnt = 1);           // delete val cnt times
    void modify(int val, int node, int cnt = 1); // modify val->node cnt times
    int find(int node);                          // not found:-1
    int rfind(int node);                         // not found:-1


private:
    ListNode* head;
};

这里实现与之前写的单链表类似, 基本的增删改查肯定要有, 然后还有一个打印函数, (不然用重载的operator<<访问私有成员头结点不太好)

然后对于删除给出了基于位置的和基于值的两种实现, 直接调用已有的成员函数即可, 减少代码量.

然后是一个从右向左查找的rfind函数(事实上比较鸡肋, 需要先遍历到最后一个节点然后开始找).

还有一个重载的operator[], 以及visit函数, 用来基于位置找节点(指针).

一些辅助函数

双向链表格式化输出

ostream& operator<<(ostream& os, ListNode* lp) {
    if (lp == nullptr) return os << "∅" << endl;
    os << "∅ <== ";
    ListNode* cur = lp;
    while (cur != nullptr) {
        os << cur->val << (cur->next ? " <=> " : " ==> ");
        cur = cur->next;
    }
    os << "∅";
    return os << endl;
}

采用连字字体看起来就很舒服.

void DoubleLinkedList::print() { cout << head; }

双向链表长度

int DoubleLinkedList::len() {
    int size = 0;
    auto cur = head;
    while (cur) ++size, cur = cur->next;
    return size;
}

头尾结点

ListNode* DoubleLinkedList::first() { return head; }

ListNode* DoubleLinkedList::last() {
    auto cur = head;
    while (cur->next) cur = cur->next;
    return cur;
}

遍历(找节点指针)


ListNode* DoubleLinkedList::operator[](int pos) {
    if (head == nullptr) {
        cerr << "Attempt to get value from a NULL DoubleLinkedList\n";
        exit(1);
    }
    if (pos > len() - 1 || pos < 0) {
        cerr << "Wrong pos!\n";
        exit(1);
    }
    auto cur = head;
    while (pos--) cur = cur->next;
    return cur;
}

ListNode* DoubleLinkedList::visit(int pos) { return this->operator[](pos); }

ListNode* DoubleLinkedList::rvisit(int rpos) {
    // 从右往左遍历找目标位置
    if (head == nullptr) {
        cerr << "Attempt to get value from a NULL DoubleLinkedList\n";
        exit(1);
    }
    if (rpos > len() - 1 || rpos < 0) {
        cerr << "Wrong rpos!\n";
        exit(1);
    }
    auto cur = visit(len() - 1);
    while (rpos--) cur = cur->prev;
    return cur;
}

构造函数/析构函数

这里和单链表实现一样, 给出了三个构造函数, 一个通过初值列实现, 另外两个直接读取vector, 还是比较方便的.


DoubleLinkedList::DoubleLinkedList(int head_val, vector<int> rest) {
    head = new ListNode(head_val);
    ListNode *cur = head, *nxt = nullptr;
    for (auto i : rest) {
        nxt = new ListNode(i);
        cur->next = nxt;
        nxt->prev = cur;
        cur = cur->next;
    }
}

DoubleLinkedList::DoubleLinkedList(vector<int> nodes) {
    if (nodes.empty()) {
        cout << "Empty Data Source!\n";
        return;
    }
    head = new ListNode(nodes[0]);
    auto cur = head;
    for (auto i = nodes.begin() + 1; i < nodes.end(); i++) {
        auto nxt = new ListNode(*i);
        cur->next = nxt;
        nxt->prev = cur;
        cur = cur->next;
    }
}

DoubleLinkedList::~DoubleLinkedList() {
    ListNode* cur;
    while (head->next) {
        cur = head->next;
        cout << head->val << " ";
        delete head;
        head = cur;
    }
    cout << head->val << " ";
    delete head;
    cout << "deleted...\n";
}

对于析构函数, 没什么好说的, 跟单链表一样, 主要说一下构造函数, 由于是双向链表, 那么就一定要注意prev指针.

一般来说, 每次添加一个新节点, 都要链接四根指针, 分别是前驱节点的next, 当前节点的prev和next, 后继结点的prev, 缺一不可.

添加节点

void DoubleLinkedList::insert(int pos, int val) {
    if (pos >= len()) {
        auto cur = last(), nxt = new ListNode(val);
        cur->next = nxt;
        nxt->prev = cur;
    } else if (pos <= 0) {
        auto cur = head;
        head = new ListNode(val);
        head->next = cur;
        cur->prev = head;
    } else { // 中间位置
        auto pre = head;
        while (--pos) pre = pre->next;

        // pre为待插入位置的前一个节点
        auto cur = pre->next;
        pre->next = new ListNode(val, pre, cur);
    }
}

void DoubleLinkedList::add2head(int val) { insert(0, val); }
void DoubleLinkedList::append(int val) { insert(len(), val); }

删除节点

void DoubleLinkedList::pop(int pos) {
    if (head == nullptr) {
        cout << "Attempt to delete a NULL DoubleLinkedList!" << endl;
    } else if (head->next == nullptr) {
        delete head;
        head = nullptr; // 指针置为空, 避免空悬指针, 下同
    } else {
        if (pos >= len() - 1) {
            // 使用内置的visit函数
            auto cur = rvisit(1), tmp = cur->next;
            delete tmp;
            cur->next = nullptr; // 必须加, 否则析构时候会出错
        } else if (pos <= 0) {
            auto cur = head->next;
            delete head;
            head = cur;
        } else {
            auto cur = visit(pos - 1);
            auto nxt = cur->next->next, tmp = cur->next;
            cur->next = nxt;
            delete tmp;
        }
    }
}

void DoubleLinkedList::pop_front() { pop(0); }
void DoubleLinkedList::pop_back() { pop(len()); }

void DoubleLinkedList::remove(int val, int cnt) {
    int idx;
    do {
        if ((idx = find(val)) == -1) {
            cout << "can not find val " << val << " to delete\n";
            break;
        } else
            pop(idx);
    } while (--cnt);
}

修改节点

// modify val->node cnt times
void DoubleLinkedList::modify(int val, int node, int cnt) {
    int idx;
    do {
        if ((idx = find(val)) == -1) {
            cout << "can not find val " << val << " to modify\n";
            break;
        } else {
            /* visit(idx)->val = node; */
            (*this)[idx]->val = node;
        }
    } while (--cnt);
}

查找节点

int DoubleLinkedList::find(int node) {
    if (head == nullptr) return -1;
    auto cur = head;
    int pos{};
    while (cur) {
        if (cur->val == node) return pos;
        ++pos;
        cur = cur->next;
    }
    return -1;
}

int DoubleLinkedList::rfind(int node) {
    // 从右往左找
    auto cur = last();
    if (cur == nullptr) return -1;
    int pos{};
    while (cur) {
        if (cur->val == node) return pos;
        ++pos;
        cur = cur->prev;
    }
    return -1;
}

主函数: 测试

#include "Double_Linked_List.hpp"

// === test func === ===//
void test_ctor() {
    DoubleLinkedList ll1(12);
    ll1.print();

    DoubleLinkedList ll2(1, {2, 3, 4});
    ll2.print();

    DoubleLinkedList ll3({1, 2, 3, 4});
    ll3.print();
    /* ∅ <== 12 ==> ∅ */
    /* ∅ <== 1 <=> 2 <=> 3 <=> 4 ==> ∅ */
    /* ∅ <== 1 <=> 2 <=> 3 <=> 4 ==> ∅ */
    /* 1 2 3 4 deleted... */
    /* 1 2 3 4 deleted... */
    /* 12 deleted... */
}

void test_fundamental() {
    DoubleLinkedList ll1({1, 2, 3, 4});
    cout << "ll1=";
    ll1.print();
    cout << "len(ll1)=" << ll1.len() << endl;
    cout << "first: " << ll1.first()->val << endl;
    cout << "last: " << ll1.last()->val << endl;
    cout << "visit(1) : " << ll1.visit(1)->val << endl;
    cout << "rvisit(1) : " << ll1.rvisit(1)->val << endl;
    /* ll1=∅ <- 1 <-> 2 <-> 3 <-> 4 -> ∅ */
    /* len(ll1)=4 */
    /* first: 1 */
    /* last: 4 */
    /* visit(1) : 2 */
    /* rvisit(1) : 3 */
    /* 1 2 3 4 deleted... */
}

void test_insert() {
    DoubleLinkedList ll1({1, 2, 3, 4});
    ll1.print();
    ll1.insert(0, 3);
    ll1.insert(5, 3);
    ll1.insert(2, 3);
    ll1.print();
    /* ∅ <- 1 <-> 2 <-> 3 <-> 4 -> ∅ */
    /* ∅ <- 3 <-> 1 <-> 3 <-> 2 <-> 3 <-> 4 <-> 3 -> ∅ */
    /* 3 1 3 2 3 4 3 deleted... */
}

void test_pop() {
    DoubleLinkedList ll1({0, 1, 2, 3, 4});
    ll1.pop(12);
    ll1.pop(-1);
    ll1.pop(1);
    ll1.print(); // ∅ <- 1 <-> 3 -> ∅
    /* 1 3  deleted... */
}

void test_find_pop() {
    DoubleLinkedList ll1({1, 2, 3, 4});
    ll1.append(5);
    ll1.pop(0);
    ll1.pop_back();
    ll1.insert(4, 12);
    ll1.add2head(9);
    cout << "ll1.find(2):" << ll1.find(2) << endl;
    cout << "ll1.rfind(12):" << ll1.rfind(12) << endl;
    ll1.print();
    /* ll1.find(2):1 */
    /* ll1.rfind(12):0 */
    /* ∅ <- 9 <-> 2 <-> 3 <-> 4 <-> 12 -> ∅ */
    /* 9 2 3 4 12 deleted... */
}

void test_operator_at_modify() {
    DoubleLinkedList ll1({1, 2, 3, 4});
    ll1.append(1);
    ll1.remove(1, 3);
    ll1.print();
    cout << "ll1[0]=" << ll1[0]->val << endl;
    /* cout << "ll1[9]=" << ll1[9] << endl; */
    ll1.add2head(1);
    ll1.modify(1, 11, 3);
    ll1.print();
    /* can not find val 1 to delete */
    /* ∅ <- 2 <-> 3 <-> 4 -> ∅ */
    /* ll1[0]=2 */
    /* can not find val 1 to modify */
    /* ∅ <- 11 <-> 2 <-> 3 <-> 4 -> ∅ */
    /* 11 2 3 4 deleted... */
}

int main(int argc, char* argv[]) {
    // test_ctor();
    test_fundamental();
    /* test_insert(); */
    /* test_pop(); */
    /* test_find_pop(); */
    /* test_operator_at_modify(); */
    return 0;
}

小结

会了单链表, 双向链表也就会了, 注意指针的删除释放内存等操作即可, 都是细节.

如果有什么问题欢迎评论区留言.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

双向链表的增删改查C++完整实现 的相关文章

  • 宝塔忘记端口,解决办法

    1 xff0c 登陆远程连接 xff0c 输入ll 2 输入cd后再ll 3 清下屏 xff0c 输入clear 4 xff0c 输入cd www server panel data 找到port pl 5 输入cat port pl查看端
  • 幽冥传奇

    JAVA环境添加 setx M JAVA HOME D YM cnmmm com bl20166 Java jdk1 8 0 144 setx M CLASSPATH JAVA HOME lib dt jar JAVA HOME lib t
  • TOR下载教程

    不想用自己浏览器ip访问可用以下设置 xff0c 当然有很多其他方法 1 xff0c 官网https www torproject org 2 xff0c 下载对应版本 3 xff0c 打开tor 洋葱浏览器 并选择config 4 lin
  • 几步搞懂cobalt strike启动

    很多人问Cobalt strike怎么启动 xff0c 总结几句 1 cmd管理员身份运2 切换到CS所在目录3 输入ipconf找到自己ip地址4 输入teamserver 自己ip 密码 回车即可5 打开start bat文件再点击确定
  • TOR下载和网桥配置教程

    不想用自己浏览器ip访问可用以下设置 xff0c 当然有很多其他方法 1 xff0c 官网https www torproject org 2 xff0c 下载对应版本安装即可 本节以windows为例 xff08 苹果 安卓手机都有对应a
  • XSS漏洞,通过XSS实现网页挂马

    今天讲下通过XSS实现网页挂马 xff0c 目的是了解安全方面知识 xff0c 提升生活网络中辨别度 原理 xff1a 实验分为两部分 xff1a 1 通过Kali linux xff0c 利用MS14 064漏洞 xff0c 制作一个木马
  • qt样式有时不生效问题分析

    qt 中的样式表非常方便 xff0c 可以自定义自己想要的控件 但是有时候会发现使用样式表时 xff0c 样式不会生效 接下来分析一下主要原因 xff1a 1 样表格式不正确 2 样式表的样式被子对象覆盖 xff0c 设置时注意作用对象 x
  • 逻辑回归案例

    应用案例 之前学习了逻辑回归 xff0c 我们现在来做一个案例 一个图片验证码识别的案例 xff1a 怎么从图片中准确的识别出正确的数字 我们分了三步 第一步 xff1a 先生成150验证码图片 xff0c 每个图片有5个数字 图片中有随机
  • CorelDRAW x4提示非法软件产品被禁用解决方法教程

    说起PS大部分人都有所耳闻 xff0c 甚至会一些简单的操作 但是CDR x4这名字相信有很多人就很陌生了 xff0c 所以在这里也很有必要先说一下CDR到底是个什么样的存在 CDR全名CorelDRAW xff0c 是加拿大Corel公司
  • Mybatis-Plus中分页插件PaginationInterceptor, MybatisPlusInterceptor在SpringBoot中的使用

    配置分页插件 span class token annotation punctuation 64 Configuration span span class token keyword public span span class tok
  • 矩阵连乘问题-构造最优解

    题目描述 使用动态规划算法求解矩阵连乘问题 输入 每组数据包括两行 xff0c 第一行为数组长度n xff0c 第二行为存储矩阵维数的一维数组 输出 矩阵连乘最优计算次序 样例输入 Copy 7 30 35 15 5 10 20 25 样例
  • 树莓派启动——安装+无显示器使用+自启动VNC

    目录 硬件准备软件准备写入系统启动树莓派换源VNC自启动 时隔一年多 xff0c 拿起树莓派却忘记如何使用了 本想用作自己搭建git服务器 xff0c 后续再完成了 在此记录一下使用流程 硬件准备 树莓派 3b 43 TF卡和读卡器 xff
  • Debain 10(Buster)换源

    Debain 10 Buster 换源的操作步骤 必要条件 xff1a 已经安装好的Debain 10 Buster 开始 Debain 10 Buster 换源的操作步骤步骤一 备份原始的源文件用户切换到root下 进行源文件备份 二 换
  • 使用nginx反向代理突然失灵

    之前使用nginx反向代理还好好的 xff0c 后来再启动项目时突然失灵 xff0c 浏览器显示如下 然后开始排查错误 xff0c 首先直接使用ip地址访问是正常的 xff0c 然后使用hosts中映射的域名访问是无效的 xff0c 这说明
  • win10 安装 Linux子系统(WSL)

    序 xff1a 前段时间字节不是发布了 modernJS 的开源项目吗 xff1f 大概看了一部分的内容 xff0c 这些的东西就不一一列出来了 xff0c 本来想尝一口的 xff0c 在环境准备的系统那里就先折了一下 xff08 目前支持
  • Java 集合

    ArrayList 默认长度为10 indexOf lastIndexOf 通过equals方法判断索引 span class token keyword public span span class token keyword int s
  • Java 多线程知识

    参考链接 xff1a https www cnblogs com kingsleylam p 6014441 html https blog csdn net ly0724ok article details 117030234 https
  • Java I/O

    参考链接 xff1a https blog csdn net m0 71563599 article details 125120982 https www cnblogs com shamo89 p 9860582 html https
  • 最小生成树 prim算法(附代码)

    prim算法是以一个根节点开始慢慢往下延伸 xff0c 不断寻找距生成树最短的距离的节点 xff0c 然后将该节点纳入生成树的集合中 xff0c 然后再将该节点影响的其他未纳入生成树节点的距离更新 xff08 缩小与生成树的距离 xff09
  • cdr x4检测显示软件产品已被禁用警告弹窗,如何解决教程分享

    偶尔翻开移动硬盘 xff0c 找到这货 xff0c CorelDraw X4简体中文正式版 网上现在比较难下载得到了 xff0c X4是我最常用的一个 现在把它分享出来 xff0c 有需要的可以去下载使用 orelDRAW X4打开显示被禁

随机推荐

  • 数据结构与算法题目集(中文) 6-1 单链表逆转 (20 分)

    本题要求实现一个函数 xff0c 将给定的单链表逆转 函数接口定义 xff1a List Reverse List L 其中List结构定义如下 xff1a typedef struct Node PtrToNode struct Node
  • HTML5 Table 布局实现 商品列表

    运行结果如上 下面说说设计过程 xff1a 一开始试探的做的时候 xff0c 是建立了一个table xff0c 这个table里面放一本图书的信息 然后建立了一个列 xff0c 然后建立了个td xff0c td里面放图片 xff0c t
  • POJ 1050 To the Max(动态规划)

    Given a two dimensional array of positive and negative integers a sub rectangle is any contiguous sub array of size 1 1
  • web前端 背景色属性bgcolor

    通过 lt body gt 元素中的bgcolor属性来设定网页的背景颜色 其语法格式如下 xff1a lt body bgcolor 61 34 value 34 gt 颜色是属性值的设定有三种方法 xff1a 1 颜色名称 规定颜色值为
  • java连接数据库步骤

    1 加载驱动 Class forname 数据库驱动名 2 建立数据库连接 使用DriverManager类的getConnection 静态方法来获取数据库连接对象 xff0c 其语法格式如下所示 Connection conn 61 D
  • 怎么从零开始运行github / 现成的项目

    这篇博客是作为非计软科班出身的我记录的一些经验 xff0c 希望得到交流和批评 目录 环境配置 通过文件命名了解项目 demo 代码运行的入口 设定参数的文件 build 通过代码了解项目 64 装饰器 一些交流时用到的术语 API 交流或
  • 生产环境中使用Kolla部署OpenStack-allinone云平台(红帽8版本)

    CentOS8系统中使用Kolla部署OpenStack allinone云平台 Kolla概述和openstack所有结点linux系统初始配置 kolla是openstack下面用于自动化部署的一个项目 xff0c 它基于docker和
  • vue2项目-request配置put请求Content-Type为x-www-form-urlencoded

    在项目中遇到需要使用put请求的接口 使用的方式是x www form urlencoded 步骤梳理 在项目的request js文件是默认配置了json方式的 span class token keyword import span a
  • STM32学习第一课——新建工程与点亮LED灯

    第一次接触到32位的MCU与之前所学的51单片机和430单片机都是有所不同的 xff0c STM32是用库函数来写程序的这样一来不管是从代码的编写和移植都会方便很多 以下是今天所学的东西 xff1a 1 新建工程 个人觉得不用去新建一个工程
  • 基于arm架构的ubuntu18 .04安装Anaconda3 + pytorch+python3.9

    记录一下项目踩坑经历 xff08 查了很多资料 xff0c 感觉都是对有基础的人来说的 xff0c 对于刚接触深度学习环境的小白并不友好 xff0c 很多细节并没有 xff0c 各种坑无数 xff0c 我也是花了好长时间才弄清楚 xff09
  • MathType7应用中文版特色功能介绍

    MathType 是由美国Design Science公司开发的功能强大的数学公式编辑器 xff0c 它同时支持Windows和Macintosh 操作系统 xff0c 与常见的文字处理软件和演示程序配合使用 xff0c 能够在各种文档中加
  • QT的延时函数

    延时函数在收发数据的时候用处很大 在其他方面也有用处 这里提供四种方法 1 多线程程序 使用QThread sleep 或者QThread msleep 或QThread usleep 或QThread wait 进行延时处理 Sleep不
  • ios基础篇(八)—— iOS触摸事件

    iOS中的事件 iOS事件中分为三大类 xff0c 触摸事件 xff0c 加速器事件 xff0c 远程控制事件 响应者对象 在iOS 中 不是任何对象都是能处理事件的 xff0c 只有继承于UIResponder 的对象才能接受并且处理事件
  • 牛客C++ACM模式输入输出11道题分析与总结

    tags C 43 43 Interview 写在前面 感觉好久没写博客了 最近看的书多 但是真正沉淀下来的东西却很少 这次总结一下C 43 43 刷题中常用的一些IO操作 也就是ACM模式中的一些基本操作 看到知识星球里面推荐了牛客的一个
  • C++类内初始化vector的一个小坑与分析解决

    tags C 43 43 Debug STL 问题 先来看这样一份代码 vector span class token operator lt span vector span class token operator lt span sp
  • 力扣螺旋矩阵系列总结(C++)

    tags DSA C 43 43 LeetCode Python 写在前面 螺旋矩阵系列 严格来说不算双指针 但是其中蕴含的思想很像双指针 应该叫四指针 54 螺旋矩阵 力扣 xff08 LeetCode xff09 需要四个指针分别在需要
  • GitHub提交时出现Host key verification failed无法读取远程仓库的解决方案

    tags Git Debug Tips 问题 今天提交代码时候发现有这样一个问题 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 6
  • 在M1芯片MacBook上部署微软最新Visual-GPT的完整方案总结

    tags Python MacOS 写在前面 好久没写博客 因为最近一直忙着看Effective系列 终于告一段落了 看到微软出的Visual chatgpt 想试试 后来失败了 在这里记录一下吧 参考 https github com m
  • Andrews定理的证明(对称单峰多项式乘积保持对称单峰性)

    tags Maths 预备定义 原始文献A Theorem on Reciprocal Polynomials with Applications to Permutations and Compositions 对称多项式 对称 reci
  • 双向链表的增删改查C++完整实现

    tags C 43 43 DSA LinkedList 写在前面 写一下双向链表的增删改查 用C 43 43 实现 完整代码可以看我的GitHub 节点类 链表类 节点 span class token keyword class span