C++实现双向链表代码分析

目录
  • 前言:
  • 一、双向链表优缺点
  • 二、C++实现分析
    • (1)节点类
    • (2)链表类分析
    • (3)链表类构造函数
    • (4)isEmpty()判断是否为空
    • (5)size()获取链表长度
    • (6)getNode()获取节点
    • (7)insert()插入节点
    • (8)、remove()删除节点
    • (9)traversal()遍历链表函数

前言:

前面文章分析了单向链表,并给出了python和C++实现:单链表从原理到实现,python和C++两个版本

本文介绍的双向链表是在单向链表基础上的一个改进,每个节点指向其直接前驱和直接后继节点。因此,从双向链表的任意位置开始,都能访问所有的节点。

一、双向链表优缺点

双向链表的缺点:

从节点的结构上可以看出,双向链表的所需的存储空间大于单向链表。同时,对于插入和删除等操作来说,双向链表的节点操作更加复杂,涉及到节点的前后两个节点。

双向链表的节点:

对于双向链表来说,它的每个节点要指向“直接前驱”和“直接后继”,所以节点类需要含有两个指针域。指向直接前驱的指针使用pre表示,指向后继的指针使用next表示。

二、C++实现分析

(1)节点类

双向链表的节点含有两个指针域,即直接前驱pre和直接后继next。节点类采用的是模板实现,这样其所存储的数据就不再依赖于特定类型。

template<class T>
class Node {
public:
    Node() {}
    Node *pre;
    Node *next;
    // 由于data属性是私有的
    // 所以采用get和set对data进行处理
    void setData(T data) { this->data = data; }
    T getData() { return this->data; }
private:
    T data;
};

(2)链表类分析

链表类应该包含基本的增、改、删、查等操作,由于其各种功能的实现是很相似的,

所以下面给出了需要实现的典型函数:

  • 构造函数:
  • isEmpty()判断是否为空;
  • size()返回链表长度;
  • insert()头插、尾插、中间插入节点;
  • delete()删除节点;
  • getNode()获取节点;
  • traversal()遍历链表;

链表类的定义如下:

template<class P>
class DoubleLinkedList {
public:
    DoubleLinkedList();
    bool isEmpty();
    Node<P>  *getNode(int index);
    int size();
    void insert(int data, int index);
    void traversal();
    void remove(int index);

private:
    Node<P> *head;
};

(3)链表类构造函数

初始化时需要创建头节点,作为头指针:

template<class P>
DoubleLinkedList<P>::DoubleLinkedList() {
    // 创建头结点
    head = new Node<P>();
    head->pre = NULL;
    head->next = NULL;
    head->setData(666);
}

(4)isEmpty()判断是否为空

对于双向链表来说,判断是否为空只需要判断头指针是否指向其他Node节点:

template<class P>
bool DoubleLinkedList<P>::isEmpty() {
    if (head->next == NULL) {
        return true;
    }
    else
    {
        return false;
    }
}

(5)size()获取链表长度

获取链表长度时需要判断链表是否为空,从而确定是否采用遍历的方式计算链表的长度。

由于采用的不是循环链表,所以循环的结束条件是判断是否指向空节点:

template<class P>
int DoubleLinkedList<P>::size() {
    if (isEmpty()) {
        return 0;
    }
    else {
        int count = 0;
        Node<P> *current = head->next;
            // 循环结束条件
        while (current!=NULL)
        {
            current = current->next;
            count++;
        }
        return count;
    }
}

(6)getNode()获取节点

在插入和删除等操作中,需要频繁的进行节点获取操作。

所以应该封装为单独的函数用于节点获取,如下:

template<class P>
Node<P> *DoubleLinkedList<P>::getNode(int index) { 
    Node<P> *current = head;
    int currentCount = 0;
    // 循环结束条件
    while (currentCount<=index)
    {
        current = current->next;
        currentCount++;
    }
    return current;
}

(7)insert()插入节点

插入节点依旧包含头插法,尾插法和任意位置的插入。插入操作与单向链表的最大区别在于节点的指针移动较为复杂,需要将插入位置前后两个节点与新节点均建立联系:

template<class P>
void DoubleLinkedList<P>::insert(int data, int index) {
    Node<P> *node = new Node<P>();
    node->setData(data);
    // 1、列表为空时
    if (isEmpty()) {
        head->next = node;
        node->pre = head;
        return;
    }
    // 2、头插法
    if (index == 0) {
        node->next = head->next;
        head->next->pre = node;
        node->pre = head;
        head->next = node;
    }
    // 3、尾插法
    else if (index >= this->size() - 1) {
        // printf("index %d, size %d \n", index, this->size());
        Node<P> *temp = this->getNode(this->size()-1);
        temp->next = node;
        node->pre = temp;
    }
    // 4、任意位置插入
    else
    {
        Node<P> *pre = this->getNode(index);
        Node<P> *next = pre->next;
        node->next = pre->next;
        node->pre = pre;
        pre->next = node;
        node->next->pre = node;
    }
}

