实现二叉树

实现二叉树_第1张图片

遍历方式:
	先序:根左右,对于每一个子树来说,都先打印头节点,然后打印左子树,然后打印右子树
	中序:左根右
	后续:左右根

二叉查找:
	左节点较小值,右节点较大值

删除:
	无子节点直接删除
	有一个子节点,子节点替换删除的节点
	有两个子节点,找到左树最大的值或右树最小的值替换删除的节点

先序遍历非递归版本

   1
  2 3
4 5 6 7
  • 准备一个栈,从第一个节点开始,打印后弹出,然后将节点的右节点入栈、左节点入栈
  • 打印后弹出,继续将节点的右节点入栈、左节点入栈
    • 1入栈,打印弹出,3、2入栈,打印2后弹出2,5、4入栈,打印4、5后出栈,然后打印3,7、6入栈,打印6、7
    • 最终打印1,2,4,5,3,6,7
function preOrder(head) {
  let stack = [];
  stack.push(head);
  while (stack.length) {
    let node = stack.pop();
    console.log(node);

    if (node?.right) {
      stack.push(node.right);
    }
    if (node?.left) {
      stack.push(node.left);
    }
  }
}

后序遍历非递归版本

  • 在先序基础上,节点的入栈顺序修改为左节点入栈、右节点入栈
  • 然后每次将弹出的内容再放进一个辅助栈中
  • 最终辅助栈内容出栈即为后序遍历
    • 原因:根左右顺序进入,弹出为根右左,出栈即为左右根
  • 1入栈,然后出栈放入辅助栈,2、3入栈,3出栈进入辅助栈,6、7入栈,7、6出栈进入辅助栈
  • 2出栈入辅助栈,4、5入栈,5、4出栈进入辅助栈
  • 最终辅助栈的内容为1,3,7,6,2,5,4,出栈为:4,5,2,6,7,3,1

中序遍历非递归版本

   1
  2 3
4 5 6 7
  • 先将树的左子树全部入栈,每弹出一个打印,如果有右子树,则右子树按照开头规则右子树的左子树的头节点全部入栈(包括右子树只有一个节点的情况)
  • 1、2、4入栈,弹出4打印,弹出2打印,2的右子树的左子树的头节点入栈,5入栈,弹出5打印,弹出1打印,将1的右子树的左子树的头节点入栈,3、6入栈,弹出6打印,弹出3打印,3的右子树的左子树入栈,7入栈,弹出7打印
  • 最终打印:4,2,5,1,6,3,7
function midOrder(head) {
  let stack = [];
  while (head != null || stack.length) {
    if (head != null) {
      stack.push(head);
      head = head.left;
    } else {
      head = stack.pop();
      console.log(head);
      head = head.right;
    }
  }
}

二叉树深度优先遍历

  • 先序遍历即可

二叉树宽度优先遍历

  • 通过对列,头进尾出
function BFS(head) {
  let arr = [];

  arr.push(head);

  for (let i = 0; i < head.length; i++) {
    if (arr[i].left) {
      arr.push(arr[i].left);
    }
    if (arr[i].right) {
      arr.push(arr[i].right);
    }
  }
}

判断一棵树是否是搜索二叉树

  • 每颗子树都是左边的比右边小即为搜索二叉树
  • 如何判断:因为左树比右树小,则采用中序遍历,最后得出的内容一定是升序,在中序遍历过程中,如果发现有降序的,则一定不是搜索二叉树

实现二叉树_第2张图片
判断一棵树是否是完全二叉树

  • 只有最后一层可以不是满的,排列顺序从左往右没有空节点
  • 如何判断:
    • 通过BFS宽度遍历,如果一个节点有右节点,但是没有左节点,则不是完全二叉树
    • 满足上面情况,如果有一个节点只有左节点但是没有右节点,则之后的遍历所有都只能是叶子节点(无子节点),比如下面的倒数第二排倒数第二个节点
      实现二叉树_第3张图片

构建二叉树以及基本操作代码示例:

function Node(data, left, right) { //单个节点
  this.data = data;
  this.left = left;
  this.right = right;

  this.show = show;
}

function show() {
  console.log(this.data);
  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) {
  let n = new Node(data, null, null);
  //判断插入时,是否空树
  if (this.root == null) {
    this.root = n;
  } else {
    let current = this.root;
    let parent;
    while (true) {
      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.data);
    inOrder(node.right);
  }
  
}

function getMin(root) {
  let current = root;
  while (current.left !== null) {
    current = current.left;
  }

  console.log('最小值:'+current.data);
  return current;

}

function getMax(root) {
  let current = root;
  while (current.right !== null) {
    current = current.right;
  }

  console.log('最大值:'+current.data);
  return current;
}

function find(data) {
  let current = this.root;
  while (true&&current!=null) {
    if (data < current.data) {
      current=current.left
    } else if (data > current.data) {
      current=current.right
    } else {
      return current;
    }
  }
  return false;
}

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

function removeNode(node, data) {
  if (node == null) {
    return false;
  }

  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;
    }
    
    let tempNode = getMin(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;
  }
}


let 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('遍历节点');
nums.inOrder(nums.root);

getMin(nums.root);
getMax(nums.root);

nums.remove(16)
nums.remove(37)

nums.inOrder(nums.root);

你可能感兴趣的:(前端算法,数据结构)