二叉树的创建和遍历

一、二叉树的概念

1、定义

每个节点最多只有2个子节点的树叫做二叉树。

2、特性

A、在二叉树的第i层上最多有2^(i-1)个结点(i>=1)。

B、高度为k的二叉树,最多有2^k-1个结点(k>=0)。

C、对任何一棵二叉树,如果其叶结点有n个,度为2的非叶子结点有m个,则n = m + 1。

D、具有n个结点的完全二叉树的高度为logn + 1

E、对于有n个结点的完全二叉树,按层次对结点进行编号(从上到下,从左到右),对于任意编号为i的结点:

二、二叉树构建

1、二叉树的存储实现

java 复制代码
public class BinaryNode<T> {

    /**
     * 数据
     */
    T data;

    /**
     * 左节点
     */
    private BinaryNode left;

    /***
     * 右节点
     */
    private BinaryNode right;


    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public BinaryNode getLeft() {
        return left;
    }

    public void setLeft(BinaryNode left) {
        this.left = left;
    }

    public BinaryNode getRight() {
        return right;
    }

    public void setRight(BinaryNode right) {
        this.right = right;
    }
}

2、二叉树的创建

java 复制代码
public class BinaryTree<T> {

    private BinaryNode<T> root;



    // 树节点总数
    private int size ;

    public BinaryTree(T data) {
        root = new BinaryNode<T>();
        root.setData(data);
        size = 1;
    }

    /**
     * 给节点添加左节点
     * @param parent
     * @param data
     * @return
     */
    public BinaryNode addLeftNode(BinaryNode parent,T data) {
        BinaryNode<T> left = new BinaryNode<T>();
        left.setData(data);
        parent.setLeft(left);
        size++;
        return left;
    }

    /**
     * 给节点添加右节点
     * @param parent
     * @param data
     * @return
     */
    public BinaryNode addRightNode(BinaryNode parent,T data) {
        BinaryNode<T> right = new BinaryNode<T>();
        right.setData(data);
        parent.setRight(right);
        size++;
        return right;
    }

    public BinaryNode<T> getRoot() {
        return root;
    }


    public int getSize() {
        return size;
    }


}

构造一颗二叉树,如下图

代码如下:

ini 复制代码
BinaryTree<String> binaryTree = new BinaryTree<String>("A");
BinaryNode n2 = binaryTree.addLeftNode(binaryTree.getRoot(), "B");
BinaryNode n3 = binaryTree.addRightNode(binaryTree.getRoot(), "C");

BinaryNode n4 = binaryTree.addLeftNode(n2, "D");
BinaryNode n5 = binaryTree.addRightNode(n2, "E");

BinaryNode n6 = binaryTree.addLeftNode(n3, "F");
BinaryNode n7 = binaryTree.addRightNode(n3, "G");

三、二叉树遍历

1、前序遍历

前序遍历(根左右): 访问根结点,再访问左子树、再访问右子树。 前序遍历: ABDECFG

递归实现

scss 复制代码
public List<T> preorderTraversal () {
    ArrayList<T> ts = new ArrayList<>();
    preorderTraversal(root,ts);
    return ts;
}

/**
 * 前序遍历
 * @param binaryNode 节点
 * @param result 结果的list
 */
private void preorderTraversal (BinaryNode<T> binaryNode, List<T> result) {
    // 节点不为空,
    if (binaryNode != null) {
        result.add(binaryNode.getData());
    }
    if (binaryNode.getLeft() != null) {
        preorderTraversal(binaryNode.getLeft(),result);
    }
    if (binaryNode.getRight() != null) {
        preorderTraversal(binaryNode.getRight(),result);
    }
}

非递归实现

用到栈(FILO 先进后出的特性)

过程:

arduino 复制代码
/**
 * 非递归的前序遍历
 * @return
 */
public List<T> noRecursivePreTraversal () {
    ArrayList<T> ts = new ArrayList<>();
    noRecursivePreTraversal(root,ts);
    return ts;
}

/***
 * 非递归的前序遍历
 * @param binaryNode
 * @param result
 */
private void noRecursivePreTraversal (BinaryNode<T> binaryNode,List<T> result) {
    // 借助stack来实现
    Stack<BinaryNode> stack = new Stack<>();
    // 迭代的停止条件
    while (binaryNode != null || stack.size() > 0) {
        // 一直向左边进行迭代
        while (binaryNode != null) {
            // 把当前节点添加到结果集中
            result.add(binaryNode.getData());
            // 节点进栈,为了后面好,进行回溯
            stack.add(binaryNode);
            // 向左进行迭代
            binaryNode = binaryNode.getLeft();
        }
        // 循环走完了,要进行出栈
        if (stack.size() > 0) {
            binaryNode = stack.pop();
            // 向右边走
            binaryNode = binaryNode.getRight();
        }
    }
}