(8)、remove()删除节点

前面已经定义了用于获取节点的getNode()函数,所以remove()函数只需要进行指针移动操作。

将所要删除的节点的直接前驱节点和直接后继节点相连:

template<class P>
void DoubleLinkedList<P>::remove(int index) {
    // 保证索引有意义
    if ((index < (this->size()-1)) && (index>0)) {
        Node<P> *node = this->getNode(index);
        Node<P> *pre = node->pre;
        Node<P> *next = node->next;
        pre->next = next;
        next->pre = pre;
    }
}

(9)traversal()遍历链表函数

虽然可以从双向链表的任一个节点开始遍历整个链表,但是下面的实现依旧是从头结点开始的,循环的结束依旧是指向空指针:

template<class P>
void DoubleLinkedList<P>::traversal() {
    if (!isEmpty()) {
        Node<P> *current = head;
        while (current)
        {
            cout << current->getData() << endl;
            current = current->next;
        }
    }
} 

到此这篇关于C++实现双向链表代码分析的文章就介绍到这了,更多相关C++双向链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++将二叉树转为双向链表及判断两个链表是否相交

    把二叉查找树转变成排序的双向链表 例如: 转换成双向链表 4=6=8=10=12=14=16 struct BSTreeNode { int m_nValue; // value of node BSTreeNode *m_pLeft; // left child of node BSTreeNode *m_pRight; // right child of node }; 首先阐述下二叉排序树: 它首先要是一棵二元树,在这基础上它或者是一棵空树:或者是具有下列性质的二元树: (1)若左子树不空

  • C++实现双向链表

    本文实例为大家分享了C++实现动态顺序表的具体代码,供大家参考,具体内容如下 List.h #pragma once #include <stdio.h> #include <iostream> #include <assert.h> using namespace std; typedef int DataType; struct ListNode { ListNode* _next; //存放下一个节点地址 ListNode* _prev; //存放上一个节点地址

  • 关于双向链表的增删改查和排序的C++实现

    双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循环链表. 由于双向链表可以方便地实现正序和逆序两个方向的插入.查找等功能,在很多算法中经常被使用, 这里用C++构造了一个双向链表,提供了对双向链表的插入.查找.删除节点.排序等功能,其中排序提供了插入排序和冒泡排序两种方式 #include<iostream> using namespace std;

  • C++实现双向链表(List)

    list是C++容器类中的"顺序存储结构"所包含的一种结构.list是非连续存储结构,具有双链表结构,支持前向/后向遍历,且支持高效的随机删除/插入. 实现代码如下: **list.h** #pragma once #include<stdio.h> #include<assert.h> #include<iostream> using namespace std; typedef int DataType; struct ListNode { Li

  • C++ 构造双向链表的实现代码

    构造双向链表,不足之处,还望指正!  复制代码 代码如下: // DoubleLinkedList.cpp : 定义控制台应用程序的入口点.//构造双向链表,实现从控制台输入,插入,删除,求大小size等操作#include "stdafx.h"#include <iostream>using namespace std;//定义双向链表的节点template<class T>struct NODE{ NODE<T>* pre; T data; NO

  • C++ 模版双向链表的实现详解

    代码如下所示: 复制代码 代码如下: #include <iostream>template <typename T>class double_linked{    struct node    {        T data;        node* prev;        node* next;        node(T t, node* p, node* n) : data(t), prev(p), next(n) {}    };    node* head;   

  • C++ 实现双向链表的实例

    双向链表C++ 的实现 本文是通过C++ 的知识实现数据结构中的双向链表,这里不多说 了,代码注释很清楚, 实现代码: //double LinkList implement with C++ template #include<iostream> using namespace std; /*template<typename Type> class DBListADT { public: virtual void Append(const Type &)=0; virt

  • c++双向链表操作示例(创建双向链、双向链表中查找数据、插入数据等)

    双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循环链表. (1)定义双向链表的基本结构 复制代码 代码如下: typedef struct _DOUBLE_LINK_NODE  {      int data;      struct _DOUBLE_LINK_NODE* prev;      struct _DOUBLE_LINK_NODE* nex

  • C++实现双向链表代码分析

    目录 前言: 一.双向链表优缺点 二.C++实现分析 (1)节点类 (2)链表类分析 (3)链表类构造函数 (4)isEmpty()判断是否为空 (5)size()获取链表长度 (6)getNode()获取节点 (7)insert()插入节点 (8).remove()删除节点 (9)traversal()遍历链表函数 前言: 前面文章分析了单向链表,并给出了python和C++实现:单链表从原理到实现,python和C++两个版本 本文介绍的双向链表是在单向链表基础上的一个改进,每个节点指向其直

  • Java的RTTI和反射机制代码分析

    RTTI,即Run-Time Type Identification,运行时类型识别.运行时类型识别是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息.RTTI能在运行时就能够自动识别每个编译时已知的类型. 很多时候需要进行向上转型,比如Base类派生出Derived类,但是现有的方法只需要将Base对象作为参数,实际传入的则是其派生类的引用.那么RTTI就在此时起到了作用,比如通过RTTI能识别出Derive类是Base的派生类,这样就能够向上转型为Derived.类似的,

  • hibernate存取json数据的代码分析

    一.场景 public class OrderModel { private List<String> favorableDescList; } 订单中会存储一些优惠信息,方便页面展示时使用,如: 1.满100减50 2.参与[老会员真情回馈--精品课程体验活动],仅需支付200.00学币 3.[Oracle + PL/SQL 实战]套装课程的[抢购]活动,优惠120.00学币 --等等 如图所示,我们在页面给用户展示他们参与的优惠信息: 二.分析 如上优惠信息有如下特点: 1.只用于展示,不

  • Zend Framework框架路由机制代码分析

    本文分析了Zend Framework框架路由机制代码.分享给大家供大家参考,具体如下: 在框架中,有关路由的调用关系为: 1.apache的mod_rewrite模块把请求路由到框架的启动脚本,一般是index.php: 2.前端控制器Zend_Controller_Front通过dispatch函数进行请求分发: 3.路由器Zend_Controller_Router_Rewrite通过route函数处理路由,对路由器中已有的路由规则,按照加入顺序的逆序(类似于栈,后进先出)对每个route

  • bootstrap 下拉多选框进行多选传值问题代码分析

    项目开发遇到个问题,就是引入bootstrap下拉多选框进行多选的时候,用form表单提交到后台,获取不到多选的值,只能获取的选择的第一个值. 纠结了会. jsp页面需要引入这东东~ <link rel="stylesheet" href="${ctx}/js/selectbootstrap/dist/css/bootstrap-select.min.css" rel="external nofollow" > <script

  • Android Fragment 和 FragmentManager 的代码分析

    这两天在研究插件化编程,在使用 Fragment 碰到了一些问题,于是查看源码,顺便分析了一下 Fragment 和 FragmentManager 以及其他几个 API 的原代码,看看他们是怎么工作的. 我们知道 Fragment 有个 onCreateView() 方法,这个方法在 Fragment 创建 View 的时候被调用,并且返回一个 View 对象.那么 onCreateView 在什么时候被调用呢,咱们在 Fragment 这个类里找到了一个方法,performCreateVie

  • Java语言求解完美数代码分析

    1.概念 首先我们理解一下,什么叫做完美数? 问题描述:若一个自然数,它所有的真因子(即除了自身以外的约数)的和恰好等于它本身,这种数叫做完全数.简称"完数" 例如, 6=1+2+3 28=1+2+4+7+14 496=1+2+4+8+16+31+62+124+248 8128=1+2+4+8+16+32+64+127+254+508+1016+2032+4064 按照完数的定义,其实用程序求解完数并不是太难,先求解出这个数的所有真因子,然后相加,判断是否等于它本身即可.但是,在这个数

  • java中继承测试代码分析

    继承:可以基于已经存在的类构造一个新类.继承已经存在的类就可以复用这些类的方法和域.在此基础上,可以添加新的方法和域,从而扩充了类的功能. public class ExtendsStu { /*动物类:动物都可以动 * 1.Dog 2.Cat * 在java中,子类可以继承父类的属性和功能; * 继承关系的指定: 子类 extends 父类 * 不能被继承的资源: * 1.子类不能继承父类的构造方法,而且必须调用一个父类的构造器(因为生成子类对象的时候会初始化父类属性) * 2.私有的资源不能

  • java编程求二叉树最大路径问题代码分析

    题目: Binary Tree Maximum Path Sum Given a binary tree, find the maximum path sum. The path may start and end at any node in the tree. For example: Given the below binary tree, 1 / \ 2 3 Return 6. 节点可能为负数,寻找一条最路径使得所经过节点和最大.路径可以开始和结束于任何节点但是不能走回头路. 这道题虽然

  • Python中eval带来的潜在风险代码分析

    0x00 前言 eval是Python用于执行python表达式的一个内置函数,使用eval,可以很方便的将字符串动态执行.比如下列代码: >>> eval("1+2") >>> eval("[x for x in range(10)]") [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 当内存中的内置模块含有os的话,eval同样可以做到命令执行: >>> import os >>&g

随机推荐