二叉搜索树

220 阅读3分钟

树是一种分层的抽象数据结构,一个树结构包含了许多有父子关系的节点,位于顶层的叫做根节点(root),是顶级的父节点。子节点分为内部节点和外部节点。至少有一个子节点的是内部节点,没有子节点的是外部节点。形象来说树的第一层至倒数第一层都是内部节点,最后一层则是外部节点。

二叉搜索树是一种只允许左侧存放比父节点小的数,右侧存放比父节点大的数。如:

创建BinarySearchTree类:

function BinarySearchTree() {        
 var node = function (key) {              
    this.key = key;              
    this.left = null;             
    this.right = null;        
   }        
 var root = null;
}

插入一个键----insert方法:

this.insert = function (key) {            
        var newNode = new node(key);           
        if (root === null) {                
        root = newNode;            
  } else {               
    insertNode(root, newNode);             
  }        
}
var insertNode = function (node, newNode) {            
    if (newNode.key < node.key) {                
        if (node.left === null) {                    
            node.left = newNode;                
        } else {                    
            insertNode(node.left, newNode)                    
            }                
        } else {                    
    if (newNode.key > node.key) {                        
        if(node.right === null) {                            
            node.right = newNode;                        
        } else {                            
            insertNode(node.right, newNode)                        
        }                    
     }                    
    }            
}

遍历:

遍历二叉树的方法有三种

1.先序遍历------preOrderTraverse:

 this.preOrderTraverse = function (callback) {            
        preOrderTraverseNode(root , callback)        
    }        
var preOrderTraverseNode = function (node , callback) {            
        if(node !== null) {                
            callback(node.key);                
            preOrderTraverseNode(node.left , callback);                
            preOrderTraverseNode(node.right , callback);            
        }        
    }

先序遍历先访问父节点,在访问左节点,最后访问右节点

2.中序遍历-----inOrderTraverse:

this.inOrderTraverse = function (callback) {            
        inOrderTraverseNode(root, callback);//这里是访问者模式        
    }        
var inOrderTraverseNode = function (node, callback) {            
        if (node !== null) {                                 
            inOrderTraverseNode(node.left, callback);                
            callback(node.key);                 
            inOrderTraverseNode(node.right, callback);                           
        }            
    }

中序遍历先访问左侧子节点,然后是父节点,最后是右侧子节点

3.后序遍历-----postOrderTraverse:

this.postOrderTraverse = function (callback) {            
            postOrderTraverseNode(root , callback);        
        }        
var postOrderTraverseNode = function (node , callback) {            
        if (node !== null) {                
            postOrderTraverseNode(node.left , callback);                
            postOrderTraverseNode(node.right , callback);                
            callback(node.key);            
        }       
    }

后序遍历先访问左侧子节点,然后是右侧子节点,最后是父节点

搜索最小值-----min:

this.min = function () {            
    return minNode(root);        
}        
var minNode = function (node) {            
    if (node) {                
        while (node && node.left !== null) {                    
            node = node.left;                
            }                
        return node.key;            
        }            
    return null;        
}

由于二叉搜索树的结构,最小值一定在树的最后一层的最左端。

搜索最大值-----max:

this.max = function () {            
    return maxNode(root);        
}        
var maxNode = function (node) {            
    if (node) {                
        while (node && node.right !== null) {                    
            node = node.right;                }                
            return node.key;            
        }            
    return null;        
}

由于二叉搜索树的结构,最大值一定在树的最后一层的最右端

搜索一个特定的值-----search:

this.search = function (key) {            
    return searchNode(root , key)        
}        
var searchNode = function (node , key) {            
    if (node == null) {                
        return false;            
    } else if (node.key < key) {                
        return searchNode(node.right , key);            
    } else if (node.key > key) {                
        return searchNode(node.left , key);            
    } else {                
        return true;            
    }        
}

移除一个节点----- remove:

this.remove = function (key) {            
    root = removeNode(root , key);        
}        
var removeNode = function (node , key) {            
    if (node == null) {                
        return null;            
    }            
    if (node.key > key) {                
        removeNode(node.left , key);            
    } else if (node.key < key) {                
        removeNode(node.key , key);            
    } else {                
        //没有子节点                
        if (node.left == null && node.right == null) {                    
            node = null;                    
            return node;    //返回节点以改变指针指向                
        } else if (node.left !== null ) {//一个左子节点                    
            node = node.left;                    
            return node;    //返回节点以改变指针指向                                
        } else if (node.right !== null) {//一个右子节点                    
            node = node.right;                    
            return node;  //返回节点以改变指针指向                                    
        } else {//两个子节点                    
            var replaceNode = findMinNode(node.right);//找到右子节点中最小的节点
            node.key = replaceNode.key;                    
            node.right = removeNode(node.right , replaceNode);                    
            return node;    //返回节点以改变指针指向                
        }            
    }        
}
//选取右子节点中最小的节点        
var findMinNode = function (node) {            
    while (node && node.left !== null) {                
        node = node.left;            
    }             
    return node;        
} 

移除一个节点时除了要把你所移除的节点变为null,还要改变指针的指向,如果没有子节点,则指针指向null,如果只有一侧的分支(只有左节点或者只有右节点),那么指针就要指向下一个子节点,如果两侧都有分支(左右节点都有)那么就要先找到当前节点的右侧分支中最小的节点,就是右侧分支最后一层的最左端的节点,将这个最小的节点代替我们当前要删除的节点,这样能保证二叉搜索树的左侧小右侧大的基本规则,最后将指针指向这个代替的节点。