javascript数据结构与算法一单向链表与双向链表

34次阅读

共计 4317 个字符,预计需要花费 11 分钟才能阅读完成。

线性表(List):零个或者多个数据元素的有限序列。线性表是一个序列,具有一对一的关系,而不能具备一对多的关系。线性表要求有限性,数据类型也要相同。
本文参考的主要是大话数据结构的原理进行理解,用 Javascript 实现线性表的相关操作。

创建单向链表:

// 创建节点
class Node{constructor(element){
        this.element = element;// 数据域
        this.next = undefined;// 指针域
    }
};
// 判断传入的对象与链表的元素是否相等,待会实现 indexOf() 要用到,在这里先写上
function(a,b){return a == b;}; 
// 创建链表
class LinkedList {constructor(equalsFn = defaultEquals) {
    this.equalsFn = equalsFn;
    this.count = 0;
    this.head = undefined;
    }
};

查询操作

判断单向表是否为空

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

计算单向链表的长度

size() {return this.count;}

查询链表的某个元素的位置

indexOf(element) {
    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;
  }

获取第一个元素

getHead() {return this.head;}

获得元素操作

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;
  }

插入操作

尾插法

push(element) {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++;
  }

任意位置插入元素

insert(element, index) {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) {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) {const index = this.indexOf(element);
    return this.removeAt(index);
  }

修改操作

单向链表转为字符串

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;
  }
}

清空单向链表

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

以上就是单向链表的常见操作。

由于单向链表只能从头到尾的遍历,如果查询得是下一节点,单向表时间复杂度为 O(1),但是要查询的是上一节点的话,那么时间复杂度就是 O(n)。如果链表也可以正反遍历的话,那么查询操作的时间复杂度就都是 O(1)啦,这时我们的前辈就提出了双向链表这一神奇的链表。由于双向链表是单向链表的拓展,只是多了一个指针,对于查询操作并没有帮助,所以实现方法还是跟单向链表一样,这里就不多加阐述。

创建双向链表

// 创建节点
class DoublyNode extends Node {constructor(element, next, prev) {super(element, next);// 继承
    this.prev = prev;// 添加前继指针
  }
};
// 初始化双向链表
class DoublyLinkedList extends LinkedList {constructor(equalsFn = defaultEquals) {super(equalsFn);
    this.tail = undefined;// 对链表最后一个元素进行引用
  }

插入操作

尾插法

push(element) {const node = new DoublyNode(element);
    if (this.head == null) {
      this.head = node;
      this.tail = node; // NEW
    } else {
      // attach to the tail node // NEW
      this.tail.next = node;
      node.prev = this.tail;
      this.tail = node;
    }
    this.count++;
  }

任意位置添加元素

insert(element, index) {if (index >= 0 && index <= this.count) {const node = new DoublyNode(element);
      let current = this.head;
      if (index === 0) {if (this.head == null) { // NEW
          this.head = node;
          this.tail = node; // NEW
        } else {
          node.next = this.head;
          this.head.prev = node; // NEW
          this.head = node;
        }
      } else if (index === this.count) { // last item NEW
        current = this.tail;
        current.next = node;
        node.prev = current;
        this.tail = node;
      } else {const previous = this.getElementAt(index - 1);
        current = previous.next;
        node.next = current;
        previous.next = node;
        current.prev = node; // NEW
        node.prev = previous; // NEW
      }
      this.count++;
      return true;
    }
    return false;
  }

删除操作

删除特定位置的元素

removeAt(index) {if (index >= 0 && index < this.count) {
      let current = this.head;
      if (index === 0) {
        this.head = this.head.next;
        // if there is only one item, then we update tail as well //NEW
        if (this.count === 1) {// {2}
          this.tail = undefined;
        } else {this.head.prev = undefined;}
      } else if (index === this.count - 1) {
        // last item //NEW
        current = this.tail;
        this.tail = current.prev;
        this.tail.next = undefined;
      } else {current = this.getElementAt(index);
        const previous = current.prev;
        // link previous with current's next - skip it to remove
        previous.next = current.next;
        current.next.prev = previous; // NEW
      }
      this.count--;
      return current.element;
    }
    return undefined;
  }

查询操作

查询元素的位置

indexOf(element) {
    let current = this.head;
    let index = 0;
    while (current != null) {if (this.equalsFn(element, current.element)) {return index;}
      index++;
      current = current.next;
    }
    return -1;
  }

查询尾部元素

getTail() {return this.tail;}

修改操作

清空双向链表

clear() {super.clear();
    this.tail = undefined;
  }

双向链表对象转为字符串

inverseToString() {if (this.tail == null) {return '';}
    let objString = `${this.tail.element}`;
    let previous = this.tail.prev;
    while (previous != null) {objString = `${objString},${previous.element}`;
      previous = previous.prev;
    }
    return objString;
  }
}

正文完
 0