【链表】双向链表

一开始我就觉得双向链表非常非常的麻烦,但是老师出了一道上机题。debug了好久才过(博主比较菜还请大家见谅),所以贴出来共享下。

链表比较麻烦的就是插入和删除操作。一定要先弄懂指针到底是指向哪里的。

我们首先用结构体struct 定义了node,node中包括了data(这个结点存的数据),*prev(指向上一个节点的指针)和*next(指向下一个节点的指针)

我在构造函数中首先新建了头尾结点,于是插入删除操作可以避免边界的讨论。

插入时,我们假定要在A结点和B结点中插入C结点,那么首先我们要新建一个节点(我用的是new),(假设我们用temp指针指向这个新的结点)然后我们把要存的元素复制给data,所以我们让temp -> data(令temp指针指向data),在把元素复制给data。 在c++ 中就是 entry(我们要存的元素) = temp -> data。然后我们令temp -> next = B, temp -> prev = A,那么C的下一个节点就是B,上一个节点就是A。

然后我们要把c结点插入A结点和B结点之间,所以我们用setPosition函数让*current 指向A结点, 那么current -> next 就会指向B结点。这时候我们要注意赋值的顺序。我们首先把current -> next -> prev = C(表示A结点的下一个节点的上一个节点,也就是B结点的上一个节点),于是B结点的prev就会指向C结点,所以B结点的上一个就会变成C结点。同理我们current -> next = C,把A的下一个节点变成了C,于是我们就完成插入插入操作。// 一定要注意current的赋值顺序。

删除操作同理,首先用setPosition找到要删除的结点。这是我们要定义多一个临时的指针temp来存这个结点(否则如果我们没有delete的话,这个结点没有指针指向它,会变成内存垃圾,如果我们delete current的话会整个链表就没有指针可以访问了,所以我们在完成操作之后要delete temp来避免这些问题)如果怕删除出错的话可以定义*p = current -> prev, *q = current -> next,用p,q指针分别指向要删除结点的前一个结点和后一个结点。然后我们让p -> next = q, q -> prev = p就可以了,如果嫌这个麻烦也可以直接 current -> next -> prev = current -> prev; current -> prev -> next = current -> next; 最后我们可以让current = current -> next  或者current = current -> prev,最后delete掉temp,完成删除操作。

