二叉排序树的前中后序遍历的非递归算法

二叉排序树的前中后序遍历的非递归算法

一棵树若为二叉排序树,需要满足以下条件:

  • 为空树或者是二叉树
  • 如果左子树不空,则左子树上所有节点的值都小于根节点的值。
  • 如果右子树不空,则右子树上所有节点的值都大于根节点的值。
  • 左子树和右子树也同样是二叉排序树
  • 没有值相等的节点

1. 声明二叉树和节点
/**
 * 二叉排序树
 */
function BST() {
  this.root = null; // 根节点
  this.pos = null; // 位置指针
  this.insert = insert; // 节点插入方法
  this.preOrder = preOrder; // 前序遍历
  this.middleOrder = middleOrder; // 中序遍历
  this.lastOrder = lastOrder; // 后续遍历
}

/**
 * 树节点
 */
function Node(data, left, right) {
  this.data = data; // 节点值
  this.left = left; // 节点左子树
  this.right = right; // 节点右子树
}
复制代码

2. 节点插入方法

基本思路:

  • 如果当前为空树,则将新节点设置为根节点
  • 否则将this.pos指向根节点,将新节点与this.pos比较
  • 如果小于且this.pos左子树为null,则将新节点设置this.pos的左子树
  • 如果左子树不为空,则将this.pos指向左子树,继续上述过程
  • 如果大于this.pos节点的值,则对右子树同理上述过程。
/**
 * 给二叉排序树插入新节点
 */
function insert(data) {
  var _new_ = new Node(data, null, null); // 新建一个节点
  if (this.root === null) {
    this.root = _new_; // 如果当前为空树直接设置为根节点
  } else {
    this.pos = this.root; // 位置指针指向根节点,从根节点开始查找
    while(true) {
      if (_new_.data === this.pos.data) break; // 值不允许相等
      if (_new_.data < this.pos.data) { // 小于插入左子树
        if (this.pos.left === null) {
          this.pos.left = _new_;
          break;
        } else {
          this.pos = this.pos.left;
        }
      } else {
        if (this.pos.right === null) { // 大于插入右子树
          this.pos.right = _new_;
          break;
        } else {
          this.pos = this.pos.right;
        }
      }
    }
  }
  this.pos = null;
  return this.root;
}
复制代码

3. 前序遍历

基本思路:前序遍历就是先根节点,再左子树,再右子树

  • 首先存储当前节点(this.pos)值,如果当前节点有左子树,则将当前节点入栈,将this.pos指向左子树,继续上述过程
  • 如果左子树为空,则取出堆栈顶部节点,this.pos指向该节点,判断当前节点右子树是否为空,如果右子树不为空,则将this.pos指向右子树,继续上述过程
  • 如果左右子树都为空,则判断堆栈中是否还有节点,如果没有,则遍历完成,如果有,取出堆栈顶部节点,判断右子树。
/**
 * 前序遍历
 */
function preOrder() {
  var orderArr = []; // 存储遍历结果
  if (this.root === null) { // 如果当前为空树,则返回空数组
    return orderArr;
  }
  var flag = true; // 是否允许遍历当前节点的左子树
  var _stack_ = []; // 模拟一个堆栈
  this.pos = this.root; // 位置指针指向根节点
  orderArr.push(this.pos.data); // 存储根节点值
  while(true) {
    if (this.pos.left !== null && flag) { // 如果当前节点的左子树不为空且没有被遍历过
      _stack_.push(this.pos); // 当前节点入栈
      this.pos = this.pos.left; // 位置指针指向当前节点左子树
      orderArr.push(this.pos.data); // 存储左子树值
    } else {
      if (this.pos.right !== null) { // 如果当前节点右子树不为空
        this.pos = this.pos.right; // 位置指针指向当前节点右子树
        orderArr.push(this.pos.data); // 存储右子树值
        flag = true; // 允许遍历当前节点左子树
      } else {
        if (_stack_.length < 1) { // 如果堆栈为空,表示已经遍历结束
          break;
        }
        // 没有左节点,没有右节点,表示当前是一个叶子节点
        this.pos = _stack_.pop(); // 取出堆栈顶部节点
        flag = false; // 不允许遍历当前节点的左子树
      }
    }
  }
  return orderArr;
}
复制代码

4. 中序遍历

基本思路:中序遍历就是先左子树,再根节点,再右子树。中序遍历和前序遍历算法差不多,主要区别在于中序是先存左子树的值,再存根节点的值。

/**
 * 中序遍历(非递归)
 */
function middleOrder() {
  var orderArr = [];
  if (this.root === null) {
    return orderArr;
  }

  var _stack_ = []; // 用一个数组作为栈
  var flag = true; // 是否允许遍历左子树
  this.pos = this.root; // 位置指向根节点
  while(true) {
    if (this.pos.left !== null && flag) { // 如果当前节点的左子树不为空且没有被遍历过
      _stack_.push(this.pos); 
      this.pos = this.pos.left;
      flag = true;
    } else { // 左子树为空,判断右子树
      if (this.pos.right !== null) { // 如果右子树不为空 
        orderArr.push(this.pos.data); // 存储当前节点值
        this.pos = this.pos.right; // 位置指针指向右子树
        flag = true; // 允许遍历当前节点左子树
      } else { // 左右子树都为空
        orderArr.push(this.pos.data); // 存储当前节点值
        if (_stack_.length < 1) { // 如果堆栈为空,遍历结束
          break;
        }
        this.pos = _stack_.pop(); // 栈顶节点出栈
        flag = false; // 不允许遍历当前节点左子树
      }
    }
  }
  return orderArr;
}
复制代码

5. 后序遍历

基本思路:后序遍历就是先左子树,再右子树,再根节点。后序遍历有一个特殊的地方,就是给每个节点对象临时增加了一个_flag_属性,属性值为0表示当前节点的左子树被遍历过了,不允许遍历左子树。为1表示当前节点的右子树也被遍历过了,左右子树都不允许遍历了。

  • 依旧是从根节点开始,this.pos指向根节点,如果左子树不为空且_flag_属性不为0或1,则当前节点入栈,同时当前节点的_flag_值设置为0,this.pos指向当前节点左子树,继续上述过程
  • 如果左子树为空,则判断右子树是否为空,如果右子树不为空且_flag_不为1,则当前节点的_flag_设为1,当前节点入栈,this.pos指向右子树,继续上述过程
  • 如果左右子树都为空,存储当前节点值且删除当前节点的_flag_属性,如果堆栈为空,遍历结束,如果不为空,则取出栈顶节点,重复上述过程。
/**
 * 后序遍历
 */
function lastOrder() {
  var orderArr = [];
  if (this.root === null) {
    return orderArr;
  }
  var _stack_ = [];
  this.pos = this.root;
  while(true) {
    if (this.pos.left !== null && this.pos._flag_ !== 0 && this.pos._flag_ !== 1) {
      this.pos._flag_ = 0;
      _stack_.push(this.pos);
      this.pos = this.pos.left;
    } else {
      if (this.pos.right !== null && this.pos._flag_ !== 1) {
        this.pos._flag_ = 1;
        _stack_.push(this.pos);
        this.pos = this.pos.right;
      } else {
        orderArr.push(this.pos.data);
        if ('_flag_' in this.pos) delete this.pos._flag_;
        if (_stack_.length < 1) {
          break;
        };
        this.pos = _stack_.pop();
      }
    }
  }
  return orderArr;
}
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值