【從蛋殼到滿天飛】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 (一個一個的工程)java
所有源代碼已上傳 github,點擊我吧,光看文章可以掌握兩成,動手敲代碼、動腦思考、畫圖才能夠掌握八成。node
本文章適合 對數據結構想了解而且感興趣的人羣,文章風格一如既往如此,就以爲手機上看起來比較方便,這樣顯得比較有條理,整理這些筆記加源碼,時間跨度也算將近半年時間了,但願對想學習數據結構的人或者正在學習數據結構的人羣有幫助。python
MySet
void add (e)
: 不能添加劇復元素void remove (e)
boolean conatains (e)
int getSize ()
boolean isEmpty ()
(class: MyBinarySearchTree, class: MyBSTSet, class: Main)
c++
MyBinarySearchTreegit
// 自定義二分搜索樹節點
class MyBinarySearchTreeNode {
constructor(element, left = null, right = null) {
// 實際存儲的元素
this.element = element;
// 當前節點的左子樹
this.left = left;
// 當前節點的右子樹
this.right = right;
}
}
// 自定義二分搜索樹
class MyBinarySearchTree {
constructor() {
this.root = null;
this.size = 0;
}
// 添加元素到二分搜索樹中 +
add(element) {
if (element === null) throw new Error("element is null. can't store.");
this.root = this.recursiveAdd(this.root, element);
}
// 添加元素到二分搜索樹中 遞歸算法 -
recursiveAdd(node, newElement) {
// 解決最基本的問題 也就是遞歸函數調用的終止條件
if (node === null) {
this.size++;
return new MyBinarySearchTreeNode(newElement);
}
// 1. 當前節點的元素比新元素大
// 那麼新元素就會被添加到當前節點的左子樹去
// 2. 當前節點的元素比新元素小
// 那麼新元素就會被添加到當前節點的右子樹去
// 3. 當前節點的元素比新元素相等
// 什麼都不作了,由於目前不添加劇復的元素
if (this.compare(node.element, newElement) > 0)
node.left = this.recursiveAdd(node.left, newElement);
else if (this.compare(node.element, newElement) < 0)
node.right = this.recursiveAdd(node.right, newElement);
else {
}
// 將複雜問題分解成多個性質相同的小問題,
// 而後求出小問題的答案,
// 最終構建出原問題的答案
return node;
}
// 判斷二分搜索樹中是否包含某個元素 +
contains(element) {
if (this.root === null) throw new Error("root is null. can't query.");
return this.recursiveContains(this.root, element);
}
// 判斷二分搜索樹種是否包含某個元素 遞歸算法 -
recursiveContains(node, element) {
if (node === null) return false;
// 當前節點元素比 要搜索的元素 大
if (this.compare(node.element, element) > 0)
return this.recursiveContains(node.left, element);
else if (this.compare(node.element, element) < 0)
// 當前元素比 要搜索的元素 小
return this.recursiveContains(node.right, element);
// 兩個元素相等
else return true;
}
// 找到二分搜索樹中的最大值的元素 +
maximum() {
if (this.size === 0) throw new Error('binary search tree is empty.');
return this.recursiveMaximum(this.root).element;
}
// 找到二分搜索樹中的最大值的元素的節點 遞歸算法 -
recursiveMaximum(node) {
// 解決最基本的問題 向右走再也走不動了,說明當前節點就是最大值節點。
if (node.right === null) return node;
return this.recursiveMaximum(node.right);
}
// 刪除二分搜索樹中最大值的元素的節點,並返回這個節點的元素 +
removeMax() {
let maxElement = this.maximum();
this.root = this.recursiveRemoveMax(this.root);
return maxElement;
}
// 刪除二分搜索樹中最大值的元素的節點,並返回這個節點 遞歸算法 -
recursiveRemoveMax(node) {
if (node.right === null) {
// 先存 當前這個節點的左子樹,
// 由於可能當前這個節點僅僅沒有右子樹,只有左子樹,
// 那麼左子樹能夠替代當前這個節點。
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
node.right = this.recursiveRemoveMax(node.right);
return node;
}
// 找到二分搜索樹中的最小值 +
minimum() {
if (this.size === 0) throw new Error('binary search tree is empty.');
return this.recursiveMinimum(this.root).element;
}
// 找到二分搜索樹中的最小值的元素的節點 遞歸算法 -
recursiveMinimum(node) {
if (node.left === null) return node;
return this.recursiveMinimum(node.left);
}
// 刪除二分搜索樹中最小值的元素的節點,並返回這個節點的元素 +
removeMin() {
let leftNode = this.minimum();
this.root = this.recursiveRemoveMin(this.root);
return leftNode;
}
// 刪除二分搜索樹中最小值的元素的節點,並返回這個節點 遞歸算法 -
recursiveRemoveMin(node) {
// 解決最簡單的問題
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
}
// 將複雜的問題拆分爲性質相同的小問題,
// 而後求出這些小問題的解後構建出原問題的答案
node.left = this.recursiveRemoveMin(node.left);
return node;
}
// 刪除二分搜索樹上的任意節點
remove(element) {
this.root = this.recursiveRemove(this.root, element);
}
// 刪除二分搜索樹上的任意節點 遞歸算法
// 返回刪除對應元素節點後新的二分搜索樹的根
recursiveRemove(node, element) {
if (node === null) return null;
// 當前節點的元素值比待刪除的元素小 那麼就向當前節點的右子樹中去找
if (this.compare(node.element, element) < 0) {
node.right = this.recursiveRemove(node.right, element);
return node;
} else if (this.compare(node.element, element) > 0) {
// 向當前節點的左子樹中去找
node.left = this.recursiveRemove(node.left, element);
return node;
} else {
// 若是找到了相同值的節點了,開始進行相應的處理
// 若是這個節點左子樹爲空,那麼就讓這個節點的右子樹覆蓋當前節點
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
}
// 若是當前節點的右子樹爲空,那麼就讓這個節點的左子樹覆蓋當前節點
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 若是當前節點的左右子樹都不爲空,那麼就開始特殊操做
// 1. 先找到當前節點右子樹上最小的那個節點,保存起來
// 2. 而後刪除掉當前節點右子樹上最小的那個節點,
// 3. 讓保存起來的那個節點覆蓋掉當前節點
// 1. 也就是保存起來的那個節點的right = 刪除掉當前節點右子樹上最小的節點後返回的那個節點
// 2. 再讓保存起來的那個節點的left = 當前節點的left
// 4. 解除當前節點及其left和right,全都賦值爲null,這樣就至關於把當前節點從二分搜索樹中剔除了
// 5. 返回保存的這個節點
let successtor = this.recursiveMinimum(node.right);
successtor.right = this.recursiveRemoveMin(node.right);
// 恢復removeMin 操做的this.size -- 帶來的影響
this.size++;
successtor.left = node.left;
// 開始正式的刪除當前節點的操做
node = node.left = node.right = null;
this.size--;
// 返回當前保存的節點
return successtor;
}
}
// 前序遍歷 +
preOrder(operator) {
this.recursivePreOrder(this.root, operator);
}
// 前序遍歷 遞歸算法 -
recursivePreOrder(node, operator) {
if (node === null) return;
// 調用一下操做方法
operator(node.element);
console.log(node, node.element);
// 繼續遞歸遍歷左右子樹
this.recursivePreOrder(node.left, operator);
this.recursivePreOrder(node.right, operator);
}
// 前序遍歷 非遞歸算法 +
nonRecursivePreOrder(operator) {
let stack = new MyLinkedListStack();
stack.push(this.root);
let node = null;
while (!stack.isEmpty()) {
// 出棧操做
node = stack.pop();
operator(node.element); // 訪問當前的節點
console.log(node.element);
// 棧是先入後出的,把須要後訪問的節點 先放進去,先訪問的節點後放進去
// 前序遍歷是訪問當前節點,而後再遍歷左子樹,最後遍歷右子樹
if (node.right !== null) stack.push(node.right);
if (node.left !== null) stack.push(node.left);
}
}
// 中序遍歷 +
inOrder(operator) {
this.recursiveInOrder(this.root, operator);
}
// 中序遍歷 遞歸算法 -
recursiveInOrder(node, operator) {
if (node == null) return;
this.recursiveInOrder(node.left, operator);
operator(node.element);
console.log(node.element);
this.recursiveInOrder(node.right, operator);
}
// 後序遍歷 +
postOrder(operator) {
this.recursivePostOrder(this.root, operator);
}
// 後序遍歷 遞歸算法 -
recursivePostOrder(node, operator) {
if (node == null) return;
this.recursivePostOrder(node.left, operator);
this.recursivePostOrder(node.right, operator);
operator(node.element);
console.log(node.element);
}
// 層序遍歷
levelOrder(operator) {
let queue = new MyLinkedListQueue();
queue.enqueue(this.root);
let node = null;
while (!queue.isEmpty()) {
node = queue.dequeue();
operator(node.element);
console.log(node.element);
// 隊列 是先進先出的,因此從左往右入隊
// 棧 是後進先出的, 因此從右往左入棧
if (node.left !== null) queue.enqueue(node.left);
if (node.right !== null) queue.enqueue(node.right);
}
}
// 獲取二分搜索樹中節點個數 +
getSize() {
return this.size;
}
// 返回二分搜索樹是否爲空的bool值 +
isEmpty() {
return this.size === 0;
}
// 新增一個比較的方法,專門用來比較新增的元素大小 -
// 第一個元素比第二個元素大 就返回 1
// 第一個元素比第二個元素小 就返回 -1
// 第一個元素比第二個元素相等 就返回 0
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is null. can't compare.");
// 先直接寫死
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 輸出二分搜索樹中的信息
// @Override toString 2018-11-03-jwl
toString() {
let treeInfo = '';
treeInfo += this.getBinarySearchTreeString(this.root, 0, treeInfo);
return treeInfo;
}
// 寫一個輔助函數,用來生成二分搜索樹信息的字符串
getBinarySearchTreeString(node, depth, treeInfo, pageContent = '') {
//之前序遍歷的方式
if (node === null) {
treeInfo += this.getDepthString(depth) + 'null \r\n';
pageContent = this.getDepthString(depth) + 'null<br /><br />';
document.body.innerHTML += `${pageContent}`;
return treeInfo;
}
treeInfo += this.getDepthString(depth) + node.element + '\r\n';
pageContent =
this.getDepthString(depth) + node.element + '<br /><br />';
document.body.innerHTML += `${pageContent}`;
treeInfo = this.getBinarySearchTreeString(
node.left,
depth + 1,
treeInfo
);
treeInfo = this.getBinarySearchTreeString(
node.right,
depth + 1,
treeInfo
);
return treeInfo;
}
// 寫一個輔助函數,用來生成遞歸深度字符串
getDepthString(depth) {
let depthString = '';
for (var i = 0; i < depth; i++) {
depthString += '-- ';
}
return depthString;
}
}
複製代碼
MyBSTSetgithub
// 自定義二分搜索樹集合Set
class MyBinarySearchTreeSet {
constructor() {
// 借用二分搜索樹來實現這個接口
this.myBinarySearchTree = new MyBinarySearchTree();
}
// 添加元素
add(element) {
this.myBinarySearchTree.add(element);
}
// 移除元素
remove(element) {
this.myBinarySearchTree.remove(element);
}
// 是否包含這個元素
contains(element) {
return this.myBinarySearchTree.contains(element);
}
// 遍歷操做
// 第一個參數 是回掉函數,
// 第二個參數 是遍歷的方式 深度優先遍歷(前pre、中in、後post),廣度優先遍歷(層序level)
each(operator, method) {
// 遍歷方式默認是非遞歸的前序遍歷,
// 其它的遍歷方式就是遞歸的前、中、後、層序遍歷。
switch (method) {
case 'pre':
this.myBinarySearchTree.preOrder(operator);
break;
case 'in':
this.myBinarySearchTree.inOrder(operator);
break;
case 'post':
this.myBinarySearchTree.postOrder(operator);
break;
case 'level':
this.myBinarySearchTree.levelOrder(operator);
break;
default:
this.myBinarySearchTree.nonRecursivePreOrder(operator);
break;
}
}
// 獲取集合中實際的元素個數
getSize() {
return this.myBinarySearchTree.getSize();
}
// 返回集合是否爲空的bool值
isEmpty() {
return this.myBinarySearchTree.isEmpty();
}
}
複製代碼
Main面試
// main 函數
class Main {
constructor() {
this.alterLine('MyBinarySearchTreeSet Area');
{
let n = 5;
let set = new MyBinarySearchTreeSet();
let random = Math.random;
let temp = null;
for (var i = 0; i < n; i++) {
temp = random();
set.add(n * n * n * temp);
set.add(n * n * n * temp);
set.add(n * n * n * temp);
set.add(n * n * n * temp);
set.add(n * n * n * temp);
set.add(n * n * n * temp);
set.add(n * n * n * temp);
}
console.log(set.getSize());
this.show(set.getSize());
let array = new MyArray(n);
set.each(element => {
console.log(element);
this.show(element);
array.add(element);
});
for (var i = 0; i < array.getSize(); i++) {
set.remove(array.get(i));
}
console.log(set.getSize());
this.show(set.getSize());
}
}
// 將內容顯示在頁面上
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();
};
複製代碼
集合設計的是一個接口,算法
二分搜索樹和鏈表都屬於動態數據結構數據庫
// 二分搜索樹的Node
class Node {
e; // Element
left; // Node
right; // Node
}
// 鏈表的Node
class Node {
e; // Element
next; // Node
}
複製代碼
MySet
void add (e)
: 不能添加劇復元素void remove (e)
boolean conatains (e)
int getSize ()
boolean isEmpty ()
( class: MyLinkedList, class: MyLinkedListSet)
MyLinkedList
// 自定義鏈表節點
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// 將一個數組對象 轉換爲一個鏈表 而且追加到當前節點上
appendToLinkedListNode(array) {
let head = null;
if (this.element === null) {
// 頭部添加
head = this;
head.element = array[0];
head.next = null;
} else {
// 插入式
head = new MyLinkedListNode(array[0], null);
head.next = this.next;
this.next = head;
}
// 添加節點的方式 頭部添加、尾部添加、中間插入
// 尾部添加節點的方式
for (var i = 1; i < array.length; i++) {
head.next = new MyLinkedListNode(array[i], null);
head = head.next;
}
}
//@override
// 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;
}
}
複製代碼
MyLinkedListSet
// 自定義鏈表集合Set
class MyLinkedListSet {
//
constructor() {
this.myLinkedList = new MyLinkedList();
}
add(element) {
if (!this.myLinkedList.contains(element))
this.myLinkedList.addFirst(element);
}
remove(element) {
this.myLinkedList.removeElement(element);
}
contains(element) {
return this.myLinkedList.contains(element);
}
each(operator) {
let size = this.myLinkedList.getSize();
for (var i = 0; i < size; i++) {
operator(this.myLinkedList.get(i));
}
}
getSize() {
return this.myLinkedList.getSize();
}
isEmpty() {
return this.myLinkedList.isEmpty();
}
}
複製代碼
O(n)
,O(h) or O(log n)
,h = log2 (n+1) = O(log2 n)
,O(log2 n)
1*n、2*n、100*n、10000*n
,它們都是線性的一個關係,O(log n)
。O(log n)
,O(log n)
實際上是一個最優的狀況,O(log n)
這個級別,[1,2,3,4,5,6]
,O(n)
這樣的性能,O(log n)
的時間複雜度,O(n)
,O(h)
,log n
,若是很是的不巧,那麼這個 h 等於 n。n=16
的時候,logn 讓它取 2 爲底,log2 n
的值爲 4,n 的值就是 16。n=1024
的時候,logn 仍是讓它取 2 爲底,log2 n
的值爲 10,n=100萬
這個級別的數據的話,logn 仍是讓它取 2 爲底,log2 n
的值爲 20,O(log n)
這個算法和O(n)
這個算法,O(log n)
這個算法花一秒時間就跑完的話,O(n)
這個算法就須要花 5 萬秒,大概 14 個小時,O(log n)
這個算法要花一天的時間跑完這個程序,O(n)
這個算法就須要花 5 萬天,大概 137 年的事件才能跑出結果,O(log n)
算法的程序就跑出結果了,O(n)
算法的程序你這一生都跑不出結果來。O(log n)
和O(n)
差很少,O(n)
O(1)
,O(n)
。O(n)
O(n)
O(n)
O(n)
O(h) or O(log n)
高度
,O(h)
這個級別的,O(h) or O(log n)
O(h) or O(log n)
2^(1-1) + 2^(2-1) + ... + 2^(h-1)
= 1 x (1-2^(h)) / (1-2) = 2^(h) - 1 = n
。h = log2 (n+1) = O(log2 n)
,O(log2 n)
1*n、2*n、100*n、10000*n
,它們都是線性的一個關係,O(log n)
。(class: MyLinkedList, class: MyBinarySearchTree, class: PerformanceTest,
class: MyLinkedListSet, class:MyBSTSet , class: Main)
MyLinkedList
// 自定義鏈表節點
class MyLinkedListNode {
constructor(element = null, next = null) {
this.element = element;
this.next = next;
}
// 將一個數組對象 轉換爲一個鏈表 而且追加到當前節點上
appendToLinkedListNode(array) {
let head = null;
if (this.element === null) {
// 頭部添加
head = this;
head.element = array[0];
head.next = null;
} else {
// 插入式
head = new MyLinkedListNode(array[0], null);
head.next = this.next;
this.next = head;
}
// 添加節點的方式 頭部添加、尾部添加、中間插入
// 尾部添加節點的方式
for (var i = 1; i < array.length; i++) {
head.next = new MyLinkedListNode(array[i], null);
head = head.next;
}
}
//@override
// 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);
}
// 新增:根據元素來刪除鏈表中的元素 2018-11-05
removeElement(element) {
let prev = this.dummyHead;
while (prev.next !== null) {
if (prev.next.element === element) break;
prev = prev.next;
}
if (prev.next !== null) {
let delNode = prev.next;
prev.next = delNode.next;
delNode = null;
this.size--;
}
}
// 輸出鏈表中的信息
// @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;
}
}
複製代碼
MyBinarySearchTree
// 自定義二分搜索樹節點
class MyBinarySearchTreeNode {
constructor(element, left = null, right = null) {
// 實際存儲的元素
this.element = element;
// 當前節點的左子樹
this.left = left;
// 當前節點的右子樹
this.right = right;
}
}
// 自定義二分搜索樹
class MyBinarySearchTree {
constructor() {
this.root = null;
this.size = 0;
}
// 添加元素到二分搜索樹中 +
add(element) {
if (element === null) throw new Error("element is null. can't store.");
this.root = this.recursiveAdd(this.root, element);
}
// 添加元素到二分搜索樹中 遞歸算法 -
recursiveAdd(node, newElement) {
// 解決最基本的問題 也就是遞歸函數調用的終止條件
if (node === null) {
this.size++;
return new MyBinarySearchTreeNode(newElement);
}
// 1. 當前節點的元素比新元素大
// 那麼新元素就會被添加到當前節點的左子樹去
// 2. 當前節點的元素比新元素小
// 那麼新元素就會被添加到當前節點的右子樹去
// 3. 當前節點的元素比新元素相等
// 什麼都不作了,由於目前不添加劇復的元素
if (this.compare(node.element, newElement) > 0)
node.left = this.recursiveAdd(node.left, newElement);
else if (this.compare(node.element, newElement) < 0)
node.right = this.recursiveAdd(node.right, newElement);
else {
}
// 將複雜問題分解成多個性質相同的小問題,
// 而後求出小問題的答案,
// 最終構建出原問題的答案
return node;
}
// 判斷二分搜索樹中是否包含某個元素 +
contains(element) {
if (this.root === null) throw new Error("root is null. can't query.");
return this.recursiveContains(this.root, element);
}
// 判斷二分搜索樹種是否包含某個元素 遞歸算法 -
recursiveContains(node, element) {
if (node === null) return false;
// 當前節點元素比 要搜索的元素 大
if (this.compare(node.element, element) > 0)
return this.recursiveContains(node.left, element);
else if (this.compare(node.element, element) < 0)
// 當前元素比 要搜索的元素 小
return this.recursiveContains(node.right, element);
// 兩個元素相等
else return true;
}
// 找到二分搜索樹中的最大值的元素 +
maximum() {
if (this.size === 0) throw new Error('binary search tree is empty.');
return this.recursiveMaximum(this.root).element;
}
// 找到二分搜索樹中的最大值的元素的節點 遞歸算法 -
recursiveMaximum(node) {
// 解決最基本的問題 向右走再也走不動了,說明當前節點就是最大值節點。
if (node.right === null) return node;
return this.recursiveMaximum(node.right);
}
// 刪除二分搜索樹中最大值的元素的節點,並返回這個節點的元素 +
removeMax() {
let maxElement = this.maximum();
this.root = this.recursiveRemoveMax(this.root);
return maxElement;
}
// 刪除二分搜索樹中最大值的元素的節點,並返回這個節點 遞歸算法 -
recursiveRemoveMax(node) {
if (node.right === null) {
// 先存 當前這個節點的左子樹,
// 由於可能當前這個節點僅僅沒有右子樹,只有左子樹,
// 那麼左子樹能夠替代當前這個節點。
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
node.right = this.recursiveRemoveMax(node.right);
return node;
}
// 找到二分搜索樹中的最小值 +
minimum() {
if (this.size === 0) throw new Error('binary search tree is empty.');
return this.recursiveMinimum(this.root).element;
}
// 找到二分搜索樹中的最小值的元素的節點 遞歸算法 -
recursiveMinimum(node) {
if (node.left === null) return node;
return this.recursiveMinimum(node.left);
}
// 刪除二分搜索樹中最小值的元素的節點,並返回這個節點的元素 +
removeMin() {
let leftNode = this.minimum();
this.root = this.recursiveRemoveMin(this.root);
return leftNode;
}
// 刪除二分搜索樹中最小值的元素的節點,並返回這個節點 遞歸算法 -
recursiveRemoveMin(node) {
// 解決最簡單的問題
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
}
// 將複雜的問題拆分爲性質相同的小問題,
// 而後求出這些小問題的解後構建出原問題的答案
node.left = this.recursiveRemoveMin(node.left);
return node;
}
// 刪除二分搜索樹上的任意節點
remove(element) {
this.root = this.recursiveRemove(this.root, element);
}
// 刪除二分搜索樹上的任意節點 遞歸算法
// 返回刪除對應元素節點後新的二分搜索樹的根
recursiveRemove(node, element) {
if (node === null) return null;
// 當前節點的元素值比待刪除的元素小 那麼就向當前節點的右子樹中去找
if (this.compare(node.element, element) < 0) {
node.right = this.recursiveRemove(node.right, element);
return node;
} else if (this.compare(node.element, element) > 0) {
// 向當前節點的左子樹中去找
node.left = this.recursiveRemove(node.left, element);
return node;
} else {
// 若是找到了相同值的節點了,開始進行相應的處理
// 若是這個節點左子樹爲空,那麼就讓這個節點的右子樹覆蓋當前節點
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
}
// 若是當前節點的右子樹爲空,那麼就讓這個節點的左子樹覆蓋當前節點
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 若是當前節點的左右子樹都不爲空,那麼就開始特殊操做
// 1. 先找到當前節點右子樹上最小的那個節點,保存起來
// 2. 而後刪除掉當前節點右子樹上最小的那個節點,
// 3. 讓保存起來的那個節點覆蓋掉當前節點
// 1. 也就是保存起來的那個節點的right = 刪除掉當前節點右子樹上最小的節點後返回的那個節點
// 2. 再讓保存起來的那個節點的left = 當前節點的left
// 4. 解除當前節點及其left和right,全都賦值爲null,這樣就至關於把當前節點從二分搜索樹中剔除了
// 5. 返回保存的這個節點
let successtor = this.recursiveMinimum(node.right);
successtor.right = this.recursiveRemoveMin(node.right);
// 恢復removeMin 操做的this.size -- 帶來的影響
this.size++;
successtor.left = node.left;
// 開始正式的刪除當前節點的操做
node = node.left = node.right = null;
this.size--;
// 返回當前保存的節點
return successtor;
}
}
// 前序遍歷 +
preOrder(operator) {
this.recursivePreOrder(this.root, operator);
}
// 前序遍歷 遞歸算法 -
recursivePreOrder(node, operator) {
if (node === null) return;
// 調用一下操做方法
operator(node.element);
console.log(node, node.element);
// 繼續遞歸遍歷左右子樹
this.recursivePreOrder(node.left, operator);
this.recursivePreOrder(node.right, operator);
}
// 前序遍歷 非遞歸算法 +
nonRecursivePreOrder(operator) {
let stack = new MyLinkedListStack();
stack.push(this.root);
let node = null;
while (!stack.isEmpty()) {
// 出棧操做
node = stack.pop();
operator(node.element); // 訪問當前的節點
console.log(node.element);
// 棧是先入後出的,把須要後訪問的節點 先放進去,先訪問的節點後放進去
// 前序遍歷是訪問當前節點,而後再遍歷左子樹,最後遍歷右子樹
if (node.right !== null) stack.push(node.right);
if (node.left !== null) stack.push(node.left);
}
}
// 中序遍歷 +
inOrder(operator) {
this.recursiveInOrder(this.root, operator);
}
// 中序遍歷 遞歸算法 -
recursiveInOrder(node, operator) {
if (node == null) return;
this.recursiveInOrder(node.left, operator);
operator(node.element);
console.log(node.element);
this.recursiveInOrder(node.right, operator);
}
// 後序遍歷 +
postOrder(operator) {
this.recursivePostOrder(this.root, operator);
}
// 後序遍歷 遞歸算法 -
recursivePostOrder(node, operator) {
if (node == null) return;
this.recursivePostOrder(node.left, operator);
this.recursivePostOrder(node.right, operator);
operator(node.element);
console.log(node.element);
}
// 層序遍歷
levelOrder(operator) {
let queue = new MyLinkedListQueue();
queue.enqueue(this.root);
let node = null;
while (!queue.isEmpty()) {
node = queue.dequeue();
operator(node.element);
console.log(node.element);
// 隊列 是先進先出的,因此從左往右入隊
// 棧 是後進先出的, 因此從右往左入棧
if (node.left !== null) queue.enqueue(node.left);
if (node.right !== null) queue.enqueue(node.right);
}
}
// 獲取二分搜索樹中節點個數 +
getSize() {
return this.size;
}
// 返回二分搜索樹是否爲空的bool值 +
isEmpty() {
return this.size === 0;
}
// 新增一個比較的方法,專門用來比較新增的元素大小 -
// 第一個元素比第二個元素大 就返回 1
// 第一個元素比第二個元素小 就返回 -1
// 第一個元素比第二個元素相等 就返回 0
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is null. can't compare.");
// 先直接寫死
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 輸出二分搜索樹中的信息
// @Override toString 2018-11-03-jwl
toString() {
let treeInfo = '';
treeInfo += this.getBinarySearchTreeString(this.root, 0, treeInfo);
return treeInfo;
}
// 寫一個輔助函數,用來生成二分搜索樹信息的字符串
getBinarySearchTreeString(node, depth, treeInfo, pageContent = '') {
//之前序遍歷的方式
if (node === null) {
treeInfo += this.getDepthString(depth) + 'null \r\n';
pageContent = this.getDepthString(depth) + 'null<br /><br />';
document.body.innerHTML += `${pageContent}`;
return treeInfo;
}
treeInfo += this.getDepthString(depth) + node.element + '\r\n';
pageContent =
this.getDepthString(depth) + node.element + '<br /><br />';
document.body.innerHTML += `${pageContent}`;
treeInfo = this.getBinarySearchTreeString(
node.left,
depth + 1,
treeInfo
);
treeInfo = this.getBinarySearchTreeString(
node.right,
depth + 1,
treeInfo
);
return treeInfo;
}
// 寫一個輔助函數,用來生成遞歸深度字符串
getDepthString(depth) {
let depthString = '';
for (var i = 0; i < depth; i++) {
depthString += '-- ';
}
return depthString;
}
}
複製代碼
PerformanceTest
// 性能測試
class PerformanceTest {
constructor() {}
// 對比都列
testQueue(queue, openCount) {
let startTime = Date.now();
let random = Math.random;
for (var i = 0; i < openCount; i++) {
queue.enqueue(random() * openCount);
}
while (!queue.isEmpty()) {
queue.dequeue();
}
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 對比棧
testStack(stack, openCount) {
let startTime = Date.now();
let random = Math.random;
for (var i = 0; i < openCount; i++) {
stack.push(random() * openCount);
}
while (!stack.isEmpty()) {
stack.pop();
}
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 對比集合
testSet(set, openCount) {
let startTime = Date.now();
let random = Math.random;
let arr = [];
let temp = null;
// 第一遍測試
for (var i = 0; i < openCount; i++) {
temp = random();
// 添加劇復元素,從而測試集合去重的能力
set.add(temp * openCount);
set.add(temp * openCount);
arr.push(temp * openCount);
}
for (var i = 0; i < openCount; i++) {
set.remove(arr[i]);
}
// 第二遍測試
for (var i = 0; i < openCount; i++) {
set.add(arr[i]);
set.add(arr[i]);
}
while (!set.isEmpty()) {
set.remove(arr[set.getSize() - 1]);
}
let endTime = Date.now();
// 求出兩次測試的平均時間
let avgTime = Math.ceil((endTime - startTime) / 2);
return this.calcTime(avgTime);
}
// 計算運行的時間,轉換爲 天-小時-分鐘-秒-毫秒
calcTime(result) {
//獲取距離的天數
var day = Math.floor(result / (24 * 60 * 60 * 1000));
//獲取距離的小時數
var hours = Math.floor((result / (60 * 60 * 1000)) % 24);
//獲取距離的分鐘數
var minutes = Math.floor((result / (60 * 1000)) % 60);
//獲取距離的秒數
var seconds = Math.floor((result / 1000) % 60);
//獲取距離的毫秒數
var milliSeconds = Math.floor(result % 1000);
// 計算時間
day = day < 10 ? '0' + day : day;
hours = hours < 10 ? '0' + hours : hours;
minutes = minutes < 10 ? '0' + minutes : minutes;
seconds = seconds < 10 ? '0' + seconds : seconds;
milliSeconds =
milliSeconds < 100
? milliSeconds < 10
? '00' + milliSeconds
: '0' + milliSeconds
: milliSeconds;
// 輸出耗時字符串
result =
day +
'天' +
hours +
'小時' +
minutes +
'分' +
seconds +
'秒' +
milliSeconds +
'毫秒' +
' <<<<============>>>> 總毫秒數:' +
result;
return result;
}
}
複製代碼
MyLinkedListSet
// 自定義鏈表集合Set
class MyLinkedListSet {
//
constructor() {
this.myLinkedList = new MyLinkedList();
}
add(element) {
if (!this.myLinkedList.contains(element))
this.myLinkedList.addFirst(element);
}
remove(element) {
this.myLinkedList.removeElement(element);
}
contains(element) {
return this.myLinkedList.contains(element);
}
each(operator) {
let size = this.myLinkedList.getSize();
for (var i = 0; i < size; i++) {
operator(this.myLinkedList.get(i));
}
}
getSize() {
return this.myLinkedList.getSize();
}
isEmpty() {
return this.myLinkedList.isEmpty();
}
}
複製代碼
MyBSTSet
// 自定義二分搜索樹集合Set
class MyBinarySearchTreeSet {
constructor() {
// 借用二分搜索樹來實現這個接口
this.myBinarySearchTree = new MyBinarySearchTree();
}
// 添加元素
add(element) {
this.myBinarySearchTree.add(element);
}
// 移除元素
remove(element) {
this.myBinarySearchTree.remove(element);
}
// 是否包含這個元素
contains(element) {
return this.myBinarySearchTree.contains(element);
}
// 遍歷操做
// 第一個參數 是回掉函數,
// 第二個參數 是遍歷的方式 深度優先遍歷(前pre、中in、後post),廣度優先遍歷(層序level)
each(operator, method) {
// 遍歷方式默認是非遞歸的前序遍歷,
// 其它的遍歷方式就是遞歸的前、中、後、層序遍歷。
switch (method) {
case 'pre':
this.myBinarySearchTree.preOrder(operator);
break;
case 'in':
this.myBinarySearchTree.inOrder(operator);
break;
case 'post':
this.myBinarySearchTree.postOrder(operator);
break;
case 'level':
this.myBinarySearchTree.levelOrder(operator);
break;
default:
this.myBinarySearchTree.nonRecursivePreOrder(operator);
break;
}
}
// 獲取集合中實際的元素個數
getSize() {
return this.myBinarySearchTree.getSize();
}
// 返回集合是否爲空的bool值
isEmpty() {
return this.myBinarySearchTree.isEmpty();
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('Set Comparison Area');
let myLinkedListSet = new MyLinkedListSet();
let myBinarySearchTreeSet = new MyBinarySearchTreeSet();
let performanceTest = new PerformanceTest();
let myLinkedListSetInfo = performanceTest.testSet(
myLinkedListSet,
5000
);
let myBinarySearchTreeSetInfo = performanceTest.testSet(
myBinarySearchTreeSet,
5000
);
this.alterLine('MyLinkedListSet Area');
console.log(myLinkedListSetInfo);
this.show(myLinkedListSetInfo);
this.alterLine('MyBinarySearchTreeSet Area');
console.log(myBinarySearchTreeSetInfo);
this.show(myBinarySearchTreeSetInfo);
}
// 將內容顯示在頁面上
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(n)
的狀況,O(logn)
這種級別,804.惟一摩爾斯密碼詞
網址:https://leetcode-cn.com/problems/unique-morse-code-words/
解答
// 答題
class Solution {
// leetcode 804. 惟一摩爾斯密碼詞
uniqueMorseRepresentations(words) {
/** * @param {string[]} words * @return {number} * 使用本身的二分搜索樹來實現 */
var uniqueMorseRepresentations = function(words) {
// 摩斯碼
const codes = [
'.-',
'-...',
'-.-.',
'-..',
'.',
'..-.',
'--.',
'....',
'..',
'.---',
'-.-',
'.-..',
'--',
'-.',
'---',
'.--.',
'--.-',
'.-.',
'...',
'-',
'..-',
'...-',
'.--',
'-..-',
'-.--',
'--..'
];
const myBinarySearchTreeSet = new MyBinarySearchTreeSet();
let content = '';
// 獲取起始字符的aceii碼,
// 從而能夠求出某個單詞的每個字符在字母表中佔的位置索引,
// 根據這些位置索引就能夠在摩斯表中找到相應的摩斯碼,
// 一個單詞就是一組摩斯碼,而後使用set添加,就能夠直接實現去重的操做了
const start = 'a'.charCodeAt(0);
for (const word of words) {
for (const w of word) content += codes[w.charCodeAt(0) - start];
myBinarySearchTreeSet.add(content);
content = '';
}
return myBinarySearchTreeSet.getSize();
};
/** * @param {string[]} words * @return {number} * 使用系統內置的Set集合類 */
var uniqueMorseRepresentations = function(words) {
// 摩斯碼
const codes = [
'.-',
'-...',
'-.-.',
'-..',
'.',
'..-.',
'--.',
'....',
'..',
'.---',
'-.-',
'.-..',
'--',
'-.',
'---',
'.--.',
'--.-',
'.-.',
'...',
'-',
'..-',
'...-',
'.--',
'-..-',
'-.--',
'--..'
];
const set = new Set();
let content = '';
// 獲取起始字符的aceii碼,
// 從而能夠求出某個單詞的每個字符在字母表中佔的位置索引,
// 根據這些位置索引就能夠在摩斯表中找到相應的摩斯碼,
// 一個單詞就是一組摩斯碼,而後使用set添加,就能夠直接實現去重的操做了
const start = 'a'.charCodeAt(0);
for (const word of words) {
for (const w of word) content += codes[w.charCodeAt(0) - start];
set.add(content);
content = '';
}
return set.size;
};
return uniqueMorseRepresentations(words);
}
}
// main 函數
class Main {
constructor() {
this.alterLine('leetcode 804.惟一摩爾斯密碼詞');
let s = new Solution();
let words = ['gin', 'zen', 'gig', 'msg'];
this.show(s.uniqueMorseRepresentations(words));
}
// 將內容顯示在頁面上
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();
};
複製代碼
高中數學裏的函數就能夠理解成是一種映射
f(x)=2*x+1
,在映域中每取出一個值,映射關係你也能夠把它稱之爲字典
生活中的映射的應用
dict:key ----> value
字典:單詞 ----> 釋意
名冊:身份證號 ----> 人
車輛管理:車牌號 ----> 車
數據庫:id ----> 信息
詞頻統計:單詞 ----> 頻率
存儲(鍵,值)數據對的數據結構(key,value)
能夠很是容易的使用鏈表或者二分搜索樹來實現映射。
// 鏈表實現時的Node
class Node {
key; // Key
value; //Value
Node next;// Node
}
// 二分搜索樹實現時的Node
class Node {
key; // Key
value; //Value
left;// Node
right;// Node
}
複製代碼
MyMap
void add(k, v)
V remove(k)
boolean contains(k)
V get(k)
void set(k, v)
int getSize()
boolean isEmpty()
(class: MyLinkedListMap)
MyLinkedListMap
// 自定義鏈表映射節點 LinkedListMapNode
class MyLinkedListMapNode {
constructor(key = null, value = null, next = null) {
this.key = key;
this.value = value;
this.next = next;
}
// @Override toString 2018-11-5-jwl
toString() {
return this.key.toString() + '---------->' + this.value.toString();
}
}
// 自定義鏈表映射 Map
class MyLinkedListMap {
constructor() {
this.dummyHead = new MyLinkedListMapNode();
this.size = 0;
}
// 根據key獲取節點 -
getNode(key) {
let cur = this.dummyHead.next;
while (cur !== null) {
if (cur.key === key) return cur;
cur = cur.next;
}
return null;
}
// 添加操做 +
add(key, value) {
let node = this.getNode(key);
// 這個節點若是存在就 覆蓋值便可
if (node !== null) node.value = value;
else {
// 若是不存在,那麼就在頭部添加如下
let newNode = new MyLinkedListMapNode(key, value);
newNode.next = this.dummyHead.next;
this.dummyHead.next = newNode;
this.size++;
}
}
// 刪除操做 返回被刪除的元素 +
remove(key) {
let prev = this.dummyHead;
// 循環查找
while (prev.next !== null) {
if (prev.next.key === key) break;
prev = prev.next;
}
// 若是觸碰了break, 那就知足條件
if (prev.next !== null) {
let delNode = prev.next;
prev.next = delNode.next;
let value = delNode.value;
devNode = delNode.next = null;
this.size--;
return value;
}
// 若是沒有觸屏break 那就返回空值回去
return null;
}
// 查詢操做 返回查詢到的元素 +
get(key) {
let node = this.getNode(key);
if (node === null) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(key);
if (node === null) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含該key的元素的判斷值 +
contains(key) {
return this.getNode(key) !== null;
}
// 返回映射中實際的元素個數 +
getSize() {
return this.size;
}
// 返回映射中是否爲空的判斷值 +
isEmpty() {
return this.size === 0;
}
// @Override toString() 2018-11-05-jwl
toString() {
let mapInfo = `MyLinkedListMap: size = ${this.size}, data = [ `;
document.body.innerHTML += `MyLinkedListMap: size = ${ this.size }, data = [ <br/><br/>`;
let cur = this.dummyHead.next;
for (var i = 0; i < this.size - 1; i++) {
mapInfo += ` ${cur.toString()}, \r\n`;
document.body.innerHTML += ` ${cur.toString()}, <br/><br/>`;
cur = cur.next;
}
if (cur !== null) {
mapInfo += ` ${cur.toString()} \r\n`;
document.body.innerHTML += ` ${cur.toString()} <br/><br/>`;
}
mapInfo += ` ] \r\n`;
document.body.innerHTML += ` ] <br/><br/>`;
return mapInfo;
}
}
複製代碼
(class: MyBSTMap)
MyBSTMap
// 自定義二分搜索樹樹映射節點 TreeMapNode
class MyBinarySearchTreeMapNode {
constructor(key = null, value = null, left = null, right = null) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
// @Override toString 2018-11-5-jwl
toString() {
return this.key.toString() + '---------->' + this.value.toString();
}
}
// 自定義二分搜索樹映射 Map
class MyBinarySearchTreeMap {
constructor() {
this.root = null;
this.size = 0;
}
// 比較的功能
compare(keyA, keyB) {
if (keyA === null || keyB === null)
throw new Error("key is error. key can't compare.");
if (keyA > keyB) return 1;
else if (keyA < keyB) return -1;
else return 0;
}
// 根據key獲取節點 -
getNode(node, key) {
// 先解決最基本的問題
if (node === null) return null;
// 開始將複雜的問題 逐漸縮小規模
// 從而求出小問題的解,最後構建出原問題的解
switch (this.compare(node.key, key)) {
case 1: // 向左找
return this.getNode(node.left, key);
break;
case -1: // 向右找
return this.getNode(node.right, key);
break;
case 0: // 找到了
return node;
break;
default:
throw new Error(
'compare result is error. compare result : 0、 一、 -1 .'
);
break;
}
}
// 添加操做 +
add(key, value) {
this.root = this.recursiveAdd(this.root, key, value);
}
// 添加操做 遞歸算法 -
recursiveAdd(node, key, value) {
// 解決最簡單的問題
if (node === null) {
this.size++;
return new MyBinarySearchTreeMapNode(key, value);
}
// 將複雜的問題規模逐漸變小,
// 從而求出小問題的解,從而構建出原問題的答案
if (this.compare(node.key, key) > 0)
node.left = this.recursiveAdd(node.left, key, value);
else if (this.compare(node.key, key) < 0)
node.right = this.recursiveAdd(node.right, key, value);
else node.value = value;
return node;
}
// 刪除操做 返回被刪除的元素 +
remove(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
this.root = this.recursiveRemove(this.root, key);
return node.value;
}
// 刪除操做 遞歸算法 +
recursiveRemove(node, key) {
// 解決最基本的問題
if (node === null) return null;
if (this.compare(node.key, key) > 0) {
node.left = this.recursiveRemove(node.left, key);
return node;
} else if (this.compare(node.key, key) < 0) {
node.right = this.recursiveRemove(node.right, key);
return node;
} else {
// 當前節點的key 與 待刪除的key的那個節點相同
// 有三種狀況
// 1. 當前節點沒有左子樹,那麼只有讓當前節點的右子樹直接覆蓋當前節點,就表示當前節點被刪除了
// 2. 當前節點沒有右子樹,那麼只有讓當前節點的左子樹直接覆蓋當前節點,就表示當前節點被刪除了
// 3. 當前節點左右子樹都有, 那麼又分兩種狀況,使用前驅刪除法或者後繼刪除法
// 1. 前驅刪除法:使用當前節點的左子樹上最大的那個節點覆蓋當前節點
// 2. 後繼刪除法:使用當前節點的右子樹上最小的那個節點覆蓋當前節點
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
} else if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
} else {
let predecessor = this.maximum(node.left);
node.left = this.removeMax(node.left);
this.size++;
// 開始嫁接 當前節點的左右子樹
predecessor.left = node.left;
predecessor.right = node.right;
// 將當前節點從根節點剔除
node = node.left = node.right = null;
this.size--;
// 返回嫁接後的新節點
return predecessor;
}
}
}
// 刪除操做的兩個輔助函數
// 獲取最大值、刪除最大值
// 之前驅的方式 來輔助刪除操做的函數
// 獲取最大值
maximum(node) {
// 不再能往右了,說明當前節點已是最大的了
if (node.right === null) return node;
// 將複雜的問題漸漸減少規模,從而求出小問題的解,最後用小問題的解構建出原問題的答案
return this.maximum(node.right);
}
// 刪除最大值
removeMax(node) {
// 解決最基本的問題
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 開始化歸
node.right = this.removeMax(node.right);
return node;
}
// 查詢操做 返回查詢到的元素 +
get(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(this.root, key);
if (node === null) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含該key的元素的判斷值 +
contains(key) {
return this.getNode(this.root, key) !== null;
}
// 返回映射中實際的元素個數 +
getSize() {
return this.size;
}
// 返回映射中是否爲空的判斷值 +
isEmpty() {
return this.size === 0;
}
// @Override toString() 2018-11-05-jwl
toString() {
let mapInfo = `MyBinarySearchTreeMap: size = ${this.size}, data = [ `;
document.body.innerHTML += `MyBinarySearchTreeMap: size = ${ this.size }, data = [ <br/><br/>`;
// 以非遞歸的前序遍歷 輸出字符串
let stack = new MyLinkedListStack();
stack.push(this.root);
if (this.root === null) stack.pop();
while (!stack.isEmpty()) {
let node = stack.pop();
if (node.left !== null) stack.push(node.left);
if (node.right !== null) stack.push(node.right);
if (node.left === null && node.right === null) {
mapInfo += ` ${node.toString()} \r\n`;
document.body.innerHTML += ` ${node.toString()} <br/><br/>`;
} else {
mapInfo += ` ${node.toString()}, \r\n`;
document.body.innerHTML += ` ${node.toString()}, <br/><br/>`;
}
}
mapInfo += ` ] \r\n`;
document.body.innerHTML += ` ] <br/><br/>`;
return mapInfo;
}
}
複製代碼
O(n)
O(n)
O(1)
,O(n)
。O(n)
O(n)
O(n)
O(n)
O(n)
O(n)
O(h) or O(log n)
O(h) or O(log n)
高度
,O(h)
這個級別的,O(h) or O(log n)
O(h) or O(log n)
O(h) or O(log n)
class: MyLinkedListMap, class: MyBSTMap , class: PerformanceTest, class: Main)
MyLinkedListMap
// 自定義鏈表映射節點 LinkedListMapNode
class MyLinkedListMapNode {
constructor(key = null, value = null, next = null) {
this.key = key;
this.value = value;
this.next = next;
}
// @Override toString 2018-11-5-jwl
toString() {
return this.key.toString() + '---------->' + this.value.toString();
}
}
// 自定義鏈表映射 Map
class MyLinkedListMap {
constructor() {
this.dummyHead = new MyLinkedListMapNode();
this.size = 0;
}
// 根據key獲取節點 -
getNode(key) {
let cur = this.dummyHead.next;
while (cur !== null) {
if (cur.key === key) return cur;
cur = cur.next;
}
return null;
}
// 添加操做 +
add(key, value) {
let node = this.getNode(key);
// 這個節點若是存在就 覆蓋值便可
if (node !== null) node.value = value;
else {
// 若是不存在,那麼就在頭部添加如下
let newNode = new MyLinkedListMapNode(key, value);
newNode.next = this.dummyHead.next;
this.dummyHead.next = newNode;
this.size++;
}
}
// 刪除操做 返回被刪除的元素 +
remove(key) {
let prev = this.dummyHead;
// 循環查找
while (prev.next !== null) {
if (prev.next.key === key) break;
prev = prev.next;
}
// 若是觸碰了break, 那就知足條件
if (prev.next !== null) {
let delNode = prev.next;
prev.next = delNode.next;
let value = delNode.value;
delNode = delNode.next = null;
this.size--;
return value;
}
// 若是沒有觸屏break 那就返回空值回去
return null;
}
// 查詢操做 返回查詢到的元素 +
get(key) {
let node = this.getNode(key);
if (node === null) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(key);
if (node === null) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含該key的元素的判斷值 +
contains(key) {
return this.getNode(key) !== null;
}
// 返回映射中實際的元素個數 +
getSize() {
return this.size;
}
// 返回映射中是否爲空的判斷值 +
isEmpty() {
return this.size === 0;
}
// @Override toString() 2018-11-05-jwl
toString() {
let mapInfo = `MyLinkedListMap: size = ${this.size}, data = [ `;
document.body.innerHTML += `MyLinkedListMap: size = ${ this.size }, data = [ <br/><br/>`;
let cur = this.dummyHead.next;
for (var i = 0; i < this.size - 1; i++) {
mapInfo += ` ${cur.toString()}, \r\n`;
document.body.innerHTML += ` ${cur.toString()}, <br/><br/>`;
cur = cur.next;
}
if (cur !== null) {
mapInfo += ` ${cur.toString()} \r\n`;
document.body.innerHTML += ` ${cur.toString()} <br/><br/>`;
}
mapInfo += ` ] \r\n`;
document.body.innerHTML += ` ] <br/><br/>`;
return mapInfo;
}
}
複製代碼
MyBSTMap
// 自定義二分搜索樹樹映射節點 TreeMapNode
class MyBinarySearchTreeMapNode {
constructor(key = null, value = null, left = null, right = null) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
// @Override toString 2018-11-5-jwl
toString() {
return this.key.toString() + '---------->' + this.value.toString();
}
}
// 自定義二分搜索樹映射 Map
class MyBinarySearchTreeMap {
constructor() {
this.root = null;
this.size = 0;
}
// 比較的功能
compare(keyA, keyB) {
if (keyA === null || keyB === null)
throw new Error("key is error. key can't compare.");
if (keyA > keyB) return 1;
else if (keyA < keyB) return -1;
else return 0;
}
// 根據key獲取節點 -
getNode(node, key) {
// 先解決最基本的問題
if (node === null) return null;
// 開始將複雜的問題 逐漸縮小規模
// 從而求出小問題的解,最後構建出原問題的解
switch (this.compare(node.key, key)) {
case 1: // 向左找
return this.getNode(node.left, key);
break;
case -1: // 向右找
return this.getNode(node.right, key);
break;
case 0: // 找到了
return node;
break;
default:
throw new Error(
'compare result is error. compare result : 0、 一、 -1 .'
);
break;
}
}
// 添加操做 +
add(key, value) {
this.root = this.recursiveAdd(this.root, key, value);
}
// 添加操做 遞歸算法 -
recursiveAdd(node, key, value) {
// 解決最簡單的問題
if (node === null) {
this.size++;
return new MyBinarySearchTreeMapNode(key, value);
}
// 將複雜的問題規模逐漸變小,
// 從而求出小問題的解,從而構建出原問題的答案
if (this.compare(node.key, key) > 0)
node.left = this.recursiveAdd(node.left, key, value);
else if (this.compare(node.key, key) < 0)
node.right = this.recursiveAdd(node.right, key, value);
else node.value = value;
return node;
}
// 刪除操做 返回被刪除的元素 +
remove(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
this.root = this.recursiveRemove(this.root, key);
return node.value;
}
// 刪除操做 遞歸算法 +
recursiveRemove(node, key) {
// 解決最基本的問題
if (node === null) return null;
if (this.compare(node.key, key) > 0) {
node.left = this.recursiveRemove(node.left, key);
return node;
} else if (this.compare(node.key, key) < 0) {
node.right = this.recursiveRemove(node.right, key);
return node;
} else {
// 當前節點的key 與 待刪除的key的那個節點相同
// 有三種狀況
// 1. 當前節點沒有左子樹,那麼只有讓當前節點的右子樹直接覆蓋當前節點,就表示當前節點被刪除了
// 2. 當前節點沒有右子樹,那麼只有讓當前節點的左子樹直接覆蓋當前節點,就表示當前節點被刪除了
// 3. 當前節點左右子樹都有, 那麼又分兩種狀況,使用前驅刪除法或者後繼刪除法
// 1. 前驅刪除法:使用當前節點的左子樹上最大的那個節點覆蓋當前節點
// 2. 後繼刪除法:使用當前節點的右子樹上最小的那個節點覆蓋當前節點
if (node.left === null) {
let rightNode = node.right;
node.right = null;
this.size--;
return rightNode;
} else if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
} else {
let predecessor = this.maximum(node.left);
node.left = this.removeMax(node.left);
this.size++;
// 開始嫁接 當前節點的左右子樹
predecessor.left = node.left;
predecessor.right = node.right;
// 將當前節點從根節點剔除
node = node.left = node.right = null;
this.size--;
// 返回嫁接後的新節點
return predecessor;
}
}
}
// 刪除操做的兩個輔助函數
// 獲取最大值、刪除最大值
// 之前驅的方式 來輔助刪除操做的函數
// 獲取最大值
maximum(node) {
// 不再能往右了,說明當前節點已是最大的了
if (node.right === null) return node;
// 將複雜的問題漸漸減少規模,從而求出小問題的解,最後用小問題的解構建出原問題的答案
return this.maximum(node.right);
}
// 刪除最大值
removeMax(node) {
// 解決最基本的問題
if (node.right === null) {
let leftNode = node.left;
node.left = null;
this.size--;
return leftNode;
}
// 開始化歸
node.right = this.removeMax(node.right);
return node;
}
// 查詢操做 返回查詢到的元素 +
get(key) {
let node = this.getNode(this.root, key);
if (node === null) return null;
return node.value;
}
// 修改操做 +
set(key, value) {
let node = this.getNode(this.root, key);
if (node === null) throw new Error(key + " doesn't exist.");
node.value = value;
}
// 返回是否包含該key的元素的判斷值 +
contains(key) {
return this.getNode(this.root, key) !== null;
}
// 返回映射中實際的元素個數 +
getSize() {
return this.size;
}
// 返回映射中是否爲空的判斷值 +
isEmpty() {
return this.size === 0;
}
// @Override toString() 2018-11-05-jwl
toString() {
let mapInfo = `MyBinarySearchTreeMap: size = ${this.size}, data = [ `;
document.body.innerHTML += `MyBinarySearchTreeMap: size = ${ this.size }, data = [ <br/><br/>`;
// 以非遞歸的前序遍歷 輸出字符串
let stack = new MyLinkedListStack();
stack.push(this.root);
if (this.root === null) stack.pop();
while (!stack.isEmpty()) {
let node = stack.pop();
if (node.left !== null) stack.push(node.left);
if (node.right !== null) stack.push(node.right);
if (node.left === null && node.right === null) {
mapInfo += ` ${node.toString()} \r\n`;
document.body.innerHTML += ` ${node.toString()} <br/><br/>`;
} else {
mapInfo += ` ${node.toString()}, \r\n`;
document.body.innerHTML += ` ${node.toString()}, <br/><br/>`;
}
}
mapInfo += ` ] \r\n`;
document.body.innerHTML += ` ] <br/><br/>`;
return mapInfo;
}
}
複製代碼
PerformanceTest
// 性能測試
class PerformanceTest {
constructor() {}
// 對比都列
testQueue(queue, openCount) {
let startTime = Date.now();
let random = Math.random;
for (var i = 0; i < openCount; i++) {
queue.enqueue(random() * openCount);
}
while (!queue.isEmpty()) {
queue.dequeue();
}
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 對比棧
testStack(stack, openCount) {
let startTime = Date.now();
let random = Math.random;
for (var i = 0; i < openCount; i++) {
stack.push(random() * openCount);
}
while (!stack.isEmpty()) {
stack.pop();
}
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 對比集合
testSet(set, openCount) {
let startTime = Date.now();
let random = Math.random;
let arr = [];
let temp = null;
// 第一遍測試
for (var i = 0; i < openCount; i++) {
temp = random();
// 添加劇復元素,從而測試集合去重的能力
set.add(temp * openCount);
set.add(temp * openCount);
arr.push(temp * openCount);
}
for (var i = 0; i < openCount; i++) {
set.remove(arr[i]);
}
// 第二遍測試
for (var i = 0; i < openCount; i++) {
set.add(arr[i]);
set.add(arr[i]);
}
while (!set.isEmpty()) {
set.remove(arr[set.getSize() - 1]);
}
let endTime = Date.now();
// 求出兩次測試的平均時間
let avgTime = Math.ceil((endTime - startTime) / 2);
return this.calcTime(avgTime);
}
// 對比映射
testMap(map, openCount) {
let startTime = Date.now();
let array = new MyArray();
let random = Math.random;
let temp = null;
let result = null;
for (var i = 0; i < openCount; i++) {
temp = random();
result = openCount * temp;
array.add(result);
array.add(result);
array.add(result);
array.add(result);
}
for (var i = 0; i < array.getSize(); i++) {
result = array.get(i);
if (map.contains(result)) map.add(result, map.get(result) + 1);
else map.add(result, 1);
}
for (var i = 0; i < array.getSize(); i++) {
result = array.get(i);
map.remove(result);
}
let endTime = Date.now();
return this.calcTime(endTime - startTime);
}
// 計算運行的時間,轉換爲 天-小時-分鐘-秒-毫秒
calcTime(result) {
//獲取距離的天數
var day = Math.floor(result / (24 * 60 * 60 * 1000));
//獲取距離的小時數
var hours = Math.floor((result / (60 * 60 * 1000)) % 24);
//獲取距離的分鐘數
var minutes = Math.floor((result / (60 * 1000)) % 60);
//獲取距離的秒數
var seconds = Math.floor((result / 1000) % 60);
//獲取距離的毫秒數
var milliSeconds = Math.floor(result % 1000);
// 計算時間
day = day < 10 ? '0' + day : day;
hours = hours < 10 ? '0' + hours : hours;
minutes = minutes < 10 ? '0' + minutes : minutes;
seconds = seconds < 10 ? '0' + seconds : seconds;
milliSeconds =
milliSeconds < 100
? milliSeconds < 10
? '00' + milliSeconds
: '0' + milliSeconds
: milliSeconds;
// 輸出耗時字符串
result =
day +
'天' +
hours +
'小時' +
minutes +
'分' +
seconds +
'秒' +
milliSeconds +
'毫秒' +
' <<<<============>>>> 總毫秒數:' +
result;
return result;
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('Map Comparison Area');
let myLinkedListMap = new MyLinkedListMap();
let myBinarySearchTreeMap = new MyBinarySearchTreeMap();
let systemMap = new Map();
let performanceTest = new PerformanceTest();
systemMap.remove = systemMap.delete;
systemMap.contains = systemMap.has;
systemMap.add = systemMap.set;
systemMap.isEmpty = () => systemMap.size === 0;
systemMap.getSize = () => systemMap.size;
let myLinkedListMapInfo = performanceTest.testMap(
myLinkedListMap,
50000
);
let myBinarySearchTreeMapInfo = performanceTest.testMap(
myBinarySearchTreeMap,
50000
);
let systemMapInfo = performanceTest.testMap(systemMap, 50000);
this.alterLine('MyLinkedListMap Area');
console.log(myLinkedListMapInfo);
this.show(myLinkedListMapInfo);
this.alterLine('MyBinarySearchTreeMap Area');
console.log(myBinarySearchTreeMapInfo);
this.show(myBinarySearchTreeMapInfo);
this.alterLine('SystemMap Area');
console.log(systemMapInfo);
this.show(systemMapInfo);
}
// 將內容顯示在頁面上
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();
};
複製代碼
MySet
void add (e)
: 不能添加劇復元素void remove (e)
boolean conatains (e)
int getSize ()
boolean isEmpty ()
MyMap
void add(k, v)
V remove(k)
boolean contains(k)
V get(k)
void set(k, v)
int getSize()
boolean isEmpty()
349.兩個數組的交集
https://leetcode-cn.com/problems/intersection-of-two-arrays/
350.兩個數組的交集 II
https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/
https://leetcode-cn.com/tag/hash-table/
(class: Solution, class: Solution)
兩道題目
349.兩個數組的交集
350.兩個數組的交集 II
// 答題
class Solution {
// leetcode 349. 兩個數組的交集
intersection(nums1, nums2) {
/** * @param {number[]} nums1 * @param {number[]} nums2 * @return {number[]} */
var intersection = function(nums1, nums2) {
let set = new Set();
let arr = [];
for (const num of nums1) set.add(num);
for (const num of nums2) {
if (set.has(num)) {
arr.push(num);
set.delete(num);
}
}
return arr;
};
return intersection(nums1, nums2);
}
// leetcode 350.兩個數組的交集 II
intersect(nums1, nums2) {
/** * @param {number[]} nums1 * @param {number[]} nums2 * @return {number[]} */
var intersect = function(nums1, nums2) {
let map = new Map();
let arr = [];
for (const num of nums1) {
if (map.has(num)) map.set(num, map.get(num) + 1);
else map.set(num, 1);
}
for (const num of nums2) {
if (map.has(num)) {
arr.push(num);
let result = map.get(num) - 1;
map.set(num, result);
if (result === 0) map.delete(num);
}
}
return arr;
};
return intersect(nums1, nums2);
}
}
// main 函數
class Main {
constructor() {
this.alterLine('leetcode 349. 兩個數組的交集');
let s = new Solution();
var nums1 = [1, 2, 2, 1],
nums2 = [2, 2];
var nums3 = [4, 9, 5],
nums4 = [9, 4, 9, 8, 4];
console.log('[' + s.intersection(nums1, nums2) + ']');
console.log('[' + s.intersection(nums3, nums4) + ']');
this.show('[' + s.intersection(nums1, nums2) + ']');
this.show('[' + s.intersection(nums3, nums4) + ']');
this.alterLine('leetcode 350. 兩個數組的交集 II');
console.log('[' + s.intersect(nums1, nums2) + ']');
console.log('[' + s.intersect(nums3, nums4) + ']');
this.show('[' + s.intersect(nums1, nums2) + ']');
this.show('[' + s.intersect(nums3, nums4) + ']');
}
// 將內容顯示在頁面上
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();
};
複製代碼