1. 程式人生 > 其它 >第k小數

第k小數

二叉樹結構圖:

樹頂部的節點是根節點,有子元素的節點叫做內部節點,沒有子元素的節點叫做外部節點節點

節點和它的子節點稱為子樹

節點左側儲存小值,節點右側儲存大值

使用js實現二叉樹結構,待實現功能如下:

insert(key) 向樹中插入新的鍵 search(key) 在樹中查詢一個鍵,如果節點存在,返回true,如果不存在,返回false inOrderTraverse() 通過中序遍歷方式遍歷所有節點(由小到大排序) preOrderTraverse() 通過先序遍歷方式遍歷所有節點() postOrderTraverse() 通過後序遍歷方式遍歷所有節點 min() 返回樹中最小的值/鍵 max() 返回樹中最大的值/鍵 remove(key) 從樹中移除某個鍵

程式碼(二叉樹資料結構會大量運用遞迴):

class Node{
    // 節點
    constructor(key){
        this.key = key
        this.left = null
        this.right = null
    }
}

const compare = {
    Less_Than : -1,
    Bigger_Than : 1
}

function defaultCompare(a, b){
    // 比較大小
    if(a === b){
        return 0
    }
    return a < b? compare.Less_Than : compare.Bigger_Than
}

建立二叉樹類:

class BinarySearchTree{
    // 二叉樹
    constructor(compareFn = defaultCompare){
        this.compareFn = compareFn  // 用來比較節點值
        this.root = null    // 根節點
    }
}

插入節點:

根據左小右大,通過遞迴逐級下查,查詢待新增節點的位置

insert(key){
    if (this.root === null){
        this.root = new Node(key)
    } else{
        
this._insertNode(this.root, key) } } _insertNode(node, key){ if(this.compareFn(key, node.key) === compare.Less_Than){ if(node.left === null){ node.left = new Node(key) }else{ this._insertNode(node.left, key) } }else{ if(node.right === null){ node.right = new Node(key) }else{ this._insertNode(node.right, key) } } }

查詢鍵是否存在:

和插入節點類似,通過遞迴,沿著一個方向逐級向下查詢,當查到null時,即不存在

search(key){
    return this._searchNode(this.root, key)
}
_searchNode(node, key){
    if(node === null){
        return false
    }
    if(this.compareFn(key, node.key) === compare.Less_Than){
        return this._searchNode(node.left, key)
    } else if(this.compareFn(key, node.key) === compare.Bigger_Than){
        return this._searchNode(node.right, key)
    } else{
        return true
    }
}

中序遍歷所有節點的大小(由小到大排序):

callback引數傳入一個回撥函式,用來遍歷後執行

例如傳入const a = (val) => { console.log(val) }

返回結果是:由小到大依次排列的節點值,例:1,2,3,4,5,6,7,8,9

inOrderTraverse(callback){
    this._inOrderTraverseNode(this.root, callback)
}
_inOrderTraverseNode(node, callback){
    if(node != null){
        this._inOrderTraverseNode(node.left, callback)
        callback(node.key)
        this._inOrderTraverseNode(node.right, callback)
    }
}

先序遍歷:

和中序遍歷類似,只是遍歷結果發生了改變

preOrderTraverse(callback){
    this._preOrderTraverseNode(this.root, callback)
}
_preOrderTraverseNode(node, callback){
    if(node !== null){
        callback(node.key)
        this._preOrderTraverseNode(node.left, callback)
        this._preOrderTraverseNode(node.right, callback)
    }
}

後序遍歷:

和中序遍歷類似,只是遍歷結果發生了改變

postOrderTraverse(callback){
    this._postOrderTraverseNode(this.root, callback)
}
_postOrderTraverseNode(node, callback){
    if(node !== null){
        this._postOrderTraverseNode(node.left, callback)
        this._postOrderTraverseNode(node.right, callback)
        callback(node.key)
    }
}

最小值查詢:

直接查詢最左側子樹的最左側節點

min(){
    return this._minNode(this.root)
}
_minNode(node){
    if(node !== null && node.left !== null){
        return this._minNode(node.left)
    }else{
        return node
    }
}

最大值查詢:

直接查詢最右側子樹的最右側節點

max(){
    return this._maxNode(this.root)
}
_maxNode(node){
    if(node !== null && node.right !== null){
        return this._maxNode(node.right)
    }else{
        return node
    }
}

刪除指定節點:

首先,找到待刪除節點,若找不到,則返回原樹

若找到,分為三種情況:

  1.節點為葉節點,即左右節點都為null,直接刪掉即可

  

  2.節點為內部節點但左右有一側為null,則用另一側節點代替當前節點

  

  3.節點左右側都有,則需要找到右側樹中的最小值來取代當前節點,然後再對右側樹執行一個末位節點刪除,刪除掉重複的原替代值

  

remove(key){
    this.root = this._removeNode(key, this.root)
}
_removeNode(key, node){
    if(node === null){
        return null
    }
    if(this.compareFn(key, node.key) === compare.Less_Than){
        node.left = this._removeNode(key, node.left)
        return node
    }else if(this.compareFn(key, node.key) === compare.Bigger_Than){
        node.right = this._removeNode(key, node.right)
        return node
    }else{
        // 第一種情況,刪除末位節點
        if(node.left === null && node.right === null){
            node = null
            return node
        }
        // 第二種情況,左或右節點為Null
        if(node.left === null){
            node = node.right
            return node
        }else if(node.right === null){
            node = node.left
            return node
        }
        // 第三種情況,左右都有子節點
        // 找到右側子樹中最小的節點
        const newNode = this._minNode(node.right)
        node.key = newNode.key
        // 刪除右側子樹中最小的節點
        this._removeNode(newNode.key, node.right)
        return node
    }
}