四、鏈表、雙向鏈表、循環鏈表

鏈表

鏈表存儲有序的元素集合,不同於數組,鏈表中的元素在內存中並不是連續放置的。每個元素由一個存儲元素本身的節點和一個指向下一個元素的引用(也稱指針或鏈接)組成。如下圖:
在這裏插入圖片描述
要想訪問鏈表中間的一個元素,需要從起點(表頭)開始迭代鏈表直到找到所需的元素。

1、創建鏈表類:

/**
 * append(element):向列表尾部添加一個新的項。
 * insert(position, element):向列表的特定位置插入一個新的項。
 * remove(element):從列表中移除一項。
 * indexOf(element):返回元素在列表中的索引。如果列表中沒有該元素則返回-1。
 * removeAt(position):從列表的特定位置移除一項。
 * isEmpty():如果鏈表中不包含任何元素,返回true,如果鏈表長度大於0則返回false。
 * size():返回鏈表包含的元素個數。與數組的length屬性類似。
 * toString():由於列表項使用了Node類,就需要重寫繼承自JavaScript對象默認的toString方法,讓其只輸出元素的值。
 * getHead():如果我們需要在類的外部實現循環訪問列表,就需要提供一種獲取類的第一個元素的方法。
 */
function LinkedList() {
	// Node類表示要加入列表的項,
	let Node = function(element) {
		this.element = element; // 添加到列表的值
		this.next = null; // 指向列表中下一個節點項的指針。
	};
	let length = 0; // 有存儲列表項的數量
	let head = null; // 存儲第一個節點的引用
	/**
	 * 向LinkedList對象尾部添加一個元素時,可能有兩種場景:
	 * 1、列表爲空,添加的是第一個元素;
	 * 2、列表不爲空,向其追加元素。
	 */
	this.append = function(element) {
		let node = new Node(element);
		let current;
		if (head === null) {
			head = node;
		} else {
			// 從第一項開始查找
			current = head;
			// 循環列表找到最後一項
			while (current.next) {
				current = current.next;
			}
			// 將最後一項的next指針指向新添加的node節點
			current.next = node;
			// 長度加1
			length++;
		}
	};
	this.insert = function(position, element) {
		// 判斷邊界值
		if (position >= 0 && position <= length) {
			let node = new Node(element);
			let current = head;
			let previous;
			let index = 0;

			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;
		}
	};
	this.removeAt = function(position) {
		// 檢查邊界值
		if (position > -1 && position < length) {
			let current = head; // 要移除的元素
			let previous; // 要移除元素的前一個元素
			let index = 0;
		
			if (position === 0) {
				head = current.next;
			} else {
				while (index++ < position) {
					previous = current;
					current = current.next;
				}
				previous.next = current.next;
			}
			lenth--;
			return current.element;
		} else {
			return null;
		}
	};
	this.remove = function(element){
		var index = this.indexOf(element);
		return this.removeAt(index);
	};
	this.indexOf = function(element) {
		let current = head;
		let index = 0;
		while (current) {
			if (element === current.element) {
				return index;
			}
			index++;
			current = current.next;
		}
		return -1;
	};
	this.isEmpty = function() {
		return length === 0;
	};
	this.size = function() {
		return length;
	};
	this.toString = function() {
		let current = head;
		let string = '';
		while (current) {
			string = current.element + ',';
			current = current.next;
		}
		let length = string.length;
		if (string.lastIndexOf(',') === length - 1) {
			string = string.substring(0, length - 1);
		}
		return string;
	};
	this.getHead = function(){
		return head;
	};
}
// 實例化鏈表類
var list = new LinkedList();

2、向鏈表尾部追加(append)元素的圖示:
list.append(15);
在這裏插入圖片描述
list.append(10);
在這裏插入圖片描述
3、從鏈表中移除(removeAt)元素的圖示:
list.removeAt(0)
在這裏插入圖片描述
list.removeAt(3)
在這裏插入圖片描述
list.removeAt(1)
在這裏插入圖片描述
4、向鏈表中任意位置添加元素的圖示:
list.insert(0,14)
在這裏插入圖片描述
list.insert(3,16)
在這裏插入圖片描述
list.insert(2,18)
在這裏插入圖片描述


雙向鏈表

雙向鏈表和普通鏈表的區別在於,在鏈表中,一個節點只有鏈向下一個節點的鏈接,而在雙向鏈表中,鏈接是雙向的:一個鏈向下一個元素,另一個鏈向前一個元素,如下圖:
在這裏插入圖片描述

1、創建雙向鏈表類:

/**
 * insert(position, element):向列表的特定位置插入一個新的項。
 * removeAt(position):從列表的特定位置移除一項。
 */
function DoublyLinkedList() {
	let Node = function(element) {
		this.element = element;
		this.next = null;
		this.prev = null;
	};
	let length = 0;
	let head = null;
	let tail = null;

	this.insert = function(position, element) {
		// 判斷邊界值
		if (position >= 0 && position <= length) {
			let node = new Node(element);
			let current = head;
			let previous;
			let index = 0;

			if (position === 0) {
				if (!head) {
					head = node;
					tail = node;
				} else {
					node.next = 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;
				}
				previous.next = node;
				node.next = current;
				current.prev = node;
				node.prev = previous;
			}
			length++;
			return true;
		} else {
			return false;
		}
	};
	this.removeAt = function(position) {
		// 檢查邊界值
		if (position > -1 && position < length) {
			let current = head; // 要移除的元素
			let previous; // 要移除元素的前一個元素
			let index = 0;

			if (position === 0) {
				head = current.next;
				if (length === 1) {
					tail = null;
				} else {
					head.prev = null;
				}
			} else if (position === length - 1) {
				current = tail;
				tail = current.prev;
				tail.next = null;
			} else {
				while (index++ < position) {
					previous = current;
					current = current.next;
                }
                // 刪除current
				previous.next = current.next;
				current.next.prev = previous;
			}
			lenth--;
			return current.element;
		} else {
			return null;
		}
	};
}

2、在任意位置插入元素圖示:
在頭部插入:
在這裏插入圖片描述
在尾部插入:
在這裏插入圖片描述
在中間插入:
在這裏插入圖片描述

3、在任意位置刪除元素:
刪除頭部元素:
在這裏插入圖片描述
刪除尾部元素:
在這裏插入圖片描述
刪除中間元素:
在這裏插入圖片描述


循環鏈表

循環鏈表可以像鏈表一樣只有單向引用,也可以像雙向鏈表一樣有雙向引用。循環鏈表和鏈表之間唯一的區別在於,最後一個元素指向下一個元素的指針(tail.next)不是引用null,而是指向第一個元素(head),如下圖所示。

單向循環鏈表:
在這裏插入圖片描述
雙向循環鏈表:
在這裏插入圖片描述
循環鏈表暫無介紹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章