【数据结构】双向链表 C++

一、什么是双向链表

1、定义

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

双向链表的结构如图(图片来源于网络):

2、时空复杂度

双向链表的空间复杂度是 O ( n ) O(n) O(n) 的,其时间复杂度如下:

操作时间复杂度
遍历 O ( n ) O(n) O(n)
访问指定节点 O ( 1 ) O(1) O(1)
删除指定编号节点 O ( n ) O(n) O(n)
删除指定位置节点 O ( 1 ) O(1) O(1)
在指定编号的节点后插入节点 O ( n ) O(n) O(n)
在指定位置的节点后插入节点 O ( 1 ) O(1) O(1)
查询前驱、后继 O ( 1 ) O(1) O(1)
修改指定编号节点的值 O ( n ) O(n) O(n)
修改指定位置节点的值 O ( 1 ) O(1) O(1)
交换两个 list 容器 O ( 1 ) O(1) O(1)

二、双向链表的基本操作

1. 定义双向链表节点

每个节点有三个值:

  1. val:存储每个节点的权值;
  2. last:指向每个节点的前面的第一个节点;
  3. next:指向每个节点的后面的第一个节点;

代码如下:

template<typename T>
struct ListNode{T value;ListNode<T>* last;ListNode<T>* next;ListNode():value(0){last=NULL,next=NULL;}ListNode(const T &x):value(x){last=NULL,next=NULL;}~ListNode(){value=0;delete last;delete next;}
};

2. 创建双向链表类

类里面包含两个节点和一个变量:

  1. headnode:头节点,初始时前驱后继均为空,值为 − 1 -1 1
  2. endnode:尾节点,初始时前驱后继均为空,值为 − 1 -1 1
  3. listsize:记录双向链表的节点个数,不包含头尾节点;

代码如下:

template<typename T>
class list{private:unsigned listsize;ListNode<T>* headnode;ListNode<T>* endnode;
};

3. 初始化双向链表类

共有四种初始化方式:

  1. list<类型名> a;:此时创建一个空的双向链表;
  2. list<类型名> a(n);:此时创建一个大小为 n n n 的双向链表,并将所有点的初始值赋为 0 0 0
  3. list<类型名> a(n,m):此时创建一个大小为 n n n 的双向链表,并将所有点的初始值赋为 m m m
  4. list<类型名> a={a1,a2,a3,...,an};:此时创建一个大小为 n n n 的双向链表,并将第 i i i 个节点的初始值赋为 a i a_i ai

第一种初始化方式代码如下:

list():listsize(0){headnode=new ListNode<T>(-1);endnode=new ListNode<T>(-1);
}

第二种初始化方式代码如下:

list(const int &size_t):listsize(size_t) {headnode=new ListNode<T>(-1);endnode=new ListNode<T>(-1);ListNode<T>* now=headnode;for(int i=0;i<listsize;++i){ListNode<T>* newnode=new ListNode<T>(0);endnode->last=newnode;newnode->next=endnode;newnode->last=now;now->next=newnode;now=now->next;}
}

第三种初始化方式代码如下:

list(const int &size_t,const int &val
):listsize(size_t){headnode=new ListNode<T>(-1);endnode=new ListNode<T>(-1);ListNode<T>* now=headnode;for(int i=0;i<listsize;++i){ListNode<T>* newnode=new ListNode<T>(val);endnode->last=newnode;newnode->next=endnode;newnode->last=now;now->next=newnode;now=now->next;}
}

第四种初始化方式代码如下:

typedef std::initializer_list<T> lisval;
list(lisval vals){listsize=0;headnode=new ListNode<T>(-1);endnode=new ListNode<T>(-1);ListNode<T>* now=headnode;for(auto val:vals){ListNode<T>* newnode=new ListNode<T>(val);endnode->last=newnode;newnode->next=endnode;newnode->last=now;now->next=newnode;now=now->next; ++listsize;}
}

3. 一些基础的函数