这份代码的函数是老师给定好的,private用了current指针而没有用head 和 而且加了curPosition. (因为如果用head的话速度会慢一点,因为插入等函数要从head开始遍历,所以每次遍历到当前的position需要 position - 1次,而如果用curPosition的话就会快一点// 因为current函数到Posiiton的距离一定比head 到Position的距离短 //  如果是连续插入的话,那么curPosition到下一个节点的距离为1,而head到position的距离就是 position -1 所以用current会快)

还有就是由于有insert(插入),和retrieve(获得position位置的元素)函数,所以我在深复制的时候直接用了insert和retrieve。

以下是代码

#include <iostream>
#include <cstdlib>
using namespace std;
enum Error_code
{
         success,
         underflow,
         overflow
};
template <class List_entry>
struct Node
{
         List_entry entry;
         Node<List_entry> *next;
         Node<List_entry> *back;
};
template <class List_entry>
class MyList
{
public:
         MyList() {
           count =  0;
           curPosition = -1;
           current = new Node<List_entry>;// head
           current -> entry = -1;
           current -> next = new Node<List_entry>; // tail
           current -> next -> entry = -1;
           current -> back = NULL;
           current -> next -> back = current;
           current -> next -> next = NULL;
         }
         ~MyList() {
            clear();
            Node<List_entry>*p = current, *q = current -> next; // 头结点和尾结点不能在clear里面删除,否则会出现clear后在insert的时候就没有头尾结点了
            delete p;
            delete q;
            current = NULL;
         }
         // 拷贝构造函数和赋值运算符重载,注意深拷贝与浅拷贝的差异
         MyList(const MyList<List_entry> &copy) {
           count =  0;
           curPosition = -1;
           current = new Node<List_entry>;// head
           current -> entry = -1;
           current -> next = new Node<List_entry>; // tail
           current -> next -> entry = -1;
           current -> back = NULL;
           current -> next -> back = current;
           current -> next -> next = NULL;
           List_entry entry;
           while (count < copy.size()) {
             copy.retrieve(count, entry);
             insert(count, entry);
           }
           setPosition(copy.curPosition);
         }
         void operator =(const MyList<List_entry> &copy) {
           count =  0;
           curPosition = -1;
           current = new Node<List_entry>;// head
           current -> entry = -1;
           current -> next = new Node<List_entry>; // tail
           current -> next -> entry = -1;
           current -> back = NULL;
           current -> next -> back = current;
           current -> next -> next = NULL;
           List_entry entry;
           while (count < copy.size()) {
             copy.retrieve(count, entry);
             insert(count, entry);
           }
           setPosition(copy.curPosition);
         }
         // 清空list
         void clear() {
           List_entry entry;
           while (size()) {
             remove(count - 1,entry);
           }
           count = 0;
         }
         // 判断list是否为空
         bool empty() const {
           return (count == 0) ? 1 : 0;
         }
         // 判断list是否已满
         bool full() const {
           return false;
         }
         // 获取list的元素数量
         int size() const {
           return count;
         }
         // 在第position个位置插入值为entry的元素,如果position为0则插入在链表头,依次类推
         // 若position < 0 或者 position > count,则返回underflow
         Error_code insert(int position, const List_entry &entry) {
           if (position < 0 || position > count) return underflow;
           Node<List_entry>*p = new Node<List_entry>;
           p -> entry = entry;
           setPosition(position - 1);
           p -> next = current -> next;
           p -> back = current;
           current -> next -> back = p;
           current -> next = p;
           count++;
           return success;
         }
         // 删除第position个位置的元素,并将该元素的值保存在entry中
         // 若position < 0 或者 position >= count,则返回underflow
         Error_code remove(int position, List_entry &entry) {
           if (position < 0 || position >= count) return underflow;
           setPosition(position);
           Node<List_entry> *p = current;
           entry = p -> entry;
           p -> next -> back = p -> back;
           p -> back -> next = p -> next;
           current = current -> next;
           delete p;
           count--;
           return success;
         }
         // 获取第position个位置的元素,保存在entry中
         // 若position < 0 或者 position >= count,则返回underflow
         Error_code retrieve(int position, List_entry &entry) const {
           if (position < 0 || position >= count) return underflow;
           setPosition(position);
           entry = current -> entry;
           return success;
         }
         // 将第position个位置的元素替换为entry
         // 若position < 0 或者 position >= count,则返回underflow
         Error_code replace(int position, const List_entry &entry) {
           if (position < 0 || position >= count) return underflow;
           setPosition(position);
           current -> entry = entry;
           return success;
         }
         // 用visit函数遍历list内所有的元素
         void traverse(void (*visit)(List_entry &)) {
           for (int i = 0; i < size(); i++) {
             setPosition(i);
             visit(current -> entry);
           }
         }
protected:
         int count;                                                                          // 记录list内元素数量
         mutable int curPosition;                                   // current指针的位置编号
         mutable Node<List_entry> *current;                 // current指针
         // 设置current指针的位置,指向第position个位置
         void setPosition(int position) const {
           if (position > curPosition)
             for (; curPosition < position; curPosition++) current = current -> next;
           else if (position < curPosition)
             for (; curPosition > position; curPosition--) current = current -> back;
         }
};

链表比较复杂,所以博主啰嗦了一些,如果各位有更好的方法,还请指导,谢谢大家了。

时间: 2024-11-10 11:29:31

【链表】双向链表的相关文章

「C语言」单链表/双向链表的建立/遍历/插入/删除

最近临近期末的C语言课程设计比平时练习作业一下难了不止一个档次,第一次接触到了C语言的框架开发,了解了View(界面层).Service(业务逻辑层).Persistence(持久化层)的分离和耦合,一种面向过程的MVC的感觉. 而这一切的基础就在于对链表的创建.删除.输出.写入文件.从文件读出...... 本篇文章在于巩固链表的基础知识(整理自<C语言程序设计教程--人民邮电出版社>第十章),只对链表的概念及增删改查作出探讨,欢迎指教. 一.链表结构和静态/动态链表 二.单链表的建立与遍历

数据结构 -- 链表&amp;双向链表

链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域. 使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理.但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大.链表最明显的好处就是,常规数组排列关

数据结构---基本数据结构---链表---双向链表

1.动态集合 2.每个元素都是一个对象,每个对象中都有一个关键字key和两个指针pre.next,对象中还可以包含其他 卫星数据: 3.若一个元素为x,x.pre=NIL,则该元素为链表的第一个元素,称为  链表的头:   若一个元素为x,x.next=NIL,则该元素为链表的最后一个元素,称为  链表的尾:   若一个元素为x,属性x.head指向链表的第一个元素: 4.图解: 原文地址:https://www.cnblogs.com/anpeiyong/p/10249653.html

Linux内核数据结构——链表

目录 目录 简介 单向链表 双向链表 环形链表 Linux内核中的链表实现 offsetof container_of container_of 第一部分 container_of 第二部分 链表初始化 向链表中增加一个节点 删除节点 移动节点 判断链表是否为空 遍历链表 Demo测试 mlisth mlistc 执行结果 简介 最近在学习Android Binder驱动程序实现的时候,发现里面的数据结构用到了struct list_head.而我google发现struct list_head

韦到头打印链表

题目描述 输入一个链表,从尾到头打印链表每个节点的值. 输入描述: 输入为链表的表头 输出描述: 输出为需要打印的“新链表”的表头 /** *    public class ListNode { *        int val; *        ListNode next = null; * *        ListNode(int val) { *            this.val = val; *        } *    } * */ import java.util.Ar

链表相关

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer).由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1). 链表类型    单向链表 双向链表 循环链表 块状链表 其它扩展 这里以单项链表为例: (取自维基百科) 实现单向链表的初始化,添

