树结构
树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。
- HTML结构就是典型的树结构
二叉搜索树
- 特点:
- 节点最多只能有两个子节点,一个左子节点,一个右子节点,左右子节点的顺序不能颠倒。
- 在左侧存储比父节点小的值
- 在右侧存储比父节点大的值
- 实现:
- 树(Tree)有:根节点,内部的Node类,查找,删除,插入,遍历
- 树(Tree)有:根节点,内部的Node类,查找,删除,插入,遍历
- 插入节点
function Tree(){
var Node = function(val){
this.value = val
this.left = null
this.right = null
}
var root = null
// 插入节点
/**
* 1、树为空树:root = value
* 2、树不是空的:对比节点
*/
var insertNode = function(node,newNode){
if(newNode.value>node.value){
if(node.right===null){
node.right = newNode
}else{
insertNode(node.right,newNode)
}
}else if(newNode.value<node.value){
if(node.left===null){
node.left = newNode
}else{
insertNode(node.left,newNode)
}
}
}
this.insert = function(value){
var node = new Node(value)
if(root===null){
root = node
}else{
insertNode(root,node)
}
}
}
- 遍历树
- 先序遍历:根节点最先被遍历,从上往下,从左往右遍历
- 中序遍历:根节点在中间时间被遍历,可以从小到大按顺序打印
- 后序遍历:根节点最后被遍历,从下往上,从左往右遍历,从树左边最后一个节点开始遍历,依次先遍历末尾节点再往上
// 遍历节点
// 借助递归
function traverse(node, callback){
if(node===null) return;
traverse(node.left,callback)
traverse(node.right,callback)
callback(node.value)
}
this.traverse = function(callback){
traverse(root,callback)
}
- 获取最小值
- 移除节点
移除节点有很多种情况:
- 移除有2个子节点的?——选哪个子节点来代替被删除的节点
- 移除末位节点(叶节点):node.leftnull, node.rightnull的情况
- 移除只有1个子节点
- 移除节点的方法
- 原理:重新构建树
- 方法:每次以右侧子树的最小节点来替换被删除的节点
/**
* 根节点 root
* 插入 insert
* 查找 search
* 遍历 traverse
* 删除 remove
* 内部的Node类
*
*/
function Tree(){
var Node = function (value){
this.value = value
this.left = null
this.right = null
}
var root = null
// 插入
function insert(node,newNode){
if(node.value<newNode.value){
if(node.right===null){
node.right = newNode
}else{
insert(node.right,newNode)
}
}else{
if(node.left===null){
node.left = newNode
}else{
insert(node.left,newNode)
}
}
}
this.insert = function(value){
var node = new Node(value)
if(!root){
root = node
}else{
insert(root,node)
}
}
// 搜索
var searchNode = function(node,value){
if(node===null) return null
if(value<node.value) return searchNode(node.left,value)
else if(value>node.value) return searchNode(node.right,value)
else return node
}
this.search = function(val){
searchNode(root,value)
}
// 遍历
function traverse (node,callback){
if(!node) return;
// callback(node.value) //前序遍历 —— 访问根节点的操作发生在遍历其左右子树之前。
traverse(node.left,callback)
// callback(node.value) //中序遍历 —— 访问根节点的操作发生在遍历其左右子树之间。
traverse(node.right,callback)
callback(node.value) //后序遍历 —— 访问根节点的操作发生在遍历其左右子树之后
}
this.traverse = function(callback){
traverse(root,callback)
}
// 最小值
this.min = function(node){
if(node===null) return null;
while(node&&node.left!==null){
node = node.left
}
return node
}
// 最大值
this.max = function(node){
if(node===null) return null
while(node&&node.right!==null){
node = node.right
}
return node
}
// 辅助方法,用于查找最小节点
function findMinNode(node){
if(node === null) return null
while(node && node.left!== null){
node = node.left
}
return node
}
// 删除——重新构建树
// 移除节点
function removeNode(node, value) {
if (node === null) return null
// 叶节点
if (value > node.value) {
// 向右查找
node.right = removeNode(node.right, value)
return node
} else if (value < node.value) {
// 向左查找
node.left = removeNode(node.left, value)
return node
} else {
// 情况一:没有子节点
if (node.left == null && node.right == null) {
node = null
return node
}
}
// 情况二:只有一个子节点
if (node.left == null && node.right) {
return node.right
} else if (node.right == null && node.left) {
return node.left
}
// 情况三:有两个子节点
function findMinNode(node) {
if (node == null) return null
while(node && node.left!== null){
node = node.left
}
return node
}
var aux = findMinNode(node) // 查找右侧最小子节点
node.value = aux.value
node.right = removeNode(node.right,aux.value)
return node
}
this.remove = function (key) {
root = removeNode(root,key) //重新构建树
}
this.getTree = function(){
return root
}
}
var t = new Tree
t.insert(8)
t.insert(2)
t.insert(3)
t.insert(9)
// t.remove(2)
// console.log(t.getTree())