JavaScript数据结构——链表

前言

嘿,掘友们!今天我们来了解并实现数据结构 —— 链表。

本文主要内容

  • 单向链表
  • 双向链表
  • 循环链表

链表

要存储多个元素,数组可能是最常用的数据结构。但是这种数据结构有一个缺点,数组的大小是固定的,从数组的起点或中间插入或移除的成本很高,因为需要移动元素。

链表存储有序的元素集合,不同于数组的是,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(指针或链接)组成。下图是一个链表的结构。

3461650465188_.pic.jpg

相比数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然而,链表需要使用指针。在数组中,我们可以直接访问任何位置的任何元素,而想要访问链表中间的一个元素,则需要从起点开始迭代链表知道找到所需要的元素。

在实现链表之前,我们先声明和定义属性和方法。

  • 属性
    • count 存储链表中的元素数量
    • head 头指针
    • equalsFn 比较元素是否相等
  • 方法
    • push(element) 向链表尾部添加一个新元素
    • insert(element, index) 向链表的特定位置插入一个新元素
    • getElementAt(index) 返回链表中的特定位置的元素,如果不存在返回undefined
    • remove(element) 从链表中移除一个元素
    • removeAt(index) 从链表的特定位置移除一个元素
    • indexOf(index) 返回元素在链表中的索引,如果链表中没有该元素,返回-1
    • isEmpty() 如果链表中不包含任何元素,返回true,否则返回false
    • size() 返回链表包含的元素个数
    • print() 返回表示整个链表的字符串

定义defaultEquals函数,作为默认的相等性比较函数。

function defaultEquals(a, b) {
  return a === b
} 

单向链表

要表示链表中的元素,我们需要一个助手类,叫做 Node。Node类表示我们想要添加到链表中的项。

class Node {
  constructor(element) {
    this.element = element	// 元素的值
    this.next = undefined	// 下一个元素的指针
  }
} 

创建 LinkedList 类的“骨架”

class LinkedList {
  constructor() {
    this.count = 0
    this.head = undefined
  }
} 

1. 向链表尾部添加元素

在尾部添加元素可能有两种场景:链表为空,添加的是第一个元素;链表不为空,向其尾部添加元素

class LinkedList {
  constructor() { ... }
  push(element) {
    const node = new Node(element)	// 创建Node项
    if(this.head == null) {
      this.head = node
    } else {
      let current = this.head		// 指向链表的current变量
      while (current.next != null) {
        current = current.next
      }
      current.next = node
    }
    this.count++
  }
} 

首先,把 element作为值传入,创建Node项

先实现第一个场景,向空链表添加一个元素,当创建一个 LinkedList 对象时,head会指向 undefined

如果 head 元素为 undefinednull,就意味着在向链表添加第一个元素。因此要做的就是让 head 指向 node 元素。

再来看第二个场景,向一个不为空的链表尾部添加元素。

要在链表的尾部添加元素,就要找到最后一个元素。但我们只有第一个元素的引用,需要循环访问链表,直到最后一项。当 current.next 元素为 undefinednull 时,我们就知道到达链表尾部了。然后让当前元素的 next 指向 node 元素。

this.head == null 相当于 this.head === undefined || this.head === null

current.next !=null 相当于 current.next !== undefined || current.next !== null

2. 循环迭代链表找到目标

循环到目标 index 的代码片段在 LinkedList 类的方法中很常见。将这部分逻辑独立为单独的方法,这样就能在不同的地方复用它。

class LinkedList {
  constructor() { ... }
  push(element) { ... }
  getElementAt(index) {
    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
  }
} 

为了确保我们能迭代链表知道找到一个合法的位置,需要对传入的 index 参数进行合法性验证。如果传入的位置不合法,返回 undefined,因为这个位置在链表中不存在。

然后,初始化 node 变量,该变量会从链表的第一个元素 head 开始,迭代整个链表知道目标 index,结束循环时,node 元素将是 index 位置元素的引用。

3. 从链表中移除元素

我们要实现两种移除元素的方法。第一种是从特定位置移除一个元素(removeAt),第二种是根据元素值移除元素(remove)。

我们先实现第一种移除元素的方法,要移除元素存在两种场景:第一种,移除第一个元素,第二种,移除第一个元素以外的元素。

removeAt(index) {
  if(index >= 0 && index < this.count) {
    if(index === 0) {
      this.head = this.head.next
    } else {
      const previous = this.getElement(index - 1)
      const current = previous.next
      previous.next = current.next
    }
    this.count-- 
    return current.element
  }
  return undefined
} 

先看第一种场景:我们从链表中移除第一个元素。想移除第一个元素,让 head 指向链表的第二个元素就实现了。

再看第二种场景:移除除第一个元素以外的元素。我们获取要删除元素的前一个元素。current 引用要删除的元素。将前一个元素的 next 指向要删除元素的 next,就可以实现了。

移除最后一个元素也通用,previous 引用最后元素的前一个元素,最后一个元素的 next 指向 undefined,那么将 previous.next = undefined,就完成了最后一个元素的移除。

我们再来实现移除元素的第二种方法:根据元素值移除元素(remove)。

remove(element) {
  const index = this.getElement(element)
  return this.removeAt(index)
} 

我们复用前面的两种方法 getElementremoveAt ,就可以实现。

先获取要删除元素的索引࿰

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈希表是一种高效的数据结构,可以用来存储和查找键值对。其中,哈希函数将键映射到一个特定的桶中,每个桶中存储一组键值对。在哈希表中,如果两个键被映射到同一个桶中,就会发生碰撞。为了解决这个问题,可以使用链表法。 链表法是一种解决哈希表碰撞问题的方法。具体来说,对于哈希表中的每个桶,可以使用一个链表来存储所有映射到该桶的键值对。如果发生碰撞,只需要将新的键值对添加到链表的末尾即可。 下面是一个使用链表法实现哈希表的示例代码: ```python class Node: def __init__(self, key, value): self.key = key self.value = value self.next = None class HashTable: def __init__(self, capacity): self.capacity = capacity self.buckets = [None] * capacity def hash_function(self, key): return hash(key) % self.capacity def put(self, key, value): index = self.hash_function(key) node = self.buckets[index] while node: if node.key == key: node.value = value return node = node.next new_node = Node(key, value) new_node.next = self.buckets[index] self.buckets[index] = new_node def get(self, key): index = self.hash_function(key) node = self.buckets[index] while node: if node.key == key: return node.value node = node.next return None def remove(self, key): index = self.hash_function(key) node = self.buckets[index] prev = None while node: if node.key == key: if prev: prev.next = node.next else: self.buckets[index] = node.next return prev = node node = node.next ``` 在这个示例中,我们定义了一个Node类来表示哈希表中的每个节点,每个节点包含一个键、一个值和一个指向下一个节点的指针。我们还定义了一个HashTable类来实现哈希表,其中包含一个桶数组和一些基本的操作方法,如put、get和remove。 在put方法中,我们首先使用哈希函数计算出键的索引,然后遍历桶中的链表,查找该键是否已经存在于哈希表中。如果找到了该键,我们只需要更新其对应的值即可。否则,我们创建一个新的节点,并将其添加到链表的开头。 在get方法中,我们同样使用哈希函数计算出键的索引,然后遍历桶中的链表,查找该键的值。如果找到了该键,我们返回其对应的值。否则,返回None。 在remove方法中,我们首先使用哈希函数计算出键的索引,然后遍历桶中的链表,查找该键。如果找到了该键,我们将其从链表中删除即可。 总的来说,链表法是一种简单且常用的哈希表解决碰撞问题的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值