js数据结构第五弹:链表

1.数组的缺点

JavaScript 中数组的主要问题是, 它们被实现成了对象, 与其他语言(比如 C++ 和 Java)的数组相比, 效率很低

2. 定义链表

链表:由一组节点组成的集合,每个节点都使用一个对象的引用指向它的后继, 指向另一个节点的引用叫做链。

注意:数组元素靠它们的位置进行引用, 链表元素则是靠相互之间的关系进行引用。 我们说 bread 跟在 milk 后面, 而不说 bread 是链表中的第二个元素。 遍历链表, 就是跟着链接, 从链表的首元素一直走到尾元素(但这不包含链表的头节点, 头节点常常用来作为链表的接入点),链表的尾元素指向一个 null 节点。

头节点:许多链表的实现都在链表最前面有一个特殊节点, 叫做头节点。

插入节点:链表中插入一个节点的效率很高。 向链表中插入一个节点, 需要修改它前面的节点(前驱), 使其指向新加入的节点, 而新加入的节点则指向原来前驱指向的节点。
在这里插入图片描述
删除节点:从链表中删除一个元素也很简单。 将待删除元素的前驱节点指向待删除元素的后继节点, 同时将待删除元素指向 null, 元素就删除成功了。
在这里插入图片描述

3 设计一个基于对象的链表

1 Node类:用来表示节点
Node 类包含两个属性: element 用来保存节点上的数据, next 用来保存指向下一个节点的链接。 我们使用一个构造函数来创建节点, 该构造函数设置了这两个属性的值:

function Node(element) {
    this.element = element;
    this.next = null;
    }

2 LinkedList类
LList 类提供了对链表进行操作的方法。 该类的功能包括插入删除节点、 在列表中查找给定的值。 该类也有一个构造函数, 链表只有一个属性, 那就是使用一个 Node 对象来保存该链表的头节点。

function LList() {
    this.head = new Node("head");
    this.find = find;
    this.insert = insert;
    this.remove = remove;
    this.display = display;
    }

3 插入新节点
insert方法向链表中插入一个节点。

 function LList() {
    this.head = new Node("head");
    this.find = find;
    this.insert = insert;
    //this.remove = remove;
    this.display = display;
    } 
    
    //查找
function find(item) {
    var currNode = this.head;
    while (currNode.element != item) {
    currNode = currNode.next;
    } 
    return currNode;
    } 
    
    //插入
function insert(newElement, item) {
    var newNode = new Node(newElement);
    var current = this.find(item);
    newNode.next = current.next;
    current.next = newNode;
    } 

 //显示
    function display() {
	    var currNode = this.head;
	    while (!(currNode.next == null)) {
	    print(currNode.next.element);
	    currNode = currNode.next;
	    }
	    } 

4 从链表中删除一个节点

//方法遍历链表中的元素, 检查每一个节点的下一个节点中是否存储着待删除数据。 
//如果找到, 返回该节点(即“前一个” 节点), 这样就可以修改它的 next 属性了
function findPrevious(item) {
	    var currNode = this.head;
	    while (!(currNode.next == null) &&
	    (currNode.next.element != item)) {
		    currNode = currNode.next;
		    } 
	    return currNode;
	    }

//跳过了待删除节点, 让“前一个” 节点指向了待删除节点的后一个节点。    
function remove(item) {
	    var prevNode = this.findPrevious(item);
	    if (!(prevNode.next == null)) {
		    prevNode.next = prevNode.next.next;
		    }
	    }

4 双向链表

通过给 Node 对象增加一个属性, 该属性存储指向前驱节点的链接, 此时向链表插入一个节点需要更多的工作, 我们需要指出该节点正确的前驱和后继。
在这里插入图片描述

1.为 Node 类增加一个 previous 属性:

 function Node(element) {
	    this.element = element;
	    this.next = null;
	    this.previous = null;
	    }

2.insert()方法
设置新节点的 previous 属性, 使其指向该节点的前驱

function insert(newElement, item) {
    var newNode = new Node(newElement);
    var current = this.find(item);
    newNode.next = current.next;
    newNode.previous = current;
    current.next = newNode;
    }

3.remove() 方法
优点:比单向链表的效率更高, 因为不需要再查找前驱节点了。

首先需要在链表中找出存储待删除数据的节点, 然后设置该节点前驱的 next 属性, 使其指向待删除节点的后继; 设置该节点后继的 previous 属性, 使其指向待删除节点的前驱。

function remove(item) {
    var currNode = this.find(item);
    if (!(currNode.next == null)) {
	    currNode.previous.next = currNode.next;
	    currNode.next.previous = currNode.previous;
	    currNode.next = null;
	    currNode.previous = null;
	    }
    }

4.findLast() 方法
找出了链表中的最后一个节点, 同时免除了从前往后遍历链表之苦

function findLast() {
    var currNode = this.head;
    while (!(currNode.next == null)) {
	    currNode = currNode.next;
	    } 
	    return currNode;
    }

5.dispReverse() 方法
反序显示双向链表中的元素

function dispReverse() {
    var currNode = this.head;
    currNode = this.findLast();
    while (!(currNode.previous == null)) {
	    print(currNode.element);
	    currNode = currNode.previous;
	    }
    }

5 循环链表

在创建循环链表时, 让其头节点的 next 属性指向它本身, 即:

head.next = head

这种行为会传导至链表中的每个节点, 使得每个节点的 next 属性都指向链表的头节点。 换句话说, 链表的尾节点指向头节点, 形成了一个循环链表。

1.创建循环链表
只需要修改 LList 类的构造函数:

function LList() {
    this.head = new Node("head");
    this.head.next = this.head;
    this.find = find;
    this.insert = insert;
    this.display = display;
    this.findPrevious = findPrevious;
    this.remove = remove;
    }

2.循环列表中一些方法需要修改

function display() {
    var currNode = this.head;
    while (!(currNode.next == null) &&
    !(currNode.next.element == "head")) {
	    print(currNode.next.element);
	    currNode = currNode.next;
	    }
    }

6 链表的其他方法

•advance(n)
在链表中向前移动 n 个节点。

• back(n)
在双向链表中向后移动 n 个节点。

• show()
只显示当前节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值