前言
二叉查找树(Binary Search Tree),又称为二叉搜索树,二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树。
二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。
以下面这个二叉搜索树为例,来看一下二叉搜索树的构建以及它的几种遍历方法:
定义一个树的节点类
class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
1、二叉搜索树的构建
public void buildTree(TreeNode node, int data){
//如果根节点为空,则设置根节点,并设置值
if(root == null){
root = new TreeNode(data);
}else{
//根节点不为空,判断data是否小于当前节点的值。
if(data < node.val){
//data小于当前节点的值,判断当前节点有没有左子节点。
if(node.left == null){
//当前节点的左子节点为空,则添加左子节点
node.left = new TreeNode(data);
}else{
//当前节点的左子节点不为空,递归调用buildTree()
buildTree(node.left, data);
}
}else{
//data大于等于当前节点的值,判断当前节点有没有右子节点。
if(node.right == null){
//当前节点的右子节点为空,则添加右子节点
node.right = new TreeNode(data);
}else{
//当前节点的右子节点不为空,递归调用buildTree()
buildTree(node.right, data);
}
}
}
}
2、二叉搜索树的前序遍历
public void preOrder(TreeNode root){
//根节点为空,直接返回
if(root == null){
return;
}
//辅助栈
Stack<TreeNode> stack = new Stack<>();
//根节点入栈
stack.push(root);
//当栈不为空
while(!stack.isEmpty()){
//取出栈顶元素
TreeNode node = stack.pop();
//打印根节点
System.out.print(node.val + "\t");
//如果使用的是辅助栈,则先将根节点的右子节点入栈;如果是辅助队列,则先将根节点的左子节点入队列。因为栈是先进后出,队列是先进入先出
if(node.right != null){
stack.push(node.right);
}
if(node.left != null){
stack.push(node.left);
}
}
}
3、二叉搜索树的中序遍历
public void inOrder(TreeNode root){
// 如果根节点为空,直接返回。
if(root == null){
return;
}
// 辅助栈
Stack<TreeNode> stack = new Stack<>();
//临时指针
TreeNode cur = root;
//当指针不为空或栈不为空
while(cur != null || !stack.isEmpty()){
//先将左节点入栈
while(cur != null){
stack.push(cur);
cur = cur.left;
}
//取出栈顶元素
TreeNode node = stack.pop();
//打印
System.out.print(node.val + "\t");
//指向右节点
cur = node.right;
}
}
4、二叉搜索树的后序遍历
public void postOrder(TreeNode root){
//根节点为空,直接返回
if(root == null){
return;
}
//辅助栈
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root, pre = null;
while(cur != null || !stack.isEmpty()){
//找到最左边节点
while(cur != null){
stack.push(cur);
cur = cur.left;
}
//栈顶元素出栈
cur = stack.pop();
//如果节点的右节点为空或者已经被访问过
if(cur.right == null || pre == cur.right){
System.out.print(cur.val + "\t");
//pre表示当前节点被访问过
pre = cur;
cur = null;
}else{
//节点入栈
stack.push(cur);
//访问右边
cur = cur.right;
}
}
}
5、二叉搜索树的层序遍历
public void levelOrder(TreeNode root){
//根节点为空,直接返回
if(root == null){
return;
}
//辅助队列
Queue<TreeNode> queue = new LinkedList<>();
//根节点入队
queue.offer(root);
while(!queue.isEmpty()){
//获取队首元素
TreeNode node = queue.poll();
System.out.print(node.val + "\t");
//左右节点分别入队
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
}
6、二叉搜索树的之字形遍历
public void sweepOrder(TreeNode root){
//根节点为空,直接返回
if(root == null){
return;
}
//存储结果
List<ArrayList<Integer>> result = new ArrayList<>();
//辅助队列
Queue<TreeNode> queue = new LinkedList<>();
//根节点入队
queue.offer(root);
//是否转向
boolean flag = false;
while(!queue.isEmpty()){
//获取队列长度
int size = queue.size();
//存储每一层的遍历结果
ArrayList<Integer> list = new ArrayList<>();
for(int i=0; i < size; i++){
//取出队列元素
TreeNode node = queue.poll();
if(node == null){
continue;
}
if(!flag){
list.add(node.val);
}else{
list.add(0, node.val);
}
//左右节点各入队
queue.offer(node.left);
queue.offer(node.right);
}
//如果有值,存入结果集
if(list.size() > 0){
result.add(list);
}
//转向
flag = !flag;
}
System.out.println(result);
}
完整代码如下:
import java.util.*;
class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
public class BinaryTree {
public TreeNode root;
public void buildTree(TreeNode node, int data){
//如果根节点为空,则设置根节点,并设置值
if(root == null){
root = new TreeNode(data);
}else{
//根节点不为空,判断data是否小于当前节点的值。
if(data < node.val){
//data小于当前节点的值,判断当前节点有没有左子节点。
if(node.left == null){
//当前节点的左子节点为空,则添加左子节点
node.left = new TreeNode(data);
}else{
//当前节点的左子节点不为空,递归调用buildTree()
buildTree(node.left, data);
}
}else{
//data大于等于当前节点的值,判断当前节点有没有右子节点。
if(node.right == null){
//当前节点的右子节点为空,则添加右子节点
node.right = new TreeNode(data);
}else{
//当前节点的右子节点不为空,递归调用buildTree()
buildTree(node.right, data);
}
}
}
}
public void preOrder(TreeNode root){
//根节点为空,直接返回
if(root == null){
return;
}
//辅助栈
Stack<TreeNode> stack = new Stack<>();
//根节点入栈
stack.push(root);
//当栈不为空
while(!stack.isEmpty()){
//取出栈顶元素
TreeNode node = stack.pop();
//打印根节点
System.out.print(node.val + "\t");
//如果使用的是辅助栈,则先将根节点的右子节点入栈;如果是辅助队列,则先将根节点的左子节点入队列。因为栈是先进后出,队列是先进入先出
if(node.right != null){
stack.push(node.right);
}
if(node.left != null){
stack.push(node.left);
}
}
}
public void inOrder(TreeNode root){
// 如果根节点为空,直接返回。
if(root == null){
return;
}
// 辅助栈
Stack<TreeNode> stack = new Stack<>();
//临时指针
TreeNode cur = root;
//当指针不为空或栈不为空
while(cur != null || !stack.isEmpty()){
//先将左节点入栈
while(cur != null){
stack.push(cur);
cur = cur.left;
}
//取出栈顶元素
TreeNode node = stack.pop();
//打印
System.out.print(node.val + "\t");
//指向右节点
cur = node.right;
}
}
public void postOrder(TreeNode root){
//根节点为空,直接返回
if(root == null){
return;
}
//辅助栈
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root, pre = null;
while(cur != null || !stack.isEmpty()){
//找到最左边节点
while(cur != null){
stack.push(cur);
cur = cur.left;
}
//栈顶元素出栈
cur = stack.pop();
//如果节点的右节点为空或者已经被访问过
if(cur.right == null || pre == cur.right){
System.out.print(cur.val + "\t");
//pre表示当前节点被访问过
pre = cur;
cur = null;
}else{
//节点入栈
stack.push(cur);
//访问右边
cur = cur.right;
}
}
}
public void levelOrder(TreeNode root){
//根节点为空,直接返回
if(root == null){
return;
}
//辅助队列
Queue<TreeNode> queue = new LinkedList<>();
//根节点入队
queue.offer(root);
while(!queue.isEmpty()){
//获取队首元素
TreeNode node = queue.poll();
System.out.print(node.val + "\t");
//左右节点分别入队
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
}
public void sweepOrder(TreeNode root){
//根节点为空,直接返回
if(root == null){
return;
}
//存储结果
List<ArrayList<Integer>> result = new ArrayList<>();
//辅助队列
Queue<TreeNode> queue = new LinkedList<>();
//根节点入队
queue.offer(root);
//是否转向
boolean flag = false;
while(!queue.isEmpty()){
//获取队列长度
int size = queue.size();
//存储每一层的遍历结果
ArrayList<Integer> list = new ArrayList<>();
for(int i=0; i < size; i++){
//取出队列元素
TreeNode node = queue.poll();
if(node == null){
continue;
}
if(!flag){
list.add(node.val);
}else{
list.add(0, node.val);
}
//左右节点各入队
queue.offer(node.left);
queue.offer(node.right);
}
//如果有值,存入结果集
if(list.size() > 0){
result.add(list);
}
//转向
flag = !flag;
}
System.out.println(result);
}
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
// int[] datas = {72,37,29,55,51,80};
int[] datas = {12,5,2,9,18,15,17,16,19};
for(int n : datas){
binaryTree.buildTree(binaryTree.root, n);
}
System.out.println("前序遍历");
binaryTree.preOrder(binaryTree.root);
System.out.println();
System.out.println("中序遍历");
binaryTree.inOrder(binaryTree.root);
System.out.println();
System.out.println("后序遍历");
binaryTree.postOrder(binaryTree.root);
System.out.println();
System.out.println("层序遍历");
binaryTree.levelOrder(binaryTree.root);
System.out.println();
System.out.println("之字形遍历");
binaryTree.sweepOrder(binaryTree.root);
}
}
执行结果如下: