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 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 硬核数据结构——链表(Javescripts) -> 正文阅读

[数据结构与算法]硬核数据结构——链表(Javescripts)

1.链表的基本概念

1.链表存储有序的元素集合,不同于数组的是链表中的元素在内存中并不是连续放置的,每一个元素由一个存储元素本身的节点和指向下一个元素的指针组成。
2.链表的优势:添加或者删除元素时,不需要移动其他元素
3.数组与链表访问元素不同点:数组拿到索引可以直接访问数组中的元素,链表想要访问一个元素时,需要从表头(起点)开始迭代直到找到这个元素。
4.链表一般分为单向链表,双向链表,环形链表

2.双向链表以及环形链表

1.单向链表:元素由一个存储元素本身的节点和指向下一个元素的指针所组成,如下图所示:

链表结构
2.双向链表:与单向链表不同的是,除了一个存储元素本身的节点外,在双向链表中,链接是双向的,一个链向下一个元素,另一个链向前一个元素。如下图所示:

在这里插入图片描述

3.循环链表:可以像单向链表一样只有单向引用,也可以像双向链表一样有双向引用,循环链表和链表唯一的区别就是,最后的一个元素指向下一个元素的指针不同,链表最后一个元素指针指向null,环形链表最后一个元素指针指向第一个元素或者其他元素。如下图所示:

在这里插入图片描述

3.链表数据结构实现

export default class LinkedList<T> {
  protected count = 0;
  protected head: Node<T> | undefined;

  constructor(protected equalsFn: IEqualsFunction<T> = defaultEquals) {}

  push(element: T) {
    const node = new Node(element);
    let current;

    if (this.head == null) {
      // catches null && undefined
      this.head = node;
    } else {
      current = this.head;

      while (current.next != null) {
        current = current.next;
      }

      current.next = node;
    }
    this.count++;
  }

  getElementAt(index: number) {
    if (index >= 0 && index <= this.count) {
      let node = this.head;
      for (let i = 0; i < index && node != null; i++) {
        node = node.next;
      }
      return node;
    }
    return undefined;
  }

  insert(element: T, index: number) {
    if (index >= 0 && index <= this.count) {
      const node = new Node(element);

      if (index === 0) {
        const current = this.head;
        node.next = current;
        this.head = node;
      } else {
        const previous = this.getElementAt(index - 1);
        node.next = previous.next;
        previous.next = node;
      }
      this.count++;
      return true;
    }
    return false;
  }

  removeAt(index: number) {
    if (index >= 0 && index < this.count) {
      let current = this.head;

      if (index === 0) {
        this.head = current.next;
      } else {
        const previous = this.getElementAt(index - 1);
        current = previous.next;
        previous.next = current.next;
      }
      this.count--;
      return current.element;
    }
    return undefined;
  }

  remove(element: T) {
    const index = this.indexOf(element);
    return this.removeAt(index);
  }

  indexOf(element: T) {
    let current = this.head;

    for (let i = 0; i < this.size() && current != null; i++) {
      if (this.equalsFn(element, current.element)) {
        return i;
      }
      current = current.next;
    }

    return -1;
  }

  isEmpty() {
    return this.size() === 0;
  }

  size() {
    return this.count;
  }

  getHead() {
    return this.head;
  }

  clear() {
    this.head = undefined;
    this.count = 0;
  }

  toString() {
    if (this.head == null) {
      return '';
    }
    let objString = `${this.head.element}`;
    let current = this.head.next;
    for (let i = 1; i < this.size() && current != null; i++) {
      objString = `${objString},${current.element}`;
      current = current.next;
    }
    return objString;
  }
}
export class Node<T> {
  constructor(public element: T, public next?: Node<T>) {}
}

export class DoublyNode<T> extends Node<T> {
  constructor(
    public element: T,
    public next?: DoublyNode<T>,
    public prev?: DoublyNode<T>
  ) {
    super(element, next);
  }
}

export function defaultEquals<T>(a: T, b: T): boolean {
  return a === b;
}
export type IEqualsFunction<T> = (a: T, b: T) => boolean;

需要添加一个Node辅助类,Node表示链表中的项,包含element属性,即添加到该项的值,以及一个next属性,下一个节点的指针,prev双向链表的新增指针(稍后解释)
push() 向列表尾部增加一个新的项
getElementAt() 获取当前链表中的尾项
insert() 向链表的特定位置添加一个新的项
removeAt() 向链表的特定位置移除一个项
remove() 链表移除一个项,头部移出(首位)
indexOf() 获取链表中的某项
isEmpty() 判断链表中是否空
size() 链表的长度
getHead() 获得链表的头部(首位),
clear() 清除链表
toString() 将链表转为一个字符串输出

4.链表是否有环

针对链表问题,我们经常会遇到判断一个链表是否有环,这个在面试中出现的频率也比较高,接下来简单说说
最常用的方式就是快慢指针,在链表的头节点分别放两个指针,一个指针慢(a表示),一个指针快(b表示),a每次移动一个位置,b移动两个位置,如果相遇则说明有环,如果不相遇则说明无环。

问:链表再有环的情况下如何能够知道链表的环入口点?

在这里插入图片描述
假设D为快慢指针相遇点
假设链表头部(A)到环口(B)的距离为 a
假设链表环口(B)到快慢指针的相遇点(D)的距离为 b
假设环的长度为 c
快指针总距离:d(fast) = a + b + c
慢指针总距离:d(slow) = a + b
因为快指针的速度是慢指针的两倍,相同时间,因此2倍的慢指针总距离等于快指针总距离,则:2*d(slow)=d(fast)
得:a=c-b
可以得到结论:链表头、相遇点各设一个指针,指针每次都各走一步,两个指针必定相遇,且相遇第一点为环入口点
代码如下:

var LinkedListCycle = function (head) {
  let p1 = head; // 慢指针
  let p2 = head; // 快指针
  while (p1 && p2 && p2.next) {
    p1 = p1.next;
    p2 = p2.next.next;
    if (p1 === p2) { // 是否有环
      p2 = head; //重新从开始找,每次一步,再相遇就是入口
      while (p2 != p1) {
        p1 = p1.next;
        p1 = p1.next;
      }
      return fast;
    }
  }
  return null; // 当循环结束不重复,则没环
};

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-21 15:43:11  更:2021-08-21 15:45:47 
 
开发: 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/25 23:47:08-

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