javascript數據結構與算法-- 二叉樹javascript
樹是計算機科學中常常用到的一種數據結構。樹是一種非線性的數據結構,以分紅的方式存儲數據,樹被用來存儲具備層級關係的數據,好比文件系統的文件,樹還被用來存儲有序列表。咱們要研究的是二叉樹,在二叉樹上查找元素很是快,爲二叉樹添加元素或者刪除元素,也是很是快的。java
樹的基本結構示意圖以下:node
咱們如今最主要的是要來學習二叉樹,二叉樹是一種特殊的樹,它的特徵是 子節點個數不超過2個。以下圖就是二叉樹的基本結構示意圖以下:算法
二叉樹是一種特殊的樹,相對較少的值保存在左節點上,較大的值保存在右節點中。這一特性使得查找的效率很是高,對於數值型和非數值型的數據,好比單詞和字符串都是同樣。數據結構
下面咱們來學習插入節點的操做吧!post
1. 二叉樹是由節點組成的,因此咱們須要定義一個對象node,能夠保存數據,也能夠保存其餘節點的連接(left 和 right),show()方法用來顯示保存在節點中的數據。Node代碼以下:學習
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; }
插入節點分析以下:測試
代碼以下:this
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.insert = insert; this.inOrder = inOrder; } function insert(data) { var n = new Node(data,null,null); if(this.root == null) { this.root = n; }else { var current = this.root; var parent; while(current) { parent = current; if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }else { current = current.right; if(current == null) { parent.right = n; break; } } } } } 初始代碼以下: var nums = new BST(); nums.insert(23); nums.insert(45); nums.insert(16); nums.insert(37); nums.insert(3); nums.insert(99); nums.insert(22);
示意圖以下:spa
1. 執行insert(23)時候,因爲根節點== null 因此 根節點爲23.
2. 執行insert(45)的時候,根節點不等於null,所以進入while語句;因爲45 > 大於根節點23 因此就進入else語句,當前current的值以下圖:
當執行 current = current.right; 這句代碼時候,當前current值變爲null了,而後進行if判斷代碼以下:
if(current == null) { parent.right = n; break; }
因此45爲根節點的右節點了。跳出循環語句;
3. 執行insert(16)的時候,根節點不等於null,所以進入while語句,因爲16 < 小於根節點23,因此就進入if語句,那麼當前的current值以下:
當執行到 current = current.left; 的時候,current的值就變爲null,因此接着往下執行代碼:
if(current == null) { parent.left = n; break; }
就把當前的節點16插入到根節點的左節點上。
4. 接着執行 insert(37) 的時候,根節點不等於null,所以進入else語句中的while語句,因爲37 大於根節點23,因此就進入while語句中的else語句,當前的current值爲:
當執行current = current.right;這句代碼的時候,那麼當前current = 45的那個節點(如上圖所示);當再執行下面的代碼:
if(current == null) { parent.right = n; break; }
那麼current != null 因此接着進入下一次while循環,執行這句代碼後;parent = current;
那麼parent = 45的那個節點了,current值以下所示:
接着進入if語句判斷,因爲當前的根節點是45,因此37 小於根節點 45了,因此就進入if語句代碼以下:
if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }
Current = current.left 所以current = null; 繼續執行上面的if語句判斷是否爲null的時候,所以就把37放入根節點爲45的左節點上了。
5. 直接執行insert(3); 的時候,根節點不爲空,因此就進入else語句的while語句中,因爲當前的data = 3,因此執行以下if判斷代碼:
if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }
插入的節點值3 小於 根節點23,進入if語句裏面執行,可是當前的current值以下:
因此當執行 current = current.left 的時候,那麼current = 16的那個節點了,以下所示:
所以current 不等於null,因此就執行到下一次while循環,繼續進入while中的if判斷,因爲當前的根節點是16,因此也就進入了if裏面的代碼去執行,在執行這句代碼後:
current = current.left; 由上圖可知:current = null;current就等於null了;再執行代碼以下:
if(current == null) { parent.left = n; break; }
就把節點3 插入到當前的根節點爲16的左節點了。
6. 執行insert(99)的時候;當前的根節點23 小於 99,那麼就進入else語句了,那麼current值就等於以下:
當執行 current = current.right; 的時候 ,那麼current 就等於以下:
再接着執行代碼:
if(current == null) { parent.right = n; break; }
如上圖所示,current並不等於null,因此執行下一次while循環,繼續進入while中的else語句,那麼當前的current值以下:
當執行current = current.right;這句代碼的時候,那麼current 就等於 null了,因此執行if語句代碼以下:
if(current == null) { parent.right = n; break; }
就把99節點插入到當前的根節點爲45節點的右節點了。
7. 執行 insert(22);的時候,因爲根節點爲23,因此節點22 小於 23,因此進入while中的if語句裏面了,那麼當前current值以下:
當執行 current = current.left; 的時候,那麼current值變爲以下所示:
因此執行 if語句代碼以下:
if(current == null) { parent.left = n; break; }
不等於null,因此斤進入while下一次循環,因爲當前的根節點16 小於插入的節點22 ,因此就進入else語句了,那麼當前的current值以下:
再執行這句代碼 current = current.right; 那麼current就等於null了;所以就把節點22插入到根節點爲16上面的右節點上了;
以上是插入節點的整個流程!
二:遍歷二叉查找樹;
遍歷二叉樹的方法有三種,中序,先序和後序。
1. 中序;
以下圖所示:
中序遍歷使用遞歸的方式實現,該方法須要以升序訪問樹中的全部節點,先訪問左子樹,再訪問根節點,最後訪問右子樹。
代碼以下:
// 中序遍歷 function inOrder(node) { if(!(node == null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } }
代碼分析以下:
JS全部代碼以下:
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.insert = insert; this.inOrder = inOrder; } function insert(data) { var n = new Node(data,null,null); if(this.root == null) { this.root = n; }else { var current = this.root; var parent; while(current) { parent = current; if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }else { current = current.right; if(current == null) { parent.right = n; break; } } } } } // 中序遍歷 function inOrder(node) { if(!(node == null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } } 代碼初始化以下: var nums = new BST(); nums.insert(23); nums.insert(45); nums.insert(16); nums.insert(37); nums.insert(3); nums.insert(99); nums.insert(22); inOrder(nums.root);
2. 先序:先序遍歷先訪問根節點,而後以一樣方式訪問左子樹和右子樹。以下圖所示:
代碼以下:
// 先序遍歷 function preOrder(node) { if(!(node == null)) { console.log(node.show()); preOrder(node.left); preOrder(node.right); } }
JS全部代碼以下:
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.insert = insert; this.inOrder = inOrder; } function insert(data) { var n = new Node(data,null,null); if(this.root == null) { this.root = n; }else { var current = this.root; var parent; while(current) { parent = current; if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }else { current = current.right; if(current == null) { parent.right = n; break; } } } } } // 中序遍歷 function inOrder(node) { if(!(node == null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } } // 先序遍歷 function preOrder(node) { if(!(node == null)) { console.log(node.show()); preOrder(node.left); preOrder(node.right); } } 初始化代碼以下: var nums = new BST(); nums.insert(23); nums.insert(45); nums.insert(16); nums.insert(37); nums.insert(3); nums.insert(99); nums.insert(22); console.log("--------------"); preOrder(nums.root);
先序遍歷打印以下:
3. 後序:後序遍歷先訪問葉子節點,從左子樹到右子樹,再到根節點,以下所示:
JS代碼以下:
// 後序遍歷 function postOrder(node) { if(!(node == null)) { postOrder(node.left); postOrder(node.right); console.log("後序遍歷"+node.show()); } }
全部的JS代碼以下:
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.insert = insert; this.inOrder = inOrder; } function insert(data) { var n = new Node(data,null,null); if(this.root == null) { this.root = n; }else { var current = this.root; var parent; while(current) { parent = current; if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }else { current = current.right; if(current == null) { parent.right = n; break; } } } } } // 中序遍歷 function inOrder(node) { if(!(node == null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } } // 先序遍歷 function preOrder(node) { if(!(node == null)) { console.log(node.show()); preOrder(node.left); preOrder(node.right); } } // 後序遍歷 function postOrder(node) { if(!(node == null)) { postOrder(node.left); postOrder(node.right); console.log("後序遍歷"+node.show()); } } 頁面初始化以下: var nums = new BST(); nums.insert(23); nums.insert(45); nums.insert(16); nums.insert(37); nums.insert(3); nums.insert(99); nums.insert(22); console.log("--------------"); postOrder(nums.root);
打印以下:
在二叉查找樹上進行查找
查找二叉樹上的最小值與最大值很是簡單,由於較小的值老是在左子節點上,在二叉樹上查找最小值,只須要遍歷左子樹,直到找到最後一個節點。
1. 二叉樹查找最小值
代碼以下:
// 二叉樹查找最小值 function getMin(){ var current = this.root; while(!(current.left == null)) { current = current.left; } return current.data; }
全部JS代碼以下:
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.insert = insert; this.inOrder = inOrder; this.getMin = getMin; } function insert(data) { var n = new Node(data,null,null); if(this.root == null) { this.root = n; }else { var current = this.root; var parent; while(current) { parent = current; if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }else { current = current.right; if(current == null) { parent.right = n; break; } } } } } // 中序遍歷 function inOrder(node) { if(!(node == null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } } // 先序遍歷 function preOrder(node) { if(!(node == null)) { console.log(node.show()); preOrder(node.left); preOrder(node.right); } } // 後序遍歷 function postOrder(node) { if(!(node == null)) { postOrder(node.left); postOrder(node.right); console.log("後序遍歷"+node.show()); } } // 二叉樹查找最小值 function getMin(){ var current = this.root; while(!(current.left == null)) { current = current.left; } return current.data; } 測試代碼初始化以下: var nums = new BST(); nums.insert(23); nums.insert(45); nums.insert(16); nums.insert(37); nums.insert(3); nums.insert(99); nums.insert(22); var min = nums.getMin(); console.log(min); // 打印出3
代碼分析以下:
1 當執行到getMin()方法內的 var current = this.root的時候,當前的this.root的值爲以下:
所以進入while內的循環,執行到代碼:
current = current.left,current.left值以下:
賦值給current,所以current等於上面的節點。接着繼續循環遍歷while,執行到代碼 current = current.left , current.left值變成以下:
而後值賦值給current。再繼續遍歷,進入while循環,while(!(current.left == null)) {}代碼判斷,由上圖可知;current.left = null,所以就跳出整個while循環,所以打印3出來。
2.在二叉樹上查找最大值;只需遍歷右子樹,直到查到最後一個節點,該節點上保存的值即爲最大值。
JS代碼以下:
// 二叉樹上查找最大值 function getMax() { var current = this.root; while(!(current.right == null)) { current = current.right; } return current.data; }
下面是全部的JS代碼:
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.insert = insert; this.inOrder = inOrder; this.getMin = getMin; this.getMax = getMax; } function insert(data) { var n = new Node(data,null,null); if(this.root == null) { this.root = n; }else { var current = this.root; var parent; while(current) { parent = current; if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }else { current = current.right; if(current == null) { parent.right = n; break; } } } } } // 中序遍歷 function inOrder(node) { if(!(node == null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } } // 先序遍歷 function preOrder(node) { if(!(node == null)) { console.log(node.show()); preOrder(node.left); preOrder(node.right); } } // 後序遍歷 function postOrder(node) { if(!(node == null)) { postOrder(node.left); postOrder(node.right); console.log("後序遍歷"+node.show()); } } // 二叉樹查找最小值 function getMin(){ var current = this.root; while(!(current.left == null)) { current = current.left; } return current.data; } // 二叉樹上查找最大值 function getMax() { var current = this.root; while(!(current.right == null)) { current = current.right; } return current.data; } HTML初始化以下: var nums = new BST(); nums.insert(23); nums.insert(45); nums.insert(16); nums.insert(37); nums.insert(3); nums.insert(99); nums.insert(22); var min = nums.getMin(); console.log(min); var max = nums.getMax(); console.log(max);
分析仍是和上面最小值分析一個道理,這裏就不分析了。
上面2個方法返回最小值和最大值,可是有時候,咱們但願方法返回存儲最小值和最大值的節點,這很好實現,只須要修改方法,讓它返回當前節點,而不是節點中存儲的數據便可。
在二叉樹上查找給定值
在二叉樹上查找給定值,須要比較該值和當前節點上的值得大小。經過比較,就能肯定若是給定值不在當前節點時,就要向左遍歷和向右遍歷了。
代碼以下:
// 查找給定值 function find(data) { var current = this.root; while(current != null) { if(current.data == data) { return current; }else if(data < current.data) { current = current.left; }else { current = current.right; } } return null; }
代碼分析以下:
好比如今的二叉樹是以下這個樣子:
頁面初始化 查找二叉樹上的45 節點,代碼初始化以下:
var value = nums.find("45");
截圖以下:
而後就return 45的節點上了。
全部的JS代碼以下:
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.insert = insert; this.inOrder = inOrder; this.getMin = getMin; this.getMax = getMax; this.find = find; } function insert(data) { var n = new Node(data,null,null); if(this.root == null) { this.root = n; }else { var current = this.root; var parent; while(current) { parent = current; if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }else { current = current.right; if(current == null) { parent.right = n; break; } } } } } // 中序遍歷 function inOrder(node) { if(!(node == null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } } // 先序遍歷 function preOrder(node) { if(!(node == null)) { console.log(node.show()); preOrder(node.left); preOrder(node.right); } } // 後序遍歷 function postOrder(node) { if(!(node == null)) { postOrder(node.left); postOrder(node.right); console.log("後序遍歷"+node.show()); } } // 二叉樹查找最小值 function getMin(){ var current = this.root; while(!(current.left == null)) { current = current.left; } return current.data; } // 二叉樹上查找最大值 function getMax() { var current = this.root; while(!(current.right == null)) { current = current.right; } return current.data; } // 查找給定值 function find(data) { var current = this.root; while(current != null) { if(current.data == data) { return current; }else if(data < current.data) { current = current.left; }else { current = current.right; } } return null; }
從二叉查找樹上刪除節點。
原理:從二叉樹上刪除節點首先要判斷當前節點是否包含待刪除的數據,若是包含,則刪除該節點;若是不包含,則要比較當前節點上的數據和待刪除的數據。若是待刪除數據小於當前節點上的數據,則要移到當前節點的左子節點繼續比較;若是刪除的數據大於當前節點上的數據,則移至當前節點的右子節點繼續比較。
若是待刪除節點是葉子節點(沒有子節點的節點),那麼只須要將父節點指向它的連接指向null;
若是待刪除的節點只包含一個子節點,那麼本來指向它的節點就得作點調整,使其指向它的子節點。
最後,若是待刪除節點包含2個子節點,正確的作法有2種,1:要麼查找待刪除節點左子樹上的最大值,要麼查找其右子樹上的最小值。這裏咱們選擇後一種;
下面是咱們刪除節點的JS代碼以下:
function remove(data) { root = removeNode(this.root,data); } function getSmallest(node) { if (node.left == null) { return node; } else { return getSmallest(node.left); } } function removeNode(node,data) { if(node == null) { return null; } if(data == node.data) { // 沒有子節點的節點 if(node.left == null && node.right == null) { return null; } // 沒有左子節點的節點 if(node.left == null) { return node.right; } // 沒有右子節點的節點 if(node.right == null) { return node.left; } // 有2個子節點的節點 var tempNode = getSmallest(node.right); node.data = tempNode.data; node.right = removeNode(node.right,tempNode.data); return node; }else if(data < node.data) { node.left = removeNode(node.left,data); return node; }else { node.right = removeNode(node.right,data); return node; } }
咱們仍是以上面的二叉樹來分析下代碼原理:
1. 好比我如今要刪除根節點爲23的節點,代碼初始化以下:
nums.remove(23);
執行這個代碼後 var tempNode = getSmallest(node.right); 就指向45的那個節點了,以下:
而後執行下面的獲取右子樹上的最小值的方法;
function getSmallest(node) { if (node.left == null) { return node; } else { return getSmallest(node.left); } }
裏面使用遞歸的方式執行代碼,當node.left == null 時候,就返回當前node節點;以下:
如上所示,當node等於37的時候 就返回node爲37的節點。
下面繼續執行第二句代碼;以下:
node.data = tempNode.data; 那麼node.data = 37了;下面是node節點的截圖以下:
接着繼續執行下面的代碼
node.right = removeNode(node.right,tempNode.data);
同時又使用遞歸的方式removeNode()方法;返回以下節點:
全部的JS代碼以下:
function Node(data,left,right) { this.data = data; this.left = left; this.right = right; this.show = show; } function show() { return this.data; } function BST() { this.root = null; this.insert = insert; this.inOrder = inOrder; this.getMin = getMin; this.getMax = getMax; this.find = find; this.remove = remove; } function insert(data) { var n = new Node(data,null,null); if(this.root == null) { this.root = n; }else { var current = this.root; var parent; while(current) { parent = current; if(data < current.data) { current = current.left; if(current == null) { parent.left = n; break; } }else { current = current.right; if(current == null) { parent.right = n; break; } } } } } // 中序遍歷 function inOrder(node) { if(!(node == null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } } // 先序遍歷 function preOrder(node) { if(!(node == null)) { console.log(node.show()); preOrder(node.left); preOrder(node.right); } } // 後序遍歷 function postOrder(node) { if(!(node == null)) { postOrder(node.left); postOrder(node.right); console.log("後序遍歷"+node.show()); } } // 二叉樹查找最小值 function getMin(){ var current = this.root; while(!(current.left == null)) { current = current.left; } return current.data; } // 二叉樹上查找最大值 function getMax() { var current = this.root; while(!(current.right == null)) { current = current.right; } return current.data; } // 查找給定值 function find(data) { var current = this.root; while(current != null) { if(current.data == data) { return current; }else if(data < current.data) { current = current.left; }else { current = current.right; } } return null; } function remove(data) { root = removeNode(this.root,data); } function getSmallest(node) { if (node.left == null) { return node; } else { return getSmallest(node.left); } } function removeNode(node,data) { if(node == null) { return null; } if(data == node.data) { // 沒有子節點的節點 if(node.left == null && node.right == null) { return null; } // 沒有左子節點的節點 if(node.left == null) { return node.right; } // 沒有右子節點的節點 if(node.right == null) { return node.left; } // 有2個子節點的節點 var tempNode = getSmallest(node.right); node.data = tempNode.data; node.right = removeNode(node.right,tempNode.data); return node; }else if(data < node.data) { node.left = removeNode(node.left,data); return node; }else { node.right = removeNode(node.right,data); return node; } } 代碼初始化以下: var nums = new BST(); nums.insert(23); nums.insert(45); nums.insert(16); nums.insert(37); nums.insert(3); nums.insert(99); nums.insert(22); var min = nums.getMin(); console.log(min); var max = nums.getMax(); console.log(max); var value = nums.find("45"); console.log(value); nums.remove(23);