首页 > 其他分享 >线性表——双链表

线性表——双链表

时间:2024-08-10 17:54:37浏览次数:8  
标签:head 线性表 next 链表 DoublyListNode 双链 节点

在Java中,双链表(Doubly Linked List)是一种常见的数据结构,它允许从两端进行元素的插入和删除操作。

与单链表相比,双链表的每个节点除了存储数据本身外,还包含两个指针:一个指向前一个节点(prev),另一个指向后一个节点(next)。这使得双链表在遍历、插入和删除操作上更加灵活。

双链表提供了比单链表更灵活的操作,特别是在需要频繁地在链表两端进行插入和删除操作时。然而,由于每个节点都需要额外的空间来存储前驱节点的指针,因此双链表在内存使用上会比单链表稍多一些。

1. 节点(Node)的定义(双链表)

在双链表中,每个节点(Node)除了包含存储的数据外,还包含两个引用(或指针):一个指向前一个节点的prev,另一个指向后一个节点的next。这样的设计使得从任一节点都可以向前或向后遍历链表。

class Node<T> {  
    T data; // 存储的数据  
    Node<T> prev; // 指向前一个节点的指针  
    Node<T> next; // 指向后一个节点的指针  
  
    // 构造函数  
    public Node(T data) {  
        this.data = data;  
        this.prev = null;  
        this.next = null;  
    }  
}

2. 双链表的定义

双链表是一种由节点组成的链表,其中每个节点都包含三个部分:数据域、指向前一个节点的指针和指向后一个节点的指针。双链表可以从两端开始遍历,支持在链表的两端快速地进行插入和删除操作。

class DoublyListNode {
    int val;
    DoublyListNode next, prev;
    DoublyListNode(int x) { val = x; }
}

DoublyListNode createDoublyLinkedList(int[] arr) {
    if (arr == null || arr.length == 0) {
        return null;
    }
    DoublyListNode head = new DoublyListNode(arr[0]);
    DoublyListNode cur = head;
    // for 循环迭代创建双链表
    for (int i = 1; i < arr.length; i++) {
        DoublyListNode newNode = new DoublyListNode(arr[i]);
        cur.next = newNode;
        newNode.prev = cur;
        cur = cur.next;
    }
    return head;
}

 

// 创建一条双链表
DoublyListNode head = createDoublyLinkedList(new int[]{1, 2, 3, 4, 5});

// 从头遍历双链表
for (DoublyListNode p = head; p != null; p = p.next) {
    System.out.println(p.val);
}

// 从尾遍历双链表(假设我们有尾节点的引用 tail)
for (DoublyListNode p = tail; p != null; p = p.prev) {
    System.out.println(p.val);
}


// 创建一条双链表
DoublyListNode head = createDoublyLinkedList(new int[]{1, 2, 3, 4, 5});

// 在双链表头部插入新节点 0
DoublyListNode newHead = new DoublyListNode(0);
newHead.next = head;
head.prev = newHead;
head = newHead;
// 现在链表变成了 0 -> 1 -> 2 -> 3 -> 4 -> 5


// 创建一条双链表
DoublyListNode head = createDoublyLinkedList(new int[]{1, 2, 3, 4, 5});

DoublyListNode tail = head;
// 先走到链表的最后一个节点
while (tail.next != null) {
    tail = tail.next;
}

// 在双链表尾部插入新节点 6
DoublyListNode newNode = new DoublyListNode(6);
tail.next = newNode;
newNode.prev = tail;
// 更新尾节点引用
tail = newNode;

// 现在链表变成了 1 -> 2 -> 3 -> 4 -> 5 -> 6


// 创建一条双链表
DoublyListNode head = createDoublyLinkedList(new int[]{1, 2, 3, 4, 5});

// 在第 3 个节点后面插入新节点 66
// 找到第 3 个节点
DoublyListNode p = head;
for (int i = 0; i < 2; i++) {
    p = p.next;
}

// 组装新节点
DoublyListNode newNode = new DoublyListNode(66);
newNode.next = p.next;
newNode.prev = p;

// 插入新节点
p.next.prev = newNode;
p.next = newNode;

// 现在链表变成了 1 -> 2 -> 3 -> 66 -> 4 -> 5


// 创建一条双链表
DoublyListNode head = createDoublyLinkedList(new int[]{1, 2, 3, 4, 5});

// 删除第 4 个节点
// 先找到第 3 个节点
DoublyListNode p = head;
for (int i = 0; i < 2; i++) {
    p = p.next;
}

// 现在 p 指向第 3 个节点,我们它后面那个节点摘除出去
DoublyListNode toDelete = p.next;

// 把 toDelete 从链表中摘除
p.next = toDelete.next;
toDelete.next.prev = p;

// 把 toDelete 的前后指针都置为 null 是个好习惯(可选)
toDelete.next = null;
toDelete.prev = null;

// 现在链表变成了 1 -> 2 -> 3 -> 5


// 创建一条双链表
DoublyListNode head = createDoublyLinkedList(new int[]{1, 2, 3, 4, 5});

// 删除头结点
DoublyListNode toDelete = head;
head = head.next;
head.prev = null;

// 清理已删除节点的指针
toDelete.next = null;

// 现在链表变成了 2 -> 3 -> 4 -> 5


// 创建一条双链表
DoublyListNode head = createDoublyLinkedList(new int[]{1, 2, 3, 4, 5});