链表的插入操作总结

链表是一种经常使用的数据结构,有单链表, 双向链表及其循环链表之分. 插入操作是链表的基本操作之中的一个.但大部分人在初学时,多少会感到有些迷惑. 以下时本人的一些小经验. 1 后向插入和前向插入 如果当前节点为P. 后向插入是指在p节点后插入新节点. 前向插入是指在p节点后插入新节点. 对于单链表而言,仅仅有后向插入. 2 基本规律 1) 先保存原链表结构不变,即先改动新节点的前后指针,然后再先远后近. 2) 先远后近是指先改动离p节点远的指针,在改动离它近的指针. 3 链表操作示意图 下图是

大话数据结构(八)Java程序——双向链表的实现

线性链表--双向链表 双向链表定义: 双向链表(double linked list): 是在单表单的每个结点中,再设置一个指向前驱结点的指针域.因此,在双向链表中的结点都有两个指针域,一个指向前驱,一个指向后继. 双向链表的存储结构 typedef struts DulNode{ Element data; Struct DulNode *prior;前驱指针 Struct DulNode *next;后继指针 }DulDouble, *DulLinkList; 双向链表的插入与删除 双向链表

objective-c算法详解(一、链表)

链表的介绍: 链表可以说是一种最为基础的数据结构.在维护集合数据的时候拥有很大的帮助,尤其是在增,删上拥有很大的效率今天总结一下曾经学习的链表. 在ios的开发中,掌握一些常用的算法可以帮助我们更加有效率的开发. 链表的总类: 单链表 双向链表 循环链表 今天先来讲述下单链表:元素之间由一个单独的指针链接.这种结构的链表允许从第一个元素开始遍历到最后一个元素. 各元素之间通过一个指针连接起来而组成.每个元素包含两个部分:数据成员和一个被称为next的指针.通过这好种结构,将每个元素的next指针

第三章 线性表---链式存储结构(双向链表)

双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域.结点都有两个指针域,一个指向直接后继,另一个指向直接前驱. 实例: 链表--双向链表 现在分析添加的情况已经有1号英雄和5号英雄,现在要添加3号英雄此时cur指向了1号英雄,hero指向3号英雄cur指向1号英雄,发现cur的下一个是5号英雄,大于要添加的3号英雄分析图过程 (1)让3号英雄指向5号英雄,即把这个①号线搭起来$hero->next=$cur->next; //$cur指向1