JS数据结构(5)——单向链表

本文深入探讨了JavaScript中链表数据结构的实现与操作,对比数组,解析链表优势与劣势,提供链表类的代码实现及多种链表操作示例。

JS数据结构(5)——单向链表

链表和数组的区别:

数组的优缺点:
  • 数组是最常用的数据结构,它用来存储多个元素。
  • 几乎每一种编程语言中都有默认实现数组结构,并且有很多关于数组的操作方法。
  • 数组的修改和查找操作,时间复杂度低。
  • 数组的创建通常需要申请一段连续的内存空间,并且大小是固定的,所以当当前的数组不能满足容量需求的时候,就需要扩容(一般情况下会申请一个更大的数组,比如说原数组的2倍,然后将原数组中的元素再复制过去),这对空间造成了大量浪费。
  • 而且在数组开头或者中间插入数据的成本非常高,需要进行大量元素的位移。
  • 虽然现在JavaScript的Array类方法已经可以很方便的使用,但是对空间和时间有很大的浪费,消耗程序的性能。
链表的优势:
  • 要存储多个元素,除了数组另外一个选择就是链表。
  • 但是不同于数组,链表中的元素在内存中不需要一定是连续的空间。
  • 链表的每个元素是由一个存储元素本身和一个指向下一个元素的指针做成。
链表相对于数组的优势:
  • 内存空间不需要是连续的,可以充分利用计算机的内存,实现灵活的动态管理、
  • 链表在创建时不需要确定大小,它的大小可以无限的延伸下去。
  • 链表在插入和删除数据时,时间复杂度可以达到O(1),相对数组效率高了很多。
链表相对于数组的劣势:
  • 链表访问任何一个位置的元素,都必须从头开始访问。
  • 链表无法通过下标值直接访问元素,需要从头开始一个一个访问,直到找到对应的元素。

什么是链表(Linked List)

  • 链表和数组一样,都可以用于存储一系列的元素,但是链表和数组的实现机制完全不同。
  • 链表是一系列的存储数据元素的单元通过指针连接起来而形成的,因此每个节点(node)都至少有两个域,一个域用来存储数据元素,另外一个域用来存放指向其他单元的指针。
  • 链表的第一节点和最后一个节点,分别是链表的头节点(head)和尾节点,尾节点的特征是它的next引用为空(null)。链表中每个节点的next引用都相当于一个指针,指向另一个节点。
链表的结构:

在这里插入图片描述

链表结构的封装

  1. 代码思路:
  • 我们需要封装一个 LinkedList 的类,用于表示链表结构。
  • 在LinkedList类中,需要封装一个内部类用于表示每个节点信息(数据的指向下一个单元的指针)。
  • 链表中我们还需要保存两个属性,一个是链表的长度,另一个是链表的第一个节点。
  • 链表中常见的操作:
    (1)append(data):向链表尾部添加一个新的节点。
    (2)insert(position,data):向链表的指定位置插入一个新的节点。
    (3)remove(element):从链表中移除数据为element的一项
    (4)removeAt(position):从链表中移除指定位置的一项。
    (5)update(position):修改某个位置的元素
    (6)get(position):获取对应位置的元素
    (7)indexOf(element):返回element在列表中的索引值,如果没有返回-1。
    (8)isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。
    (9)size():返回链表包含的元素个数,与length属性类似。
    (10)toString():以String的方式输出链表中元素的值。
  1. 代码实现
function LinkedList() {
  this.head = null;
  // 封装节点node类
  function node(data) {
    this.data = data;
    this.next = null;
  }
  this.length = 0;

  // append(data):向链表尾部添加一个新的节点
  LinkedList.prototype.append = function(data) { 
      // 创建新节点
      var newNode = new node(data);
      // 判断添加的是否是第一个节点
      if(this.length == 0)
        this.head = newNode;
      else {
        var current = this.head;
        while(current.next)
          current = current.next;
        current.next = newNode;
      }
      this.length++;
  }
  // insert(position, data):向链表的指定位置插入一个新的节点
  LinkedList.prototype.insert = function(position, data) {
    // 对position进行越界判断
    if(position < 0 || position > this.length) 
      return false;
    // 创建新节点
    var newNode = new node(data);
    // 判断插入的位置是否是第一个
    if(position == 0){
      newNode.next = this.head;
      this.head = newNode;
    }else {
      var index = 0;
      var current = this.head;
      while(index < position - 1) {
        current = current.next;
        index++;
      }
      newNode.next = current.next;
      current.next = newNode;
    }
    this.length++;
  }
  // get(position):获取对应位置的元素
  LinkedList.prototype.get = function(position) {
    if(position < 0 || position >= this.length)
      return null;
    var current = this.head;
    var index = 0;
    while(index < position) {
      index++;
      current = current.next;
    }
    return current.data;
  }
  // indexOf(element):返回元素在列表中的索引,如果列表中没有则返回-1
  LinkedList.prototype.indexOf = function(element) {
    var current = this.head;
    var index = 0;
    while(current.data !== element){
      current = current.next;
      index ++;
    }
    if(current.data === element)
      return index;
    return -1;
  }
  // update(position, element):修改某个位置的元素
  LinkedList.prototype.updata = function(position, element) {
    if(position < 0 || position > this.length) 
      return null;
    var current = this.head;
    var index = 0 ;
    while(index < position) {
      index ++;
      current = current.next;
    }
    current.data = element;
    return true;
  }
  // removeAt(position):移除链表中位置为position的一项
  LinkedList.prototype.removeAt = function(position) {
    var current = this.head;
    if(position < 0 || position >= this.length) return null;
    if(position === 0) 
      this.head = this.head.next;
    else {
      var index = 0;
      while(index < position - 1) {
        current = current.next;
        index ++;
      }
      current.next = current.next.next;
    }
    this.length --;
    return true;
  }
  // remove(element):移除链表中元素为element的一项
  LinkedList.prototype.remove = function(element) {
    var position = this.indexOf(element);
    return this.removeAt(position);
  }
  // toString():以String的方式输出链表中元素的值
  LinkedList.prototype.toString = function() {
    // 首先获取头节点
    var current = this.head;
    var listString = "";
    // 循环获取后面的节点
    while(current){
      listString += current.data + " ";
      current = current.next;
    }
    return listString;
  }
  // isEmpty():判断链表是否为空,若为空则返回true,否则返回false
  LinkedList.prototype.isEmpty = function() {
    if(this.length === 0)
      return true;
    return false;
  }
  // size():返回链表包含的元素个数
  LinkedList.prototype.size = function() {
    return this.length;
  }
}

// 测试
var list = new LinkedList();

list.append(111);     
list.append(222);
console.log(list.toString());      // 结果为:111 222 

list.insert(1, 333);
console.log(list.toString());     // 结果为:111 333 222
console.log(list.get(1));		  // 结果为:333

console.log(list.indexOf(333));   // 结果为:1
console.log(list.updata(2,999));  // 结果为:true
console.log(list.toString());	  // 结果为:111 333 999 

console.log(list.removeAt(1));	  // 结果为:true
console.log(list.toString());	  // 结果为:111 999 

list.append(444);
list.append(555);
list.append(666);
console.log(list.toString());	  // 结果为:111 999 444 555 666 

list.remove(444);
console.log(list.toString());	  // 结果为:111 999 555 666 

console.log(list.isEmpty());	  // 结果为:false
console.log(list.size());	      // 结果为:4
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值