2、中序遍历

中序遍历(左根右): 先访问左子树,再访问根结点、再访问右子树。 中序遍历: DBEAFCG

递归实现

scss 复制代码
/**
 * 中序遍历
 * @return
 */
public List<T> midTraversal() {
    ArrayList<T> ts = new ArrayList<>();
    midTraversal(root,ts);
    return ts;
}


private void  midTraversal(BinaryNode<T> binaryNode,List<T> result) {
    if (binaryNode != null) {
        if (binaryNode.getLeft() != null) {
            midTraversal(binaryNode.getLeft(),result);
        }
        result.add(binaryNode.getData());
        if (binaryNode.getRight() != null) {
            midTraversal(binaryNode.getRight(),result);
        }
    }
}

非递归实现

csharp 复制代码
/**
 * 非递归中序遍历
 * @return
 */
public List<T> noRecursiveMidTraversal() {
    ArrayList<T> ts = new ArrayList<>();
    noRecursiveMidTraversal(root,ts);
    return ts;
}

private void  noRecursiveMidTraversal(BinaryNode<T> binaryNode,List<T> result) {
    // 借助栈
    Stack<BinaryNode> stack = new Stack<>();
    // 栈不为空
    while (!stack.isEmpty() || binaryNode !=null) {
        // 一直向左迭代
        while (binaryNode != null) {
            stack.add(binaryNode);
            binaryNode = binaryNode.getLeft();
        }
        // 出栈
        binaryNode = stack.pop();
        //  保存结果集
        result.add(binaryNode.getData());
        binaryNode = binaryNode.getRight();
    }

}

3、后序遍历

后续遍历(左右根): 先访问左子树,再访问右子树,再访问根结点。 后续遍历: DEBFGCA

递归实现

scss 复制代码
/**
 * 后序遍历
 * @return
 */
public List<T> afterTraversal() {
    ArrayList<T> ts = new ArrayList<>();
    afterTraversal(root,ts);
    return ts;
}


private void  afterTraversal(BinaryNode<T> binaryNode,List<T> result) {
    if (binaryNode != null) {
        // 先左节点
        if (binaryNode.getLeft() != null) {
            afterTraversal(binaryNode.getLeft(),result);
        }
        // 再右节点
        if (binaryNode.getRight() != null) {
            afterTraversal(binaryNode.getRight(),result);
        }
        // 父节点
        result.add(binaryNode.getData());
    }
}

非递归实现

scss 复制代码
/**
 * 后序遍历
 * @return
 */
public List<T> noRecursiveAfterTraversal() {
    ArrayList<T> ts = new ArrayList<>();
    noRecursiveAfterTraversal(root,ts);
    return ts;
}



private void  noRecursiveAfterTraversal(BinaryNode<T> binaryNode,List<T> result) {

    Stack<BinaryNode> stackA = new Stack<>();
    Stack<BinaryNode> stackB = new Stack<>();

    stackA.push(binaryNode);

    while (!stackA.isEmpty()) {
        binaryNode = stackA.pop();
        stackB.push(binaryNode);
        // 左边有节点
        if (binaryNode.getLeft() != null) {
            stackA.push(binaryNode.getLeft());
        }
        // 右边有节点
        if (binaryNode.getRight() != null) {
            stackA.push(binaryNode.getRight());
        }
    }

    while (!stackB.isEmpty()) {
        BinaryNode<T> pop = stackB.pop();
        result.add(pop.getData());
    }
}

4、二叉树层序遍历(BFS)

用到队列(FIFO 先进先出的特性)代码后有队列和其中元素的关系具体过程,建议静下心来慢慢看,有助于理解代码如何运行

scss 复制代码
/**
 * 层序遍历
 * @return
 */
public List<T> bfsTraversal() {
    ArrayList<T> ts = new ArrayList<>();
    bfsTraversal(root,ts);
    return ts;
}

private void  bfsTraversal(BinaryNode<T> binaryNode,List<T> result) {
    // 借助队列
    Queue<BinaryNode> queue = new ArrayDeque();
    if (binaryNode != null) {
        queue.add(binaryNode);
    }
    // 队列不为空
    while (!queue.isEmpty() ) {
        // 出队
        binaryNode = queue.poll();
        result.add(binaryNode.getData());
        // 左边有节点
        if (binaryNode.getLeft() != null) {
            queue.add(binaryNode.getLeft());
        }
        // 右边有节点
        if (binaryNode.getRight() != null) {
            queue.add(binaryNode.getRight());
        }
    }

}
相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
倒头就睡的小比特4 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码4 天前
别再前后端各写一套表单校验了
java·后端
大勇前进4 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
猎头南楼4 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
yuzhi_liu4 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile4 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go