这些函数是除了加点删点之外最常见的几个函数。

  1. size():获取链表的大小,返回一个 unsigned 值,表示当前链表中普通节点(非头尾节点)的个数。

    代码如下:

    unsigned size() const {return listsize;
    }
    
  2. empty():返回当前链表是否为空,如果是,返回 true,否则返回 false。

    代码如下:

    bool empty() const {return listsize==0;
    }
    
  3. begin():返回第一个普通节点。

    代码如下:

    ListNode<T>* begin(
    ) const {return headnode->next;
    }
    
  4. end():返回尾指针。

    代码如下:

    ListNode<T>* end(
    ) const {return endnode;
    }
    
  5. rbegin():返回最后一个普通节点。

    代码如下:

    ListNode<T>* rbegin(
    ) const {return endnode->last;
    }
    
  6. rend():返回头指针。

    代码如下:

    ListNode<T>* rend(
    ) const {return headnode;
    }
    
  7. front():返回第一个普通节点的值。

    代码如下:

    T front() const {return begin()->value;
    }
    
  8. back():返回最后一个普通节点的值。

    代码如下:

    T back() const {return rbegin()->value;
    }
    
  9. print():遍历并输出链表中每个普通节点的值,结尾换行。

    代码如下:

    void print(
    ) const {if(empty()) return;ListNode<T>* now=headnode->next;while(now->next!=NULL){printf("%d ",now->value);now=now->next;} putchar('\n');
    }
    
  10. swap(list<类型名> &b):交换两个 list 容器,实际上是交换头尾指针和 l i s t s i z e listsize listsize

    代码如下:

    void swap(list<T> &b){ListNode<T>* temp;temp=headnode;headnode=b.headnode;b.headnode=temp;temp=endnode;endnode=b.endnode;b.endnode=temp;unsigned size_t=listsize;listsize=b.listsize;b.listsize=size_t;
    }
    

5. 插入节点

共四种方法,代码如下:

void push_back(const T &val
){ ++listsize;if(endnode->last==NULL){ListNode<T>* newnode=new ListNode<T>(val);endnode->last=newnode;newnode->next=endnode;headnode->next=newnode;newnode->last=headnode;return;}ListNode<T>* pre=endnode->last;ListNode<T>* newnode=new ListNode<T>(val);pre->next=newnode;newnode->last=pre;newnode->next=endnode;endnode->last=newnode;
}
void push_front(const T &val
){ ++listsize;if(headnode->next==NULL){ListNode<T>* newnode=new ListNode<T>(val);endnode->last=newnode;newnode->next=endnode;headnode->next=newnode;newnode->last=headnode;return;}ListNode<T>* suf=headnode->next;ListNode<T>* newnode=new ListNode<T>(val);headnode->next=newnode;newnode->last=headnode;newnode->next=suf;suf->last=newnode;
}
void insert(const T &pos,const T &val
){  int nowpos=0;if(pos==0){push_front(val);++listsize; return;} else if(pos>=listsize){push_back(val);++listsize; return;}ListNode<T>* now=headnode->next;while(now->next!=NULL){++nowpos;if(nowpos==pos){ListNode<T>* newnode=new ListNode<T>(val);ListNode<T>* suf=now->next;newnode->next=suf;suf->last=newnode;newnode->last=now;now->next=newnode;++listsize; return;}now=now->next;}
}
void insert(ListNode<T>* now,const T &val
){if(now==endnode){push_back(val); return;}ListNode<T>* newnode=new ListNode<T>(val);ListNode<T>* suf=now->next;newnode->next=suf;suf->last=newnode;newnode->last=now;now->next=newnode;++listsize; return;
}

6. 修改指定位置的值

两种方法,代码如下:

void reassign(const T &pos,const T &val
){if(pos>listsize) return;if(empty()||!pos) return;ListNode<T>* now=headnode->next;int nowpos=0;while(now->next!=NULL){++nowpos;if(nowpos==pos){now->value=val;return;} now=now->next;}
}
void reassign(ListNode<T>* now,const int &val
) const {now->value=val;
}

7.删除节点

和插入一样,共有四种,代码如下:

void pop_back(){if(empty()) return;ListNode<T>* now=endnode->last;ListNode<T>* pre=now->last;if(pre==headnode){endnode->last=NULL;headnode->last=NULL;--listsize; return;}endnode->last=pre;pre->next=endnode;--listsize;
}
void pop_front(){if(empty()) return;ListNode<T>* now=headnode->next;ListNode<T>* suf=now->next;if(suf==endnode){endnode->last=NULL;headnode->last=NULL;--listsize; return;}headnode->next=suf;suf->last=headnode;--listsize;
}
void erase(const int &pos
) {if(pos>listsize) return;if(empty()||!pos) return;ListNode<T>* now=headnode->next;int nowpos=0;while(now!=endnode){++nowpos;if(nowpos==pos){ListNode<T>* pre=now->last;ListNode<T>* suf=now->next;if(pre==headnode||suf==endnode){endnode->last=NULL;headnode->next=NULL;delete now;--listsize; return;}pre->next=suf;suf->last=pre;delete now;--listsize; return;}now=now->next;}
}
void erase(ListNode<T>* now
){  if(now==headnode) return;if(now==endnode) return;if(empty()) return;ListNode<T>* pre=now->last;ListNode<T>* suf=now->next;if(pre==headnode||suf==endnode){endnode->last=NULL;headnode->last=NULL;--listsize; return;}pre->next=suf;suf->last=pre;--listsize; return;
}

8. 注销双向链表类

遍历一遍,然后将每个节点都删除就可以了。

代码如下:

~list(){ListNode<T>* now=headnode->next;while(now!=NULL){ListNode<T>* nxt=now->next;delete now;now=nxt;} delete headnode; listsize=0;
}

三、完整代码

我知道你们只看这个

码风丑陋,不喜勿喷

#include<stdio.h>
#include<stdlib.h>
#include<initializer_list>
namespace STL{template<typename T>struct ListNode{T value;ListNode<T>* last;ListNode<T>* next;ListNode():value({}){last=NULL,next=NULL;}ListNode(const T &x):value(x){last=NULL,next=NULL;}~ListNode(){// value={};last=NULL;next=NULL;}};template<typename T>class list{private:unsigned listsize;ListNode<T>* headnode;ListNode<T>* endnode;public:list():listsize(0){headnode=new ListNode<T>(T({-1}));endnode=new ListNode<T>(T({-1}));}list(const int &size_t):listsize(size_t) {headnode=new ListNode<T>(-1);endnode=new ListNode<T>(-1);ListNode<T>* now=headnode;for(int i=0;i<listsize;++i){ListNode<T>* newnode=new ListNode<T>(0);endnode->last=newnode;newnode->next=endnode;newnode->last=now;now->next=newnode;now=now->next;}}list(const int &size_t,const int &val):listsize(size_t){headnode=new ListNode<T>(-1);endnode=new ListNode<T>(-1);ListNode<T>* now=headnode;for(int i=0;i<listsize;++i){ListNode<T>* newnode=new ListNode<T>(val);endnode->last=newnode;newnode->next=endnode;newnode->last=now;now->next=newnode;now=now->next;}}typedef std::initializer_list<T> lisval;list(lisval vals){listsize=0;headnode=new ListNode<T>(-1);endnode=new ListNode<T>(-1);ListNode<T>* now=headnode;for(auto val:vals){ListNode<T>* newnode=new ListNode<T>(val);endnode->last=newnode;newnode->next=endnode;newnode->last=now;now->next=newnode;now=now->next; ++listsize;}}unsigned size() const {return listsize;}bool empty() const {return listsize==0;}ListNode<T>* begin() const {return headnode->next;}ListNode<T>* end() const {return endnode;}ListNode<T>* rbegin() const {return endnode->last;}ListNode<T>* rend() const {return headnode;}T front() const {return begin()->value;}T back() const {return rbegin()->value;}void print() const {if(empty()) return;ListNode<T>* now=headnode->next;while(now->next!=NULL){printf("%lld ",now->value);now=now->next;} putchar('\n');}void push_back(const T &val){ ++listsize;if(endnode->last==NULL){ListNode<T>* newnode=new ListNode<T>(val);endnode->last=newnode;newnode->next=endnode;headnode->next=newnode;newnode->last=headnode;return;}ListNode<T>* pre=endnode->last;ListNode<T>* newnode=new ListNode<T>(val);pre->next=newnode;newnode->last=pre;newnode->next=endnode;endnode->last=newnode;}void push_front(const T &val){ ++listsize;if(headnode->next==NULL){ListNode<T>* newnode=new ListNode<T>(val);endnode->last=newnode;newnode->next=endnode;headnode->next=newnode;newnode->last=headnode;return;}ListNode<T>* suf=headnode->next;ListNode<T>* newnode=new ListNode<T>(val);headnode->next=newnode;newnode->last=headnode;newnode->next=suf;suf->last=newnode;}void insert(const T &pos,const T &val){  int nowpos=0;if(pos==0){push_front(val);++listsize; return;} else if(pos>=listsize){push_back(val);++listsize; return;}ListNode<T>* now=headnode->next;while(now->next!=NULL){++nowpos;if(nowpos==pos){ListNode<T>* newnode=new ListNode<T>(val);ListNode<T>* suf=now->next;newnode->next=suf;suf->last=newnode;newnode->last=now;now->next=newnode;++listsize; return;}now=now->next;}}void insert(ListNode<T>* now,const T &val){if(now==endnode){push_back(val); return;}ListNode<T>* newnode=new ListNode<T>(val);ListNode<T>* suf=now->next;newnode->next=suf;suf->last=newnode;newnode->last=now;now->next=newnode;++listsize; return;}void reassign(const T &pos,const T &val){if(pos>listsize) return;if(empty()||!pos) return;ListNode<T>* now=headnode->next;int nowpos=0;while(now->next!=NULL){++nowpos;if(nowpos==pos){now->value=val;return;} now=now->next;}}void reassign(ListNode<T>* now,const int &val) const {now->value=val;}void pop_back(){if(empty()) return;ListNode<T>* now=endnode->last;ListNode<T>* pre=now->last;if(pre==headnode){endnode->last=NULL;headnode->next=NULL;delete now;--listsize; return;}endnode->last=pre;pre->next=endnode;delete now;--listsize;}void pop_front(){if(empty()) return;ListNode<T>* now=headnode->next;ListNode<T>* suf=now->next;if(suf==endnode){endnode->last=NULL;headnode->next=NULL;delete now;--listsize; return;}headnode->next=suf;suf->last=headnode;delete now;--listsize;}void erase(const int &pos) {if(pos>listsize) return;if(empty()||!pos) return;ListNode<T>* now=headnode->next;int nowpos=0;while(now!=endnode){++nowpos;if(nowpos==pos){ListNode<T>* pre=now->last;ListNode<T>* suf=now->next;if(pre==headnode||suf==endnode){endnode->last=NULL;headnode->next=NULL;delete now;--listsize; return;}pre->next=suf;suf->last=pre;delete now;--listsize; return;}now=now->next;}}void erase(ListNode<T>* now){  if(now==headnode) return;if(now==endnode) return;if(empty()) return;ListNode<T>* pre=now->last;ListNode<T>* suf=now->next;if(pre==headnode||suf==endnode){endnode->last=NULL;headnode->last=NULL;delete now;--listsize; return;}pre->next=suf;suf->last=pre;delete now;--listsize; return;}void swap(list<T> &b){ListNode<T>* temp;temp=headnode;headnode=b.headnode;b.headnode=temp;temp=endnode;endnode=b.endnode;b.endnode=temp;unsigned size_t=listsize;listsize=b.listsize;b.listsize=size_t;}~list(){ListNode<T>* now=headnode->next;while(now!=NULL){ListNode<T>* nxt=now->next;delete now;now=nxt;} delete headnode;listsize=0;}};
}
using STL::list;signed main(){system("pause");
}

给个赞再走吧

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

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

相关文章

归并排序的非递归写法

归并排序的非递归写法 核心&#xff1a; 通过while循环控制gap 通过for循环控制归并的区间 但要注意begin2和end2&#xff0c;如果超过n-1的话就会将随机数拷贝到原数组中&#xff0c;从而引发报错。 通过调试可以发现报错是由于随机值引发的。 void merge_sort_non(int* …

辽宁梵宁教育:设计培训领域的靠谱之选

在当今日益竞争激烈的社会中&#xff0c;设计行业正以其独特的魅力和无限的创意空间吸引着越来越多的年轻人。然而&#xff0c;想要在这个领域取得一席之地&#xff0c;仅凭一腔热情是远远不够的&#xff0c;专业的培训和教育显得尤为重要。辽宁梵宁教育&#xff0c;作为设计培…

测开面经(pytest测试案例,接口断言,多并发断言)

pytest对用户登录接口进行自动化脚本设计 a. 创建一个名为"test_login.py"的测试文件&#xff0c;编写以下测试脚本 import pytest import requests# 测试用例1&#xff1a;验证登录成功的情况 # 第一个测试用例验证登录成功的情况&#xff0c;发送有效的用户名和密…

springboot实现上传文件接口(简单版)

使用springboot实现一个最简单版本的上传文件接口 private String uploadPath "C:/imageFiles";RequestMapping(value "/upload", method RequestMethod.POST)private Result upload( RequestParam("modelName") String modelName,RequestPar…

Keil 出现 “access to xxx was denied” 错误

先说结论&#xff1a;文件路径太长导致的 事情是这样的&#xff0c;在用STM32的开发板调试CC1101的收发工程&#xff0c;运行程序呢&#xff0c;结果电脑死机重启了&#xff0c;顺便吐槽一下&#xff0c;最近用VMware经常重启。 电脑重启后再打开工程&#xff0c;发现程序能烧…

腾讯电商运营起来竟然这么简单!视频号小店操作玩法一文详解!

大家好&#xff0c;我是电商小布。 在新型电商玩法的兴起下&#xff0c;很多的平台都在电商行业内分到了一杯羹。 腾讯自然也就坐不住了&#xff0c;背靠自身的视频号平台&#xff0c;推出了视频号小店这个项目。 有很多的小伙伴想要趁着这个初期阶段&#xff0c;来加入到其…

Linux gcc day5粘滞位

粘滞位 背景&#xff1a;一定时在一个公共目录&#xff08;root创建&#xff09;下。进行临时文件的操作 Linux系统中有很多人&#xff0c;我们需要在一个公共目录下&#xff0c;进行临时文件的操作&#xff08;增删查改&#xff09; 创建一个根目录下的dir&#xff08;mytmp…

基于liorf_localization的重定位

文章目录 概述保存和加载地图利用现有地图进行重定位代码实现Q&&AQ1: point cloud is not in dense format概述 在LIO-SAM的基础上进行重定位,主要是指在已经建立的地图上进行位置的快速定位,这对于机器人在已知环境中的快速启动或者在丢失定位后的恢复尤为重要。L…

链表之双向链表的实现

铁汁们大家好&#xff0c;我们上一篇博客学习了单链表&#xff0c;这节课让我们继续往深学习&#xff0c;学习一下双线链表&#xff0c;话不多说&#xff0c;我们开始吧&#xff01; 目录 1.双向链表 2.顺序表和链表的优缺点 3.双向链表的实现 1.双向链表 1.我们要实现的双线…

设计模式之观察者模式讲解

概念&#xff1a;定义对象间一种一对多的依赖关系&#xff0c;使得当每一个对象改变状态&#xff0c;则所有依赖于它的对象都会得到通知并被自动更新。 抽象主题&#xff1a;或者叫被观察者&#xff0c;可以持有、增加、删除观察者对象。具体主题&#xff1a;实现抽象主题定义的…

挖掘未来:私有LTE/5G网络驱动智慧矿山的自动化

私有LTE/5G网络为世界上一些最偏远的角落提供无线连接。如果没有无线通信网络&#xff0c;各行业就无法满足增加产量、降低运营成本和减少环境破坏的需求。 在本案例研究中&#xff0c;我们着眼于自动化如何改变无线网络的动态。智慧矿山要求运营商无缝集成多个系统和应用程序…

Xinstall助力提升用户体验:一键打开App用户页面

在移动互联网时代&#xff0c;App已经成为我们日常生活中不可或缺的一部分。然而&#xff0c;随着App数量的激增&#xff0c;如何让用户更便捷地打开和使用App&#xff0c;提升用户体验&#xff0c;成为了开发者和广告主们亟待解决的问题。此时&#xff0c;Xinstall作为国内专业…