浅谈二叉树

一、树(Tree)

树形结构是一种非线性数据结构;树是以分支关系定义的层次结构。

问题来了,什么是线性数据结构?什么是非线性数据结构?
线性结构是一组有序数据元素的集合,比如数组队列,除了第一个元素和最后一个元素以外,其他元素都是首尾相连的。
非线性结构中各个数据元素没有对应的顺序,每个数据元素都可能和零个或多个数据元素发生关系,典型的非线性结构包括二维数组

生活中比较常见的树形结构:企业组织层级架构、计算机文件系统、族谱等。

二、二叉树(Binary Tree)

二叉树是一种特殊的树形结构,每个节点(Node)最多只能有两棵子树(SubTree),子树有左右之分,顺序不能任意颠倒。

那么二叉树有何特点呢?
可以高效的插入、查找和删除数据。


三、二叉查找树(Binary Search Tree)的实现

二叉查找树(BST)是一种特殊的二叉树,相对较小的值保存在左子树中,较大的值保存在右子树中。

首先定义节点,节点是组成二叉树的基本单元。(以下代码均用JavaScript实现)

function Node(data, left, right) {
  this.data = data; // 节点值
  this.left = left;  // 左子树(左节点)
  this.right = right;  // 右子树(右节点)
  this.show = show;  // 显示当前节点值
}

function show() {
  return this.data;
}


其次定义BST类,包含:
1.一个根节点,初始化为null
2.向BST对象中插入节点的insert方法

function BST() {
  this.root = null;
  this.insert = insert;
}

function insert(data) {
  var n = new Node(data, null, null); 
  
  if (this.root === null) {
    this.root = n;
  } else {
    var current = this.root;
    var parent;
    while (true) {
      parent = current;
      if (data < parent.data) {
        current = current.left;
        if (current === null) {
          parent.left = n;
          break;
        }
      } else {
        current = current.right;
        if (current === null) {
          parent.right = n;
          break;
        }
      }
    }
  }
}


遍历二叉查找树
所谓遍历,即按照某种顺序访问树中每个节点,使得每个节点都被访问一次,且仅被访问一次。
二叉树由三个基本单元组成——根节点、左节点和右节点,所以遍历整个二叉树,也就可以看作依次遍历这三部分。
一般遵循先左后右的原则,所以常见的遍历方案有前序遍历(根左右)、中序遍历(左根右)和后序遍历(左右根)。

前序遍历

function preOrder(node) {
  if (!(node === null)) {
    console.log(node.show());
    preOrder(node.left);
    preOrder(node.right);
  }
}

 

中序遍历

function inOrder(node) {
  if (!(node === null)) {
    inOrder(node.left);
    console.log(node.show());
    inOrder(node.right);
  }
}

 

后序遍历

function postOrder(node){
  if (!(node ===null)) {
    postOrder(node.left);
    postOrder(node.right);
    console.log(node.show());
  }
}

 

以上实现基于递归算法


下面介绍非递归算法

非递归算法需要用到另外一种数据结构——,本篇对栈不进行展开叙述,只贴出简单的JavaScript实现

function Stack() {
  this.dataStore = [];
  this.top = 0;
  this.push = push; // 入栈 
  this.pop = pop; // 出栈
  this.peek = peek; // 返回栈顶元素
  this.clear = clear; // 清空栈
  this.length = length; // 返回栈内元素个数
}

function push(element) {
  this.dataStore[this.top++] = element;
}

function pop() {
  return this.dataStore[--this.top];
}

function peek() {
  return this.dataStore[this.top - 1];
}

function clear() {
  this.top = 0;
}

function length() {
  return this.top;
}

 

前序遍历非递归算法

思路:

1.当前节点为根节点,根节点不为空且栈不为空

2.当前节点不为空,访问当前节点,当前节点入栈,访问当前节点左子树

3.否则,节点出栈,当前节点指向出栈节点右子树

function preOrder_Stack(root) {
  var result = [];
  var stack = new Stack();
  var p = root;

  while (stack.length() || p !== null) {
    if (p !== null) {
      stack.push(p);
      result.push(p.data); // 在遍历左子树之前加入根元素
      p = p.left;
    } else {
      var node = stack.pop();
      p = node.right;
    }
  }
  return result;
}

 

中序遍历非递归算法

思路:

1.当前节点为根节点,根节点不为空且栈不为空

2.当前节点不为空,当前节点入栈,访问当前节点左子树

3.否则,节点出栈,访问出栈节点,当前节点指向出栈节点右子树

function inOrder_Stack(root) {
  var result = [];
  var stack = new Stack();
  var p = root;

  while (stack.length() || p !== null) {
    if (p !== null) {
      stack.push(p);
      p = p.left;
    } else {
      var node = stack.pop();
      result.push(node.data); // 遍历完左子树之后加入根元素
      p = node.right;
    }
  }
  return result;
}

 

后序遍历非递归算法

思路:

后序遍历访问顺序为左右根,可以换个思路,按照根右左的顺序访问,然后利用数组unshift方法实现结果反转,这样就转化成根右左的遍历算法

1.当前节点为根节点,根节点不为空且栈不为空

2.当前节点不为空,访问当前节点,当前节点入栈,访问当前节点右子树

3.否则,节点出栈,当前节点指向出栈节点左子树

function postOrder_Stack(root) {
  var result = [];
  var stack = new Stack();
  var p = root;

  while (stack.length() || p !== null) {
    if (p !== null) {
      stack.push(p);
      result.unshift(p.data);
      p = p.right;
    } else {
      var node = stack.pop();
      p = node.left;
    }
  }
  return result;
}


四、备注

1.第一次写文,肯定有诸多不足,请多多指教,多多批评!

2.花时间写这篇文章的时候,才真的体会到其中的不易,向各位前辈致敬!

3.参考书目:

[美]Michael McMillan.数据结构与算法JavaScript描述[M]王群锋、杜欢译.北京:人民邮电出版社,2014

严蔚敏、吴伟民.数据结构(C语言版)[M].北京:清华大学出版社,1997

4.参考技术博客:

线性结构和非线性结构 - CSDN博客​blog.csdn.net
 
 
posted @ 2018-08-02 10:17  ShawnCui  阅读(298)  评论(0编辑  收藏  举报