1. 程式人生 > >前端你也需要了解的演算法,二叉樹概念及JS實現二叉查詢樹

前端你也需要了解的演算法,二叉樹概念及JS實現二叉查詢樹

前述:樹是電腦科學中經常用到的一種資料結構。樹是一種非線性的資料結構,以分層的形式儲存資料。樹被用來儲存具有層級關係的資料結構,比如檔案系統中的檔案;樹還被用來儲存有序列表。

樹的定義

樹是由一組以邊連線的節點組成。公司的組織結構圖就是一個樹的例子。

二叉樹

二叉樹是一種特殊的樹,它的子節點個數不超過兩個。二叉樹具有一些特殊的計算性質,使得在它們之上的一些操作異常高效。

樹可以分為幾個層次,根節點是第0層,它的子節點是第一層,子節點的子節點是第2層,以此類推。

樹中任何一層的節點可以都看做是子樹的根,該子樹包含根節點的子節點,子節點的子節點等。

我們定義樹的層數就是樹的深度。

每個節點都有一個與之相關的值,該值有時候也會被稱為鍵。

正是因為二叉樹每個節點的子節點不允許超過兩個,才能寫出高效的程式方便在樹中插入、查詢和刪除資料。

二叉樹形態

滿二叉樹

定義:除最後一層無任何子節點外,每一層上的所有結點都有兩個子結點二叉樹。 也就是說,如果一個二叉樹的層數為K,且結點總數是(2^k) -1 ,則它就是滿二叉樹。

完全二叉樹

定義:若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。

二叉排序樹

二叉排序樹(Binary Sort Tree),又稱二叉查詢樹(Binary Search Tree),亦稱二叉搜尋樹。

二叉查詢樹是一種特殊的二叉樹,相對較小的值儲存在左節點中,較大的值儲存在右節點中。

這一特性使得查詢的效率很高,對於數值型和非數值型的資料,比如單詞和字串,都是如此。

二叉排序樹或者是一棵空樹,或者是具有下列性質的二叉樹:

(1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;

(2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;

(3)左、右子樹也分別為二叉排序樹;

(4)沒有鍵值相等的節點。

程式碼實現二叉查詢樹的基本功能

// index.js 實現二叉查詢樹的基本功能
function BST(){
        // 根節點初始化為空
	this.root = null;
	// 插入節點
	this.insert = function(data){
		// 建立一個節點儲存資料
	    var node = new Node(data,null,null);
	    // 下面將節點node插入到樹中
	    // 如果樹是空的,就將節點設為根節點
	    if (this.root === null) {
	    	this.root = node
	    }else{   //樹不為空
	    	// 判斷插在父節點的左邊還是右邊
			// 所以先要儲存一下父節點
			var current = this.root;
	        var parent;
	        // 如果要插入的節點鍵值小於父節點鍵值,則插在父節點左邊,
	        // 前提是父節點的左邊為空,否則要將父節點往下移一層,
	        // 然後再做判斷
	        while(true){
	            // data小於父節點的鍵值
	            parent = current;
	            if(data < parent.data){
	                // 將父節點往左下移(插入左邊)
	                // parent = parent.left;
	                current = current.left;
	                // 如果節點為空,則直接插入
	                if(current === null){
	                    // !!!此處特別注意,如果就這樣把parent賦值為node,也僅僅只是parent指向node,
	                    // 而並沒有加到父元素的左邊!!!根本沒有加到樹中去。所以要先記住父元素,再把當前元素加入進去
	                    parent.left = node;
	                    break;
	                }           
	            }else{  // 將父節點往右下移(插入右邊)
	                current = current.right;
	                if(current === null){
	                    parent.right = node;
	                    break;
	                }
	            }
	        }
	    }
	}

	//中序遍歷 (左中右)
	this.inorder = function(node){
	    if(node){
	        this.inorder(node.left);
	        console.log(node.show());
	        this.inorder(node.right);
	    }
	}

	// 先序遍歷 (中左右)
	this.preorder = function(node){
	    if(node){
	        console.log(node.show());
	        this.preorder(node.left);
	        this.preorder(node.right);
	    }
	}

	// 後序遍歷 (左右中)
	this.postorder = function(node){
	    if(node){
	        this.preorder(node.left);
	        this.preorder(node.right);
	        console.log(node.show());
	    }
	}
}

//以下定義一個節點類
function Node(data,left,right){
    // 節點的鍵值
    this.data = data;
    // 左節點
    this.left = left;
    // 右節點
    this.right = right;
    // 顯示該節點的鍵值
    this.show = function(){
    	return this.data;
    }
}

程式碼實現二叉查詢樹的查詢功能(最大值、最小值、給定值)

// second.js 實現二叉查詢樹的查詢功能(最大值、最小值、給定值)

// 獲取最小值
BST.prototype.getMin = function() {
	var current = this.root;
	while(current.left !== null){
		current = current.left;
	}
	return current.data;
};

// 獲取最大值
BST.prototype.getMax = function() {
	var current = this.root;
	while(current.right !== null){
		current = current.right;
	}
	return current.data;
};

// 獲取指定值
BST.prototype.find = function(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;
};

程式碼實現二叉查詢樹的刪除節點功能

// third.js 實現二叉查詢樹的刪除節點功能

BST.prototype.remove = function(data) {
	this.root = removeNode(this.root, data);
};

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;
		}
		// 有兩個子節點的節點
		var tempNode = getSmallset(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;
	}
};