IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 链表及双向链表 -> 正文阅读

[数据结构与算法]链表及双向链表

链表及双向链表

这里暂时不谈环形链表(circular linked list)。

链表

链表是一个非常有用的结构,与数组不同,它并不要求数据结构的内存地址保存在一起,而是可以通过指针/引用的方式,获取下一个结点的位置,因此,链表结构可以将散落的数据串联在一起,更大程度的利用空间,减少数据的重排。

链表结构如下:

1
2
3
4
null

每一个节点包含当前的值,以及下一个结点的引用。结点代码如下:

class Node {
  constructor(val, next) {
    this.val = val !== undefined ? val : 0;
    this.next = next ? next : null;
  }
}

链表本身的实现就比较简单了,下面实现一些比较基础的功能。

首先是 LinkedList 的基础结构:

class LinkedList {
  constructor() {
    this.head = null;
  }

  print() {
    let l1 = this.head;
    let res = [];

    while (l1?.next) {
      res.push(l1?.val, "->");
      l1 = l1.next;
    }

    res.push(l1?.val);

    console.log(res.join(" "));
  }
}

这里简单的实现了一个 print 功能,直接打印会出现 circular object 这种情况。

此时初始化后 LinkedList 会输出一个空字符串:

linkedlist initialized

  • appendToHead

    这个方法的目的是新建一个 Node,并且将这个 Node 加到链表的头部,其实现如下:

    1. 新建一个 Node:

      0
      1
      2
      3
      4
      head
    2. 将新建结点的 next 指向原有的 head

      0
      1
      2
      3
      4
      head
    3. 将原有的 head 重置为现在的 head

      0
      1
      2
      3
      4
      head

    代码实现如下:

    class LinkedList {
      appendToHead(node) {
        const newNode = node instanceof Node ? node : new Node(node);
    
        if (!this.head) return (this.head = newNode);
    
        newNode.next = this.head;
        this.head = newNode;
      }
    }
    

    测试结果如下:

    test appendToHead

  • appendToTail

    appendToTail 的实现与 appendToHead 相似,只不过这一次不是修改 this.head,而是将新建的结点连到当前链表最后一个结点后。其逻辑如下:

    1. 新建一个 Node:

      1
      2
      3
      4
      5
      head
    2. 让当前链表中最后一个元素的 next 指向新建的结点

      1
      2
      3
      4
      5
      head

    这里主要的问题在于,链表无法像数组一样只靠下标就能过获取当前结点,因此这里需要依靠迭代去获取最后一个结点的位置。当然,也可以使用 this.tail 去保存最后一个结点,不过这会增加一些实现的复杂度,而且能实现 head 也能实现 tail,这里就偷懒了。

    实现如下:

    class LinkedList {
      appendToTail(node) {
        const newNode = node instanceof Node ? node : new Node(node);
    
        if (!this.head) return (this.head = newNode);
    
        let l = this.head;
        while (l.next) {
          l = l.next;
        }
    
        l.next = newNode;
      }
    }
    

    测试结果如下:

    test appendToTail

  • appendAfter

    这里就是实现将新建的结点连到某个结点之后,是 appendToHeadappendToTail 的结合了。

    1. 新建一个 Node:

      1
      2
      3
      5
      7
      head
    2. prevNodenext 指向 新创立的结点

      1
      2
      3
      5
      7
      head
    3. 将新建的结点的 next 指向 prevNodenext

      1
      2
      3
      5
      7
      head

    实现代码如下:

    class LinkedList {
      appendAfter(prevNode, node) {
        const newNode = node instanceof Node ? node : new Node(node);
    
        // 可以抛错,这里就直接返回了
        if (!prevNode) return;
    
        const next = prevNode.next;
    
        prevNode.next = newNode;
        newNode.next = next;
      }
    }
    

    测试结果如下:

    test appendAfter

双向链表

双向链表和普通链表的结构其实差不多,只不过在每个结点上会多使用一个空间去保存上一个节点的指针/引用,大体结构如下:

1
2
3
4
5

这样就能够解决向前追溯的问题,比如说 146. LRU Cache

这里就直接贴一下实现了:

class Node {
  constructor(val) {
    this.val = val;
    this.prev = null;
    this.next = null;
  }
}

class DLL {
  constructor() {
    this.head = null;
    this.tail = null;
  }

  print() {
    let l1 = this.head;
    let res = [];

    while (l1?.next) {
      res.push(l1?.val, "<->");
      l1 = l1.next;
    }

    res.push(l1?.val);

    console.log(res.join(" "));
  }

  appendToHead(node) {
    const newNode = node instanceof Node ? node : new Node(node);
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;

      return;
    }

    newNode.next = this.head;
    this.head.prev = newNode;
    this.head = newNode;
  }

  appendToTail(node) {
    const newNode = node instanceof Node ? node : new Node(node);
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;

      return;
    }

    newNode.prev = this.tail;
    this.tail.next = newNode;
    this.tail = newNode;
  }

  moveToHead(node) {
    if (node === this.head) return ;

    const prev = node.prev, next = node.next;
    prev.next = next;
    if (next) next.prev = prev;
    if (node === this.tail) this.tail = prev;

    this.head.prev = node;
    node.next = this.head;
    this.head = node;
  }

  removeTail() {
    if (this.head === this.tail) {
      this.head = null;
      this.tail = null;
      return ;
    }

    const prev = this.tail.prev;
    prev.next = null;
    this.tail = prev;
  }
}

const node5 = new Node(5);
const node10 = new Node(10);

const dll = new DLL();

dll.appendToHead(7);
dll.appendToHead(node5);

dll.print();

dll.appendToTail(8);
dll.appendToTail(node10);

dll.print();

dll.moveToHead(node5)
dll.moveToHead(node10)
dll.moveToHead(node5)
dll.print()

dll.removeTail();
dll.print()

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-06-21 21:32:04  更:2022-06-21 21:32:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:46:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码