双向链表(一)

本文介绍了双向链表的基本概念,包括Node节点的定义和List类的构建。讲解了如何初始化链表头节点,强调了指针变量初始化的重要性,以及在类中使用typedef简化代码的实践。提供双向链表部分功能的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

双向链表(一)

双向链表是一个常用的数据结构,同时在STL中也占有一席之地,那么实现双向链表是一个很好的练习,那么下面就实现一个最简单的双向链表。
基本设置:

  1. 含有next prev指针,value值的node类。
  2. 包含若干方法的链表类:其中包含双向链表常用的push_back,pop_back,end(),begin功能

Node节点

首先针对Node节点,就是很常规的声明一个结构体,因为节点中的数据我们会在class List中进行初始化,所以不需要写构造函数。同时为了使用的普遍性我们还可以使用泛型进行书写。

template<typename T>
struct Node
{
 T value;
 Node<T>* next;
 Node<T>* prev;
};

类List

再定义一个类的时候,首先要考虑到类中需要的数据成员,以及类中需要定义的方法成员。首先我们分析需要哪一些数据成员。

  • 一个记录链表长度的size_t变量。
  • 首先一个Node类型的数据成员是必须的,我们需要给我们的双向链表添加一个空节点,这样可以通过这个头节点方便的对整个双向链表操作。但是有一点需要考虑的是:我们该创建Node的什么类型变量呢?
    是直接创建还是创建一个指向Node变量的指针呢?好像都可以,那么一会我们就试一试是不是这回事。

数据成员考虑完了后,再来考虑成员函数。首先对于一个链表最重要的就是可以对链表的首尾进行插入删除操作。同时还要有判定首尾位置的函数。

  1. 数据成员与构造函数
protected:
 Node<T>* TailNode;
 int len;
public:
 List() :len(0) {
  //Node<T>* TailNode = new Node<T>;
  TailNode = new Node<T>;
  TailNode->next = TailNode;
  TailNode->prev = TailNode;
 }
 //other codes

观察此构造函数,发现在数据成员声明之后构造函数对其进行了动态内存分配。这涉及到一个知识点:在对象创建之后才会分配数据成员的内存空间
之前的定义只是指明这个数据类型占多大的空间。 创建类实例对象的时候,自动调用构造函数,这时候通过构造函数把值传递给对象的数据成员。接下来观察构造函数:

  • 第一行中有一个被注释掉的句子,对比和下面的内容,除了数据类型外其他完全一样。但这两条语句有一个很大的区别。
    Node<T>* TailNode = new Node<T>;
    此语句动态分配了一个新内存空间,通过Node<T>声明这个指针指向的数据类型,这是一个局部变量(作用域为构造函数内,但他的生存期会持续到delete为止,实际上根据本体设置它会一直存在),只能在构造函数内使用,一旦超过构造函数的作用域,会自动被此例中同名数据成员所屏蔽。
  • 真正的new语句——核心目的:初始化指针
    注释掉的那一行是一个很典型的错误思路,真正正确的是下面的这一句。我们对数据成员动态分配一块内存。在动态分配内存之前,创建对象给对应的数据成员分配了一个地址,里面的地址为随机值,对于值类型来说我们不需要对内存进行分配了,因为编译器已经帮助我们分配好了空间。而对于这个指针变量,问题就不一样了。指针在分配之后指向了一块未知的内存,这块内存可能正在执行,可能存储了其他的数据或者别的什么事情,直接对指向的内存进行操作会产生不知道会发生什么的惊喜,也即未定义行为。所以我们必须初始化指针。
    说了这么多,这个语句就是用来初始化指针的。
  1. 成员函数与别名表
public:
 typedef Node<T>* link_type;
 link_type begin() {
  return TailNode->next;
 }
 link_type end() {
  return TailNode;//要不要有prev...
 }
 //position is null,as the row 19
 link_type insert(link_type position, T& val) {
  //Node<T>* newnode;
  Node<T>* temp = new Node<T>;
  temp->value = val;
  temp->prev = position->prev;
  temp->next = position;  
  position->prev->next = temp;
  position->prev = temp; 
  ++len;
  return temp;
 }
 void push_back(T tx) {
  insert(end(), tx);
 }

这里面的数据类型表使用了typedef语句给比较长的变量名Node<T>*起了别名,方便我们使用。这样就方便了许多。本质上这样起到了一个简化代码的作用。

结尾注:

对于这个练习系列来说,只需要理解到这个东西要怎么写并实现就ok了,当然也是因为我写完了一个完整的了,再次重新练习的时候就不需要把功能全部实现一遍了。这一套的系列就是一步步的从没有迭代器到拥有迭代器,同时也实现了一个单项链表。这里附上代码:
双向链表部分功能的实现
单向链表部分功能的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值