参考文章:https://www.cnblogs.com/xiaohuochai/p/8175716.html#anchor4

1.链表

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

JavaScript数据结构与算法(7)链表

2.创建链表

理解了链表是什么之后,现在就要开始实现我们的数据结构了。以下是我们的LinkedList 类的骨架:

function LinkedList() {
	var Node = function(element){ // {1} 
    	this.element = element; 
    	this.next = null;
  	};
  	
  	var length = 0; // {2} 
  	var head = null; // {3}
 
 	this.append = function(element){}; 
  	this.insert = function(position, element){}; 
  	this.removeAt = function(position){}; 
  	this.remove = function(element){}; 
  	this.indexOf = function(element){}; 
  	this.isEmpty = function() {};
 	this.size = function() {}; 
  	this.toString = function(){}; 
  	this.print = function(){};
  	
}

LinkedList数据结构还需要一个Node辅助类(行{1})。Node类表示要加入列表的项。它包含一个element属性,即要添加到列表的值,以及一个next属性,即指向列表中下一个节点项的指针。

LinkedList类也有存储列表项的数量的length属性(内部/私有变量)(行{2})。另一个重要的点是,我们还需要存储第一个节点的引用。为此,可以把这个引用存储在一个称为head的变量中(行{3})。

LinkedList类的方法:
append(element):向列表尾部添加一个新的项。
insert(position, element):向列表的特定位置插入一个新的项。
remove(element):从列表中移除一项。
indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。
removeAt(position):从列表的特定位置移除一项。
isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。
size():返回链表包含的元素个数。与数组的length属性类似。
toString() :由 于列表项使 用了 Node 类 ,就 需要重写 继承自 JavaScript对 象默认 的
toString方法,让其只输出元素的值。

1.append方法

向LinkedList对象尾部添加一个元素时,可能有两种场景:列表为空,添加的是第一个元素,或者列表不为空,向其追加元素

function LinkedList(){

	//创建新的节点
	//Node类表示要加入列表的项。它包含一个element属性,即要添加到列表的值,以及一个next属性,即指向列表中下一个节点项的指针
	var Node = function(element){
		this.element = element;;
		this.next = null; //列表最后节点的下一个元素始终时null
	}

	var length = 0;
	//我们还需要存储第一个节点的引用。为此,可以把这个引用存储在一个称为head的变量中
	var head = null;

	//向LinkedList对象尾部添加一个元素时,可能有两种场景:列表为空,添加的是第一个元素,或者列表不为空,向其追加元素
	this.append = function(element){

		//创建新的节点(Node项)
		var node = new Node(element);
		//指向列表中current项的变量
		var current;

		if(head === null){ //head为null说明链表为空
			head = node; //链表为空时,我们就是让head指向了node,同时我们要知道列表最后节点的下一个元素始终时null
		}else{
			//我们只有第一个元素的引用
			current = head;
			//循环列表,知道找到最后一项,current.next为null时候,说明此时current是最后一个,此时的指向是最后一项
			while(current.next){
				current = current.next;
			}
			//找到最后一项,将其next赋为node,建立链接
			current.next = node;
		}

		//更新列表的长度
		length++;
	}

}

先来实现第一个场景:向为空的列表添加一个元素。当我们创建一个LinkedList对象时,head会指向null:

JavaScript数据结构与算法(7)链表

第二个场景是向一个不为空的列表尾部添加元素

要向列表的尾部添加一个元素,首先需要找到最后一个元素。记住,我们只有第一个元素的引用(行{4}),因此需要循环访问列表,直到找到最后一项。为此,我们需要一个指向列表中current项的变量(行{2})。循环访问列表时,当current.next元素为null时,我们就知道已经到达列表尾部了。然后要做的就是让当前(也就是最后一个)元素的next指针指向想要添加到列表的节点(行{5})。下图展示了这个行为:

JavaScript数据结构与算法(7)链表

2.remove方法

现在,让我们看看如何从LinkedList对象中移除元素。移除元素也有两种场景:第一种是移除第一个元素,第二种是移除第一个以外的任一元素。我们要实现两种remove方法:第一种是从特定位置移除一个元素,第二种是根据元素的值移除元素(稍后会展示第二种remove方法)

//现在,让我们看看如何从LinkedList对象中移除元素。移除元素也有两种场景:第一种是移除第一个元素,第二种是移除第一个以外的任一元素。
this.removeAt = function(position){

	//验证位置是有效的
	if(position>-1 && position<length){

		//我们将用current变量创建一个对列表中第一个元素的引用
		//head是第一个节点的引用(说明哪一个是头)。current是代表当前节点(遍历或者移动时当时的那个节点current)
		var current = head;
		var previous;
		//需要依靠一个细节来迭代列表,直到到达目标位置
		var index = 0;

		//移除第一项,因此,如果想移除第一个元素,要做的就是让head指向列表的第二个元素。
		//我们将用current变量创建一个对列表中第一个元素的引用
		//如果把 head 赋为current.next,就会移除第一个元素。
		if(position === 0){
			head = current.next;
		}else{
			while(index++ < position){
				 previous = current;
				 //找到删除元素位置的position,此时
				 current=current.next;

			}
			////将previous与current的下一项链接起来:跳过current,从而移除它
			previous.next = current.next;
		}
		length--;
		return current.element;
	}else{
		return null
	}

}

3.insert方法

//接下来,我们要实现insert方法。使用这个方法可以在任意位置插入一个元素。
this.insert = function(position,element){

	//检查越界值
	if(position>=0  && position<=length){

		var node = new Node(element);
		var current = head;
		var previous;
		var index;

		if(position === 0){
			node.next = current;
			head = node;
		}else{
			while(index++ < position){
				previous = current;
				current = current.next;
			}
			node.next = current;
			previous.next = node;
		}

		length++;

		return true;

	}else{
		return false;
	}

}

3.双向链表

双向链表和普通链表的区别在于,在链表中,一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素, 另一个链向前一个元素,如下图所示:

JavaScript数据结构与算法(7)链表

先从实现DoublyLinkedList类所需的变动开始:

function DoubleLinkedList(){

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

	let length = 0;
	let head = null;
	let tail = null;
}

双向链表提供了两种迭代列表的方法:从头到尾,或者反过来。我们也可以访问一个特定节点的下一个或前一个元素。在单向链表中,如果迭代列表时错过了要找的元素,就需要回到列表起点,重新开始迭代。这是双向链表的一个优点.

1.insert

向双向链表中插入一个新项跟(单向)链表非常类似。区别在于,链表只要控制一个next指针,而双向链表则要同时控制next和prev(previous,前一个)这两个指针

这是向任意位置插入一个新元素的算法

function DoubleLinkedList(){

	let Node = function(element){

		this.element = element;
		this.next = null;
		this.prev;

	}

	let length = 0;
	let head = null;
	let tail = null;

	this.insert = function(position,element){

		//检查越界值
		if(position>=0 && position<=length){

			let node = new Node(element);
			var current = head;
			var previous;
			var index;

			if(position === 0){
				if(!head){
					head = node;
					tail = node;
				}else{
					node.nexe = current;
					current.prev = node;
					head = node; 
				}

			}else if(position === length){
				current = tail;
				current.next = node;
				node.prev = current;
				tail = node;

			}else{
				while(index++ < position){
					previous = current;
					current = current.next;
				}
				node.next = current;
				previous.next = node;
				current.prev = node;
				node.prev = previous;
			}
			length++;
			return true;
		}else{
			return false;
		}

	};

}

2.removeAt

从双向链表中移除元素跟链表非常类似。唯一的区别就是还需要设置前一个位置的指针。下面来看一下它的实现:

this.removeAt = function(position){
 //检查越界值
 if (position > -1 && position < length){
  let current = head,
      previous,
      index = 0;
  //移除第一项
  if (position === 0){
    head = current.next; // {1}
    //如果只有一项,更新tail //新增的
    if (length === 1){ // {2}
     tail = null;
    } else {
     head.prev = null; // {3}
    }
  } else if (position === length-1){ //最后一项 //新增的
    current = tail; // {4}
    tail = current.prev;
    tail.next = null;
  } else {
    while (index++ < position){ // {5}
      previous = current;
      current = current.next;
    }
    //将previous与current的下一项链接起来——跳过current
    previous.next = current.next; // {6}
    current.next.prev = previous; //新增的
  }
  length--;
  return current.element;
 } else {
  return null;
 }
};

4.循环链表

循环链表可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间唯一的区别在于,最后一个元素指向下一个元素的指针(tail.next)不是引用null,而是指向第一个元素(head),如下图所示:

JavaScript数据结构与算法(7)链表

双向循环链表有指向head元素的tail.next,和指向tail元素的head.prev

JavaScript数据结构与算法(7)链表

相关文章:

  • 2021-04-14
  • 2021-06-02
  • 2022-03-03
  • 2022-12-23
  • 2021-06-20
  • 2022-12-23
  • 2021-07-29
猜你喜欢
  • 2021-10-05
  • 2021-11-02
  • 2022-12-23
  • 2022-12-23
  • 2021-04-24
  • 2021-06-11
相关资源
相似解决方案