【從蛋殼到滿天飛】JS 數據結構解析和算法實現,所有文章大概的內容以下: Arrays(數組)、Stacks(棧)、Queues(隊列)、LinkedList(鏈表)、Recursion(遞歸思想)、BinarySearchTree(二分搜索樹)、Set(集合)、Map(映射)、Heap(堆)、PriorityQueue(優先隊列)、SegmentTree(線段樹)、Trie(字典樹)、UnionFind(並查集)、AVLTree(AVL 平衡樹)、RedBlackTree(紅黑平衡樹)、HashTable(哈希表)html
源代碼有三個:ES6(單個單個的 class 類型的 js 文件) | JS + HTML(一個 js 配合一個 html)| JAVA (一個一個的工程)node
所有源代碼已上傳 github,點擊我吧,光看文章可以掌握兩成,動手敲代碼、動腦思考、畫圖才能夠掌握八成。git
本文章適合 對數據結構想了解而且感興趣的人羣,文章風格一如既往如此,就以爲手機上看起來比較方便,這樣顯得比較有條理,整理這些筆記加源碼,時間跨度也算將近半年時間了,但願對想學習數據結構的人或者正在學習數據結構的人羣有幫助。github
class Node {
e; //Element
next; //Node
}
複製代碼
O(1)
的複雜度取出這個元素,數組面試
scores[2]
鏈表算法
對比數組
要清楚何時使用數組這樣的靜態數據結構,數據結構
簡單的代碼示例MyLinkedList
ide
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {}
}
複製代碼
MyLinkedList
中node.next = head
,head = node
,node.next = prev.next
,prev.next = node
,node.next = prev.next
和prev.next = node
prev.next = node
在前,node.next = prev.next
在後,這樣一來邏輯就不成立了,(class: MyLinkedList)
MyLinkedList函數
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.head = null;
this.size = 0;
}
// 獲取鏈表中實際的節點個數
getSise() {
return this.size;
}
// 判斷鏈表是否爲空
isEmpty() {
return this.size === 0;
}
// 在鏈表頭添加節點
addFirst(element) {
let node = new MyLinkedListNode(element, null);
node.next = this.head;
this.head = node;
this.size++;
}
// 在鏈表指定索引處插入節點
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
if (index === 0) {
this.addFirst(element);
} else {
// 第一個prev就是head
let prev = this.head;
// 變量i(索引)之因此要從 1 開始,由於索引爲0的那個節點就是head,循環就不須要從0開始了,
// 小於index是由於要找到指定索引位置的前一個節點
// 循環是由於 要繼續找到指定索引處的節點的前一個節點
for (var i = 1; i < index; i++) {
// 不停的切換引用,直到找到對應索引處節點的下一個節點
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
}
// 擴展:在鏈表最後一個節點的位置添加節點
addLast(element) {
this.insert(this.size, element);
}
}
複製代碼
node.next = prev.next
和prev.next = node
了,node.next = head
和head = node
,node.next = dummyHead.next
、dummyHead.next = node
,node.next = head.next;head = node;
。node.next = dummyHead.next; dummyHead.next = node;
,dummyHead.next
纔是鏈表中第一個實際記錄的節點,(class: MyLinkedList)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 獲取鏈表中實際的節點個數
getSise() {
return this.size;
}
// 判斷鏈表是否爲空
isEmpty() {
return this.size === 0;
}
// 在鏈表頭添加節點
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虛擬頭節點
insert(0, element);
}
// 在鏈表指定索引處插入節點
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一個prev就是dummyHead
let prev = this.dummyHead;
// 以前變量i(索引)之因此要從 1 開始,由於索引爲0的那個節點就是head,循環就不須要從0開始了,
// 如今索引之因此要從 0 開始, 由於初始化時 多增長了一個虛擬的頭節點
// (由於這個索引爲0的節點並非dummyHead,dummyHead這個節點並不記錄爲鏈表中的實際節點),
// 小於index是由於要找到指定索引位置的前一個節點
// 循環是由於 要繼續找到指定索引處的節點的前一個節點
for (var i = 0; i < index; i++) {
// 不停的切換引用,直到找到對應索引處節點的下一個節點
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 擴展:在鏈表最後一個節點的位置添加節點
addLast(element) {
this.insert(this.size, element);
}
}
複製代碼
dummyHead
開始遍歷,dummyHead.next
開始遍歷。(class: MyLinkedList, class: Main)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 獲取鏈表中實際的節點個數
getSize() {
return this.size;
}
// 判斷鏈表是否爲空
isEmpty() {
return this.size === 0;
}
// 在鏈表頭添加節點
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虛擬頭節點
this.insert(0, element);
}
// 在鏈表指定索引處插入節點
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一個prev就是dummyHead
let prev = this.dummyHead;
// 以前變量i(索引)之因此要從 1 開始,由於索引爲0的那個節點就是head,循環就不須要從0開始了,
// 如今索引之因此要從 0 開始, 由於初始化時 多增長了一個虛擬的頭節點
// (由於這個索引爲0的節點並非dummyHead,dummyHead這個節點並不記錄爲鏈表中的實際節點),
// 小於index是由於要找到指定索引位置的前一個節點
// 循環是由於 要繼續找到指定索引處的節點的前一個節點
for (var i = 0; i < index; i++) {
// 不停的切換引用,直到找到對應索引處節點的下一個節點
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 擴展:在鏈表最後一個節點的位置添加節點
addLast(element) {
this.insert(this.size, element);
}
// 獲取指定索引位置的元素
get(index) {
// 判斷索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 若是你要找指定索引節點的前一個節點 就使用dummyHead
// 若是你要找到指定索引節點 就使用dummyHead.next
// 由於duumyHead並非第一個節點,由於它是一個虛擬節點,
// dummyHead.next纔是真正被記錄的第一個節點。
let node = this.dummyHead.next;
for (var i = 0; i < index; i++) {
node = node.next;
}
return node.element;
}
// 獲取頭節點的元素
getFirst() {
return this.get(0);
}
// 獲取尾節點的元素
getLast() {
return this.get(this.size - 1);
}
// 設置指定索引位置的元素值
set(index, element) {
// 判斷索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 從第一個真正被記錄的節點開始,從0開始
let node = this.dummyHead.next;
// 索引爲 0 時,實際上切換到的節點 它的索引爲 1
// i < index ,當索引爲 index-1 時, 實際上切換到的節點 它的索引爲index
for (let i = 0; i < index; i++) {
// 每一次切換 都只是改變引用
// 不的在鏈表中找下一個節點
node = node.next;
}
node.element = element;
}
// 全部節點中是否有包含該元素
contains(element) {
let node = this.dummyHead;
while (node.next !== null) {
if (node.next.element === element) return true;
// 不停的向下切換
node = node.next;
}
return false;
}
// 輸出鏈表中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedList: size = ${this.size},\n`;
arrInfo += `data = front [`;
let node = this.dummyHead.next;
while (node.next !== null) {
arrInfo += `${node.element}->`;
node = node.next;
}
arrInfo += 'NULL] tail';
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
Main
class Main {
constructor () {
/this.alterLine("MyLinkedList Area");
let mylinkedList = new MyLinkedList();
for (let i = 1; i <= 5 ; i++) {
mylinkedList.addFirst(i);
console.log(mylinkedList.toString());
}
mylinkedList.insert(2, 88888);
console.log(mylinkedList.toString());
}
// 將內容顯示在頁面上
show (content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展現分割線
alterLine (title) {
let line = `--------------------${title}----------------------`
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
複製代碼
prev.next = delNode.next
,delNode.next = null
就完成了刪除,delNode = delNode.next
,(class: MyLinkedList, class: Main)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 獲取鏈表中實際的節點個數
getSize() {
return this.size;
}
// 判斷鏈表是否爲空
isEmpty() {
return this.size === 0;
}
// 在鏈表頭添加節點
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虛擬頭節點
this.insert(0, element);
}
// 在鏈表指定索引處插入節點
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一個prev就是dummyHead
let prev = this.dummyHead;
// 以前變量i(索引)之因此要從 1 開始,由於索引爲0的那個節點就是head,循環就不須要從0開始了,
// 如今索引之因此要從 0 開始, 由於初始化時 多增長了一個虛擬的頭節點
// (由於這個索引爲0的節點並非dummyHead,dummyHead這個節點並不記錄爲鏈表中的實際節點),
// 小於index是由於要找到指定索引位置的前一個節點
// 循環是由於 要繼續找到指定索引處的節點的前一個節點
for (var i = 0; i < index; i++) {
// 不停的切換引用,直到找到對應索引處節點的下一個節點
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 擴展:在鏈表最後一個節點的位置添加節點
addLast(element) {
this.insert(this.size, element);
}
// 獲取指定索引位置的元素
get(index) {
// 判斷索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 若是你要找指定索引節點的前一個節點 就使用dummyHead
// 若是你要找到指定索引節點 就使用dummyHead.next
// 由於duumyHead並非第一個節點,由於它是一個虛擬節點,
// dummyHead.next纔是真正被記錄的第一個節點。
let node = this.dummyHead.next;
for (var i = 0; i < index; i++) {
node = node.next;
}
return node.element;
}
// 獲取頭節點的元素
getFirst() {
return this.get(0);
}
// 獲取尾節點的元素
getLast() {
return this.get(this.size - 1);
}
// 設置指定索引位置的元素值
set(index, element) {
// 判斷索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 從第一個真正被記錄的節點開始,從0開始
let node = this.dummyHead.next;
// 索引爲 0 時,實際上切換到的節點 它的索引爲 1
// i < index ,當索引爲 index-1 時, 實際上切換到的節點 它的索引爲index
for (let i = 0; i < index; i++) {
// 每一次切換 都只是改變引用
// 不的在鏈表中找下一個節點
node = node.next;
}
node.element = element;
}
// 全部節點中是否有包含該元素
contains(element) {
let node = this.dummyHead;
while (node.next !== null) {
if (node.next.element === element) return true;
// 不停的向下切換
node = node.next;
}
return false;
}
// 刪除指定索引位置的節點
remove(index) {
// 驗證索引的合法性
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index > this.size');
}
let node = this.dummyHead;
for (let i = 0; i < index; i++) {
node = node.next;
}
// 待刪除的節點
let delNode = node.next;
// 給待刪除那個節點的前一個的節點的next引用替換爲
// 但刪除的這個節點的next
node.next = delNode.next;
// 或者這樣也行
// node.next = node.next.next;
// 臨時存儲待刪除的那個節點裏的元素
let element = delNode.element;
// 清空 待刪除的節點
delNode = null;
this.size--;
return element;
}
// 擴展:移除鏈表頭的元素
removeFirst() {
return this.remove(0);
}
// 擴展:移除鏈表尾部的元素
removeLast() {
return this.remove(this.size - 1);
}
// 輸出鏈表中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedList: size = ${this.size},\n`;
arrInfo += `data = front [`;
let node = this.dummyHead.next;
while (node.next !== null) {
arrInfo += `${node.element}->`;
node = node.next;
}
arrInfo += 'NULL] tail';
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
Main
class Main {
constructor() {
this.alterLine('MyLinkedList Area');
let mylinkedList = new MyLinkedList();
for (let i = 1; i <= 5; i++) {
mylinkedList.addFirst(i);
console.log(mylinkedList.toString());
}
mylinkedList.insert(2, 88888);
console.log(mylinkedList.toString());
mylinkedList.remove(2);
console.log(mylinkedList.toString());
mylinkedList.removeFirst();
console.log(mylinkedList.toString());
mylinkedList.removeLast();
console.log(mylinkedList.toString());
}
// 將內容顯示在頁面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展現分割線
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
複製代碼
O(n)
:在只對鏈表頭進行操做時爲O(1)
O(n)
:在只對鏈表頭進行操做時爲O(1)
O(n)
O(n)
:只查鏈表頭的元素時爲O(1)
### 添加操做 O(n)
addLast(e)
:O(n)
addFirst(e)
:O(1)
insert(index, e)
:O(n/2) = O(n)
removeLast()
:O(n)
removeFirst()
:O(1)
remove(index)
:O(n/2) = O(n)
set(index, e)
:O(n)
get(index)
:O(n)
contains(e)
:O(n)
find(e)
:O(n)
O(n)
,O(1)
O(n)
,O(1)
O(n)
,O(1)
MyLinkedListStack
的接口。
void push(E e)
:添加一個元素E pop()
:移除一個元素E peek()
:查看棧頂的元素int getSize()
:獲取棧中實際的元素個數boolean isEmpty()
:判斷棧是否爲空(class: MyLinkedList,class: MyLinkedListStack, class: Main)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 獲取鏈表中實際的節點個數
getSize() {
return this.size;
}
// 判斷鏈表是否爲空
isEmpty() {
return this.size === 0;
}
// 在鏈表頭添加節點
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虛擬頭節點
this.insert(0, element);
}
// 在鏈表指定索引處插入節點
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一個prev就是dummyHead
let prev = this.dummyHead;
// 以前變量i(索引)之因此要從 1 開始,由於索引爲0的那個節點就是head,循環就不須要從0開始了,
// 如今索引之因此要從 0 開始, 由於初始化時 多增長了一個虛擬的頭節點
// (由於這個索引爲0的節點並非dummyHead,dummyHead這個節點並不記錄爲鏈表中的實際節點),
// 小於index是由於要找到指定索引位置的前一個節點
// 循環是由於 要繼續找到指定索引處的節點的前一個節點
for (var i = 0; i < index; i++) {
// 不停的切換引用,直到找到對應索引處節點的下一個節點
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 擴展:在鏈表最後一個節點的位置添加節點
addLast(element) {
this.insert(this.size, element);
}
// 獲取指定索引位置的元素
get(index) {
// 判斷索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 若是你要找指定索引節點的前一個節點 就使用dummyHead
// 若是你要找到指定索引節點 就使用dummyHead.next
// 由於duumyHead並非第一個節點,由於它是一個虛擬節點,
// dummyHead.next纔是真正被記錄的第一個節點。
let node = this.dummyHead.next;
for (var i = 0; i < index; i++) {
node = node.next;
}
return node.element;
}
// 獲取頭節點的元素
getFirst() {
return this.get(0);
}
// 獲取尾節點的元素
getLast() {
return this.get(this.size - 1);
}
// 設置指定索引位置的元素值
set(index, element) {
// 判斷索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 從第一個真正被記錄的節點開始,從0開始
let node = this.dummyHead.next;
// 索引爲 0 時,實際上切換到的節點 它的索引爲 1
// i < index ,當索引爲 index-1 時, 實際上切換到的節點 它的索引爲index
for (let i = 0; i < index; i++) {
// 每一次切換 都只是改變引用
// 不的在鏈表中找下一個節點
node = node.next;
}
node.element = element;
}
// 全部節點中是否有包含該元素
contains(element) {
let node = this.dummyHead;
while (node.next !== null) {
if (node.next.element === element) return true;
// 不停的向下切換
node = node.next;
}
return false;
}
// 刪除指定索引位置的節點
remove(index) {
// 驗證索引的合法性
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index > this.size');
}
let node = this.dummyHead;
for (let i = 0; i < index; i++) {
node = node.next;
}
// 待刪除的節點
let delNode = node.next;
// 給待刪除那個節點的前一個的節點的next引用替換爲
// 但刪除的這個節點的next
node.next = delNode.next;
// 或者這樣也行
// node.next = node.next.next;
// 臨時存儲待刪除的那個節點裏的元素
let element = delNode.element;
// 清空 待刪除的節點
delNode = null;
this.size--;
return element;
}
// 擴展:移除鏈表頭的元素
removeFirst() {
return this.remove(0);
}
// 擴展:移除鏈表尾部的元素
removeLast() {
return this.remove(this.size - 1);
}
// 輸出鏈表中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedList: size = ${this.size},\n`;
arrInfo += `data = front [`;
let node = this.dummyHead.next;
while (node.next !== null) {
arrInfo += `${node.element}->`;
node = node.next;
}
arrInfo += 'NULL] tail';
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MyLinkedListStack
class MyLinkedListStack {
constructor() {
this.myLinkedList = new MyLinkedList();
}
// 入棧
push(element) {
this.myLinkedList.addFirst(element);
}
// 出棧
pop() {
return this.myLinkedList.removeFirst();
}
// 查看棧頂元素
peek() {
return this.myLinkedList.getFirst();
}
// 查看棧中實際元素的個數
getSize() {
return this.myLinkedList.getSize();
}
// 判斷棧是否爲空
isEmpty() {
return this.myLinkedList.isEmpty();
}
// 輸出棧中信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedListStack: size = ${this.getSize()},\n`;
arrInfo += `data = stack top [`;
let node = this.myLinkedList.dummyHead.next;
for (var i = 1; i < this.getSize(); i++) {
arrInfo += `${node.element},`;
node = node.next;
}
if (!this.isEmpty()) {
arrInfo += `${node.element}`;
}
arrInfo += ']';
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
Main
class Main {
constructor() {
this.alterLine('MyLinkedListStack Area');
let myLinkedListStack = new MyLinkedListStack();
for (let i = 1; i <= 5; i++) {
myLinkedListStack.push(i);
console.log(myLinkedListStack.toString());
}
console.log(myLinkedListStack.peek());
this.show(myLinkedListStack.peek());
for (let i = 0; i < 5; i++) {
console.log(myLinkedListStack.toString());
myLinkedListStack.pop();
}
}
// 將內容顯示在頁面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展現分割線
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
window.onload = function() {
// 執行主函數
new Main();
};
複製代碼
O(1)
級別的,(class: MyLinkedList, class: MyLinkedListStack, class: MyArray, class: MyStack, class: Main)
MyLinkedList
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// toString 2018-10-20-jwl
toString() {
return this.element.toString();
}
}
class MyLinkedList {
constructor() {
this.dummyHead = new MyLinkedListNode(null, null);
this.size = 0;
}
// 獲取鏈表中實際的節點個數
getSize() {
return this.size;
}
// 判斷鏈表是否爲空
isEmpty() {
return this.size === 0;
}
// 在鏈表頭添加節點
addFirst(element) {
// let node = new MyLinkedListNode(element, null);
// node.next = this.head;
// this.head = node;
// this.size ++;
// 改用虛擬頭節點
this.insert(0, element);
}
// 在鏈表指定索引處插入節點
insert(index, element) {
if (index < 0 || index > this.size) {
throw new Error('add error. index < 0 or index > size');
}
// 第一個prev就是dummyHead
let prev = this.dummyHead;
// 以前變量i(索引)之因此要從 1 開始,由於索引爲0的那個節點就是head,循環就不須要從0開始了,
// 如今索引之因此要從 0 開始, 由於初始化時 多增長了一個虛擬的頭節點
// (由於這個索引爲0的節點並非dummyHead,dummyHead這個節點並不記錄爲鏈表中的實際節點),
// 小於index是由於要找到指定索引位置的前一個節點
// 循環是由於 要繼續找到指定索引處的節點的前一個節點
for (var i = 0; i < index; i++) {
// 不停的切換引用,直到找到對應索引處節點的下一個節點
prev = prev.next;
}
let node = new MyLinkedListNode(element, null);
node.next = prev.next;
prev.next = node;
this.size++;
}
// 擴展:在鏈表最後一個節點的位置添加節點
addLast(element) {
this.insert(this.size, element);
}
// 獲取指定索引位置的元素
get(index) {
// 判斷索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 若是你要找指定索引節點的前一個節點 就使用dummyHead
// 若是你要找到指定索引節點 就使用dummyHead.next
// 由於duumyHead並非第一個節點,由於它是一個虛擬節點,
// dummyHead.next纔是真正被記錄的第一個節點。
let node = this.dummyHead.next;
for (var i = 0; i < index; i++) {
node = node.next;
}
return node.element;
}
// 獲取頭節點的元素
getFirst() {
return this.get(0);
}
// 獲取尾節點的元素
getLast() {
return this.get(this.size - 1);
}
// 設置指定索引位置的元素值
set(index, element) {
// 判斷索引合法性
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size');
}
// 從第一個真正被記錄的節點開始,從0開始
let node = this.dummyHead.next;
// 索引爲 0 時,實際上切換到的節點 它的索引爲 1
// i < index ,當索引爲 index-1 時, 實際上切換到的節點 它的索引爲index
for (let i = 0; i < index; i++) {
// 每一次切換 都只是改變引用
// 不的在鏈表中找下一個節點
node = node.next;
}
node.element = element;
}
// 全部節點中是否有包含該元素
contains(element) {
let node = this.dummyHead;
while (node.next !== null) {
if (node.next.element === element) return true;
// 不停的向下切換
node = node.next;
}
return false;
}
// 刪除指定索引位置的節點
remove(index) {
// 驗證索引的合法性
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index > this.size');
}
let node = this.dummyHead;
for (let i = 0; i < index; i++) {
node = node.next;
}
// 待刪除的節點
let delNode = node.next;
// 給待刪除那個節點的前一個的節點的next引用替換爲
// 但刪除的這個節點的next
node.next = delNode.next;
// 或者這樣也行
// node.next = node.next.next;
// 臨時存儲待刪除的那個節點裏的元素
let element = delNode.element;
// 清空 待刪除的節點
delNode = null;
this.size--;
return element;
}
// 擴展:移除鏈表頭的元素
removeFirst() {
return this.remove(0);
}
// 擴展:移除鏈表尾部的元素
removeLast() {
return this.remove(this.size - 1);
}
// 輸出鏈表中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedList: size = ${this.size},\n`;
arrInfo += `data = front [`;
let node = this.dummyHead.next;
while (node.next !== null) {
arrInfo += `${node.element}->`;
node = node.next;
}
arrInfo += 'NULL] tail';
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MyLinkedListStack
class MyLinkedListStack {
constructor() {
this.myLinkedList = new MyLinkedList();
}
// 入棧
push(element) {
this.myLinkedList.addFirst(element);
}
// 出棧
pop() {
return this.myLinkedList.removeFirst();
}
// 查看棧頂元素
peek() {
return this.myLinkedList.getFirst();
}
// 查看棧中實際元素的個數
getSize() {
return this.myLinkedList.getSize();
}
// 判斷棧是否爲空
isEmpty() {
return this.myLinkedList.isEmpty();
}
// 輸出棧中信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedListStack: size = ${this.getSize()},\n`;
arrInfo += `data = stack top [`;
let node = this.myLinkedList.dummyHead.next;
for (var i = 1; i < this.getSize(); i++) {
arrInfo += `${node.element},`;
node = node.next;
}
if (!this.isEmpty()) {
arrInfo += `${node.element}`;
}
arrInfo += ']';
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MyArray
class MyArray {
// 構造函數,傳入數組的容量capacity構造Array 默認數組的容量capacity=10
constructor(capacity = 10) {
this.data = new Array(capacity);
this.size = 0;
}
// 獲取數組中的元素實際個數
getSize() {
return this.size;
}
// 獲取數組的容量
getCapacity() {
return this.data.length;
}
// 判斷數組是否爲空
isEmpty() {
return this.size === 0;
}
// 給數組擴容
resize(capacity) {
let newArray = new Array(capacity);
for (var i = 0; i < this.size; i++) {
newArray[i] = this.data[i];
}
// let index = this.size - 1;
// while (index > -1) {
// newArray[index] = this.data[index];
// index --;
// }
this.data = newArray;
}
// 在指定索引處插入元素
insert(index, element) {
// 先判斷數組是否已滿
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// 而後判斷索引是否符合要求
if (index < 0 || index > this.size) {
throw new Error(
'insert error. require index < 0 or index > size.'
);
}
// 最後 將指定索引處騰出來
// 從指定索引處開始,全部數組元素所有日後移動一位
// 從後往前移動
for (let i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
// 在指定索引處插入元素
this.data[index] = element;
// 維護一下size
this.size++;
}
// 擴展 在數組最前面插入一個元素
unshift(element) {
this.insert(0, element);
}
// 擴展 在數組最後面插入一個元素
push(element) {
this.insert(this.size, element);
}
// 其實在數組中添加元素 就至關於在數組最後面插入一個元素
add(element) {
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// size其實指向的是 當前數組最後一個元素的 後一個位置的索引。
this.data[this.size] = element;
// 維護size
this.size++;
}
// get
get(index) {
// 不能訪問沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size.');
}
return this.data[index];
}
// 擴展: 獲取數組中第一個元素
getFirst() {
return this.get(0);
}
// 擴展: 獲取數組中最後一個元素
getLast() {
return this.get(this.size - 1);
}
// set
set(index, newElement) {
// 不能修改沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('set error. index < 0 or index >= size.');
}
this.data[index] = newElement;
}
// contain
contain(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return true;
}
}
return false;
}
// find
find(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return i;
}
}
return -1;
}
// findAll
findAll(element) {
// 建立一個自定義數組來存取這些 元素的索引
let myarray = new MyArray(this.size);
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
myarray.push(i);
}
}
// 返回這個自定義數組
return myarray;
}
// 刪除指定索引處的元素
remove(index) {
// 索引合法性驗證
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index >= size.');
}
// 暫存即將要被刪除的元素
let element = this.data[index];
// 後面的元素覆蓋前面的元素
for (let i = index; i < this.size - 1; i++) {
this.data[i] = this.data[i + 1];
}
this.size--;
this.data[this.size] = null;
// 若是size 爲容量的四分之一時 就能夠縮容了
// 防止複雜度震盪
if (Math.floor(this.getCapacity() / 4) === this.size) {
// 縮容一半
this.resize(Math.floor(this.getCapacity() / 2));
}
return element;
}
// 擴展:刪除數組中第一個元素
shift() {
return this.remove(0);
}
// 擴展: 刪除數組中最後一個元素
pop() {
return this.remove(this.size - 1);
}
// 擴展: 根據元素來進行刪除
removeElement(element) {
let index = this.find(element);
if (index !== -1) {
this.remove(index);
}
}
// 擴展: 根據元素來刪除全部元素
removeAllElement(element) {
let index = this.find(element);
while (index != -1) {
this.remove(index);
index = this.find(element);
}
// let indexArray = this.findAll(element);
// let cur, index = 0;
// for (var i = 0; i < indexArray.getSize(); i++) {
// // 每刪除一個元素 原數組中就少一個元素,
// // 索引數組中的索引值是按照大小順序排列的,
// // 因此 這個cur記錄的是 原數組元素索引的偏移量
// // 只有這樣纔可以正確的刪除元素。
// index = indexArray.get(i) - cur++;
// this.remove(index);
// }
}
// @Override toString 2018-10-17-jwl
toString() {
let arrInfo = `Array: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.size - 1; i++) {
arrInfo += `${this.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.data[this.size - 1]}`;
}
arrInfo += `]`;
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MyStack
class MyStack {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 入棧
push(element) {
this.myArray.push(element);
}
// 出棧
pop() {
return this.myArray.pop();
}
// 查看棧頂的元素
peek() {
return this.myArray.getLast();
}
// 棧中實際元素的個數
getSize() {
return this.myArray.getSize();
}
// 棧是否爲空
isEmpty() {
return this.myArray.isEmpty();
}
// 查看棧的容量
getCapacity() {
return this.myArray.getCapacity();
}
// @Override toString 2018-10-20-jwl
toString() {
let arrInfo = `Stack: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.myArray.size - 1; i++) {
arrInfo += `${this.myArray.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.myArray.data[this.myArray.size - 1]}`;
}
arrInfo += `] stack top is right!`;
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
Main
class Main {
constructor() {
this.alterLine('Stacks Comparison Area');
let myStack = new MyStack();
let myLinkedListStack = new MyLinkedListStack();
let performanceTest = new PerformanceTest();
let myStackInfo = performanceTest.testStack(myStack, 100000);
let myLinkedListStackInfo = performanceTest.testStack(
myLinkedListStack,
100000
);
this.alterLine('MyStack Area');
console.log(myStackInfo);
this.show(myStackInfo);
this.alterLine('MyLinkedListStack Area');
console.log(myLinkedListStackInfo);
this.show(myLinkedListStackInfo);
}
// 將內容顯示在頁面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展現分割線
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
class Student {
constructor(studentName, studentScore) {
this.name = studentName;
this.score = studentScore;
}
toString() {
let studentInfo = `Student(name: ${this.name}, score: ${this.score})`;
return studentInfo;
}
}
window.onload = function() {
// 執行主函數
new Main();
};
複製代碼
O(n)
,O(1)
,O(n)
O(n)
,O(1)
,O(n)
O(n)
,O(1)
,O(n)
O(n)
級別,O(1)
去刪除尾部的節點,( class: MyArray, class: MyQueue, class: MyLoopQueue, class: MyLinkedListQueue, class: Main)
MyArray
class MyArray {
// 構造函數,傳入數組的容量capacity構造Array 默認數組的容量capacity=10
constructor(capacity = 10) {
this.data = new Array(capacity);
this.size = 0;
}
// 獲取數組中的元素實際個數
getSize() {
return this.size;
}
// 獲取數組的容量
getCapacity() {
return this.data.length;
}
// 判斷數組是否爲空
isEmpty() {
return this.size === 0;
}
// 給數組擴容
resize(capacity) {
let newArray = new Array(capacity);
for (var i = 0; i < this.size; i++) {
newArray[i] = this.data[i];
}
// let index = this.size - 1;
// while (index > -1) {
// newArray[index] = this.data[index];
// index --;
// }
this.data = newArray;
}
// 在指定索引處插入元素
insert(index, element) {
// 先判斷數組是否已滿
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// 而後判斷索引是否符合要求
if (index < 0 || index > this.size) {
throw new Error(
'insert error. require index < 0 or index > size.'
);
}
// 最後 將指定索引處騰出來
// 從指定索引處開始,全部數組元素所有日後移動一位
// 從後往前移動
for (let i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
// 在指定索引處插入元素
this.data[index] = element;
// 維護一下size
this.size++;
}
// 擴展 在數組最前面插入一個元素
unshift(element) {
this.insert(0, element);
}
// 擴展 在數組最後面插入一個元素
push(element) {
this.insert(this.size, element);
}
// 其實在數組中添加元素 就至關於在數組最後面插入一個元素
add(element) {
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// size其實指向的是 當前數組最後一個元素的 後一個位置的索引。
this.data[this.size] = element;
// 維護size
this.size++;
}
// get
get(index) {
// 不能訪問沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size.');
}
return this.data[index];
}
// 擴展: 獲取數組中第一個元素
getFirst() {
return this.get(0);
}
// 擴展: 獲取數組中最後一個元素
getLast() {
return this.get(this.size - 1);
}
// set
set(index, newElement) {
// 不能修改沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('set error. index < 0 or index >= size.');
}
this.data[index] = newElement;
}
// contain
contain(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return true;
}
}
return false;
}
// find
find(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return i;
}
}
return -1;
}
// findAll
findAll(element) {
// 建立一個自定義數組來存取這些 元素的索引
let myarray = new MyArray(this.size);
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
myarray.push(i);
}
}
// 返回這個自定義數組
return myarray;
}
// 刪除指定索引處的元素
remove(index) {
// 索引合法性驗證
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index >= size.');
}
// 暫存即將要被刪除的元素
let element = this.data[index];
// 後面的元素覆蓋前面的元素
for (let i = index; i < this.size - 1; i++) {
this.data[i] = this.data[i + 1];
}
this.size--;
this.data[this.size] = null;
// 若是size 爲容量的四分之一時 就能夠縮容了
// 防止複雜度震盪
if (Math.floor(this.getCapacity() / 4) === this.size) {
// 縮容一半
this.resize(Math.floor(this.getCapacity() / 2));
}
return element;
}
// 擴展:刪除數組中第一個元素
shift() {
return this.remove(0);
}
// 擴展: 刪除數組中最後一個元素
pop() {
return this.remove(this.size - 1);
}
// 擴展: 根據元素來進行刪除
removeElement(element) {
let index = this.find(element);
if (index !== -1) {
this.remove(index);
}
}
// 擴展: 根據元素來刪除全部元素
removeAllElement(element) {
let index = this.find(element);
while (index != -1) {
this.remove(index);
index = this.find(element);
}
// let indexArray = this.findAll(element);
// let cur, index = 0;
// for (var i = 0; i < indexArray.getSize(); i++) {
// // 每刪除一個元素 原數組中就少一個元素,
// // 索引數組中的索引值是按照大小順序排列的,
// // 因此 這個cur記錄的是 原數組元素索引的偏移量
// // 只有這樣纔可以正確的刪除元素。
// index = indexArray.get(i) - cur++;
// this.remove(index);
// }
}
// @Override toString 2018-10-17-jwl
toString() {
let arrInfo = `Array: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.size - 1; i++) {
arrInfo += `${this.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.data[this.size - 1]}`;
}
arrInfo += `]`;
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MyQueue
class MyQueue {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 入隊
enqueue(element) {
this.myArray.push(element);
}
// 出隊
dequeue() {
return this.myArray.shift();
}
// 查看隊首的元素
getFront() {
return this.myArray.getFirst();
}
// 查看隊列中實際元素的個數
getSize() {
return this.myArray.getSize();
}
// 查看 隊列當前的容量
getCapacity() {
return this.myArray.getCapacity();
}
// 查看隊列是否爲空
isEmpty() {
return this.myArray.isEmpty();
}
// 輸出隊列中的信息
// @Override toString 2018-10-20-jwl
toString() {
let arrInfo = `Queue: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = front [`;
for (var i = 0; i < this.myArray.size - 1; i++) {
arrInfo += `${this.myArray.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.myArray.data[this.myArray.size - 1]}`;
}
arrInfo += `] tail`;
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MyLoopQueue
class MyLoopQueue {
constructor(capacity = 10) {
// 初始化新數組
this.data = new Array(capacity);
// 初始化 隊首、隊尾的值 (索引)
this.front = this.tail = 0;
// 隊列中實際元素個數
this.size = 0;
}
// 擴容
resize(capacity) {
let newArray = new Array(capacity);
let index = 0;
for (let i = 0; i < this.size; i++) {
// 索引可能會越界,因而就要取餘一下,
// 若是越界了,就從隊首開始
index = (this.front + i) % this.getCapacity();
newArray[i] = this.data[index];
}
this.data = newArray;
this.front = 0;
this.tail = this.size;
}
// 入隊
enqueue(element) {
// 判斷隊列中是否已滿
if ((this.tail + 1) % this.getCapacity() === this.front) {
this.resize(this.getCapacity() * 2);
}
this.data[this.tail] = element;
this.tail = (this.tail + 1) % this.getCapacity();
this.size++;
}
// 出隊
dequeue() {
// 判斷隊列是否爲空
if (this.isEmpty()) {
throw new Error("can't dequeue from an empty queue.");
}
let element = this.data[this.front];
this.data[this.front] = null;
this.front = (this.front + 1) % this.getCapacity();
this.size--;
// 當size 爲容量的四分之一時就縮容一倍
if (this.size === Math.floor(this.getCapacity() / 4)) {
this.resize(Math.floor(this.getCapacity() * 2));
}
return element;
}
// 查看隊首的元素
getFront() {
if (this.isEmpty()) {
throw new Error('queue is empty.');
}
return this.data[front];
}
// 查看實際的元素個數
getSize() {
return this.size;
}
// 查看容量
getCapacity() {
return this.data.length;
}
// 隊列是否爲空
isEmpty() {
// return this.size === 0;
return this.front == this.tail;
}
// 輸出循環隊列中的信息
// @Override toString 2018-10-20-jwl
toString() {
let arrInfo = `LoopQueue: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = front [`;
for (var i = 0; i < this.myArray.size - 1; i++) {
arrInfo += `${this.myArray.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.myArray.data[this.myArray.size - 1]}`;
}
arrInfo += `] tail`;
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MyLinkedListQueue
class MyLinkedListQueue {
constructor() {
this.front = this.tail = null;
this.size = 0;
}
// 入隊
enqueue(element) {
// 判斷隊尾是否爲空
if (this.tail === null) {
// 第一個節點 便是尾也是頭
this.tail = new MyLinkedListNode(element, null);
this.front = this.tail;
} else {
let node = new MyLinkedListNode(element, null);
this.tail.next = node;
this.tail = node;
}
this.size++;
}
// 出隊
dequeue() {
// 判斷隊首是否爲空
if (this.front === null) {
throw new Error('front is empty.');
}
let delNode = this.front;
let element = delNode.element;
this.front = this.front.next;
delNode = null;
if (this.front === null)
// 若是頭爲空了,那麼尾部也爲空
this.tail = null;
this.size--;
return element;
}
// 查看隊首的元素
getFront() {
// 判斷隊首是否爲空
if (this.front === null) {
throw new Error('front is empty.');
}
return this.front.element;
}
// 查看隊列中實際元素的個數
getSize() {
return this.size;
}
// 判斷隊列是否爲空
isEmpty() {
return this.size === 0;
}
// 輸出隊列中的信息
// @Override toString 2018-10-21-jwl
toString() {
let arrInfo = `LinkedListQueue: size = ${this.getSize()},\n`;
arrInfo += `data = front [`;
let node = this.front;
for (var i = 1; i < this.getSize(); i++) {
arrInfo += `${node.element},`;
node = node.next;
}
if (!this.isEmpty()) {
arrInfo += `${node.element}`;
}
arrInfo += '] tail';
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
Main
class Main {
constructor() {
this.alterLine('MyLinkedListQueue Area');
let myLinkedListQueue = new MyLinkedListQueue();
for (let i = 1; i <= 5; i++) {
myLinkedListQueue.enqueue(i);
console.log(myLinkedListQueue.toString());
}
console.log(myLinkedListQueue.getFront());
this.show(myLinkedListQueue.getFront());
for (let i = 0; i < 5; i++) {
console.log(myLinkedListQueue.toString());
myLinkedListQueue.dequeue();
}
}
// 將內容顯示在頁面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展現分割線
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
class Student {
constructor(studentName, studentScore) {
this.name = studentName;
this.score = studentScore;
}
toString() {
let studentInfo = `Student(name: ${this.name}, score: ${this.score})`;
return studentInfo;
}
}
window.onload = function() {
// 執行主函數
new Main();
};
複製代碼