// 删除尾节点
DoublyListNode p = head;
// 找到尾结点
while (p.next != null) {
    p = p.next;
}

// 现在 p 指向尾节点
// 把尾节点从链表中摘除
p.prev.next = null;

// 把被删结点的指针都断开是个好习惯(可选)
p.prev = null;

// 现在链表变成了 1 -> 2 -> 3 -> 4

3. 双链表的基本操作

  • 添加(Append):在链表末尾添加一个新节点。
  • 添加至头部(Prepend):在链表头部添加一个新节点。
  • 删除:删除链表中的指定节点。
  • 查找:根据数据查找链表中的节点。
  • 遍历:从头节点或尾节点开始遍历整个链表。
  • 反转:将链表中的节点顺序反转。

4. 优点和缺点

优点

  1. 灵活性:可以从链表的两端进行插入和删除操作,这使得双链表在某些应用场景下(如队列和栈的混合操作)非常有用。
  2. 双向遍历:由于每个节点都包含指向前一个节点的指针,因此可以方便地向前遍历链表,这在某些算法中非常有用。
  3. 空间效率:与数组相比,双链表在插入和删除节点时不需要移动其他元素,因此具有更好的空间效率。

缺点

  1. 内存使用:每个节点都需要额外的空间来存储指向前一个节点的指针,这增加了内存的消耗。
  2. 缓存效率:由于链表中的节点在内存中通常不是连续存储的,这可能导致较差的缓存效率,尤其是在遍历链表时。
  3. 查找效率:与数组相比,链表的查找效率较低,因为需要从头节点开始逐个遍历节点。尽管双链表提供了双向遍历的能力,但这并不改变其查找效率的基本性质。

标签:head,线性表,next,链表,DoublyListNode,双链,节点
From: https://blog.csdn.net/m0_74051652/article/details/141094059

相关文章

  • 线性表
    目录1.顺序表1.1数组的插入删除操作1.2修改为last版本1.3顺序表的相关操作1.4顺序表总结:2.链表2.1单向链表2.1.1分类1)有头单向链表2)无头单向链表2.1.2操作函数1)创建一个空的(有头)单向链表2)向post位置插入一个数据3)删除指定位置的数据4)单向链表的转置2.2单向循......
  • 【算法】【线性表】【链表】LRU 缓存2
    1 题目请你设计并实现一个满足  LRU(最近最少使用)缓存 约束的数据结构。实现 LRUCache 类:LRUCache(intcapacity) 以 正整数 作为容量 capacity 初始化LRU缓存intget(intkey) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。voidput(......
  • 线性表
    P5727#include<cstdio>#include<vector>usingstd::vector;intmain(){ intn;scanf("%d",&n); vector<int>ans;//定义一个里面元素是int类型的动态数组 //一般不用vector<char>vector<bool> //此时是没有ans[0]...的 while(n!=1){ ......
  • 创建一个简单的双链表
    1.ListNode.h头文件#pragmaonce#include<stdio.h>#include<stdlib.h>#include<assert.h>#include<string.h>typedefintLTDataType;typedefstructListNode{ structListNode*next; structListNode*prev; LTDataTypedata;}LN;//初始化......
  • 线性表之--栈和队列
    1. 栈的表示和实现1.1栈的概念及结构栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(LastInFirstOut)的原则。压栈:栈的插入操作叫做进栈/压......
  • 408 数据结构线性表算法
    第一章线性表定义:线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列。线性表的表示:若用L命名,表示:L=(a1,a2,a3,a4,a5,……,an)线性表的逻辑特性:a1:唯一的表头元素an:唯一的表尾元素除去a1:每个元素有且仅有一个直接前驱除去an:每个元素有且仅有一个直接后继......
  • 数据结构-线性表
    目录王道章节内容知识框架考纲内容引入方法1:顺序存储结构的直接表示方法2:顺序存储结构表示非0项方法3:链表结构存储非零项线性表定义线性表的主要操作(ADT)顺序存储结构定义结构代码实现操作及实现初始化获得查找插入删除链式存储结构单链表定义结构代码......
  • 双链DNA的横截面积是多少?
    我希望确定具有不同碱基(10至150)的双链DNA的持久长度。我找到了一个方程:P=B_s/KT其中B_s是弯曲刚度B_s=E*I其中I是横截面积所以,我只需要确定双链DNA的横截面。另外,以前有人在Python上使用过oxDNA吗?我也想确定oxDNA上的持久长度,以便能够比较这些......
  • 算法入门篇(三)之线性表
    目录一.顺序表1、静态顺序表2、动态顺序表3、顺序表的操作二.单链表、双向链表、循环链表、静态链表1、单链表2、双向链表3、循环链表4、静态链表三.UVA101、UVA11988、UVA126571.UVA101:木块问题(TheBlocksProblem)2.UVA11988:破损的键盘(BrokenKeyboard)......
  • 数据结构:线性表的应用
    文章目录1.线性表的合并2.有序表的合并1.线性表的合并问题描述:假设利用两个线性表La和Lb分别表示两个集合A和B,现要求一个新的集合A=AUBLa=(7,5,3,11)Lb=(2,6,3)->La=(7,5,3,11,2,6)算法步骤依次取出Lb中的每个元素,执行以下操作:在La中查找该元素如果找不到,则将......