数据结构之二叉树(一)(附Java、C++代码)

 前言

        简单讲述二叉树相关知识,建议小伙伴们在理解基础概念的基础上,对照代码部分近一步加深理解,兼顾学习理论知识的同时巩固算法!!一起加油啊!~

种类

满二叉树 

        如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。也可以说深度为k,有2^k-1个节点的二叉树。

完全二叉树

        在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。

注:优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系 

二叉搜索树

        二叉搜索树是一个有序树

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

平衡二叉树

        AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

存储方式

        顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在各个地址的节点串联一起。

链式存储

顺序存储(用得少)

        用数组来存储二叉树,左孩子为 2 * i + 1,右孩子为 2 * i + 2;

深度优先遍历

        可以理解为“一条路走到黑”,递归遍历方式,一般用栈来存储;如前中后序遍历。所谓前中后序遍历指的是访问根节点的位置顺序。

广度优先遍历

        可以理解为像水波一样一层一层去遍历,如层序(迭代)遍历方式,一般用队列来存储

二叉树的定义代码版

Java 版本:

public class TreeNode{
    int val;
    TreeNode left;
    TreeNode right;
    
    TreeNode(){}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right){
        this.val = val;
        this.left = left;
        this.right = right;
    }

C++版本:

struct TreeNode{
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

准备解题啦!

递归遍历

步骤:
1、确定递归函数的参数和返回值
2、确定终止条件
3、确定单层递归的逻辑
       以前序为例, C++版本:

class Solution{
public:
    void traversal(TreeNode *cur, vector<int>& vec){
        if(cur == NULL) return;
        //前序遍历
        vec.push(cur->value);  //根节点
        traversal(cur->left,vec);   //遍历左孩子
        traversal(cur->right,vec);  //遍历右孩子
    }
    vector<int> preorderTraversal(TreeNode* root){
        vector<int> result;
        traversal(root,result);
        return result;
    }
};

注:中序、后序遍历为上述最后三句代码交换位置
        以前序为例,Java版本:

class Solution{
    public List<Integer> preorderTarversal(TreeNode root) {
        List<Interger> result = new ArrayList<Integer>();
        preorder(root,result);
        return result;
    }
    public void preorder(TreeNode root,List<Integer> list){
        if (root == null) return;
        list.add(root.val);
        preorder(root.left, list);
        preorder(root.left, list);
    }
}

迭代遍历

        迭代遍历和递归遍历不同,不能简单的进行语句交换,这是由实现迭代的数据结构——栈的特点决定的。

        前序: C++版本:

// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root){
        stack<TreeNode*> st; //设置一个栈来存放中间结果
        vector<int> result;
        if ( root == NULL) return result;
        st.push(root);  //前序遍历先把根节点放进去
        
        while(!st.empty()) {
            TreeNode* node = st.top();    //获取“中”值
            st.pop();
            result.push_back(node->val);  //“中”值放入结果中

            //这里需要先放入右孩子,因为栈的结构是先进后出
            //这里还需要判断左右孩子是否存在,因为空节点不入栈
            if(node->right)  st.push(Node->right);  //如果有右孩子,则把右孩子放进去
            if(node->left)   st.push(Node->left);   //如果有左孩子,则把左孩子放进去
        }
        return result;
    }
};

        前序:Java版本:

// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if(root == null) return result;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()) {
            TreeNode node = stack.pop();  //存放“中”
            result.add(node.val);
            if(node.right != null) stack.push(node.right); //存放“右”
            if(node.left != null) stack.push(node.left);  //存放“左”
            return result;
        }
    }
}

        在进行后序遍历之前,先进行一个分析如下,所以可以直接在前序遍历的基础上进行代码改进——调换左右顺序并进行数组反转。

         后序:C++版本:

// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root){
        stack<TreeNode*> st;
        stack<int> result;
        if (root == NULL) return result;
        st.push(root);
        while(!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node);
            if(node->left) st.push(node->left);
            if(node->right) st.push(node->right);
        }
        reverse(result.begin(),result.end());
        return result;
    }
};

        后序:Java版本:

// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution(){
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) return result;
        Stack<TreeNode> st;
        st.push(root);
        while(!st.isEmpty()){
            TreeNode node = st.pop();
            TreeNode.add(node.val);
            if(node.left!=null) st.add(node.left);
            if(node.right!=null) st.add(node.right);
        }
        Collections.reverse(result);
        return result;
    }
}

        中序:C++版:

// 中序遍历顺序: 左-中-右 入栈顺序: 左-右
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while(cur !=NULL || !st.empty()) {
            if(cur != NULL) {
                st.push(cur);
                cur = cur->left;  //这个时候就是不停放入左孩子(遍历左子树)
            } else {
                cur = st.top();  //到最底层以后开始弹出
                st.pop();
                result.push_back(cur->val);  //把当前值加入结果序列
                cur = cur->right; //每次把当前值弹出以后就去遍历它的右孩子
            }
        }
        return result;
    }
};

        中序:Java版本:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if(root == null) return result;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur!=null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop();
                result.add(cur.val);
                cur = cur.right;
            }
        }
        return result;
    }
}

层序遍历

        C++版本:

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*>que;  //创建队列存放节点
        if(root != nullptr) que.push(root);
        vector<vector<int>>result;  //因返回结果是一层一层的,所以这里定义一个二维数组

        while(!que.empty()){
            int size = que.size();
            vector<int>vec;

            //这里不能用que.size()--作为判断条件,因为它在动态变化
            while(size--){
                TreeNode* node = que.front(); 
                que.pop();
                vec.push_back(node->val);
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result.push_back(vec);               
        }
        return result;
    }
};

        Java版本:

class Solution {
    public List<List<Integer>> resList = new ArrayList<List<Integer>>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        if (root == null) return resList;
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(root);
        while (!que.isEmpty()) {
            List<Integer> itemList = new ArrayList<Integer>();
            int len = que.size();
            while (len > 0) {
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);
                if (tmpNode.left != null) que.offer(tmpNode.left);
                if (tmpNode.right != null) que.offer(tmpNode.right);
                len--;
            }
            resList.add(itemList);
        }
        return resList;
    }
}

求二叉树每一层的平均值 

        本质上还是层序遍历, 只需要在每一层的末尾计算一次平均值即可

        C++ 版本:

class Solution {
public:
    vector<double> averageOfLevels(TreeNode* root) {
        vector<double> result;
        queue<TreeNode*> que;
        if(root == NULL) return result;
        que.push(root);
        while(!que.empty()){
            double sum = 0;
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                sum += node->val;
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result.push_back(sum / size);
        }
        return result;
    }
};

        Java 版本: 

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        Deque<TreeNode> que = new LinkedList<>();
        List<Double> result = new ArrayList<>();
        if(root != null) que.add(root);
        while(!que.isEmpty()){
            Double sum = 0.0;
            int size = que.size();
            for(int i = 0;i < size; i++){
                TreeNode node = que.poll();
                sum += node.val;
                if(node.left != null) que.add(node.left);
                if(node.right != null) que.add(node.right);
            }
            result.add(sum / size);
        }
        return result;
    }
}

 求N叉树的层序遍历

        解决问题的关键在于如何对节点的孩子进行插入操作,其实只需要进行一个循环判断就可以了~~~

        另外还要注意这里对节点的定义!!!不要搞错了哈~

        C++ 版本:

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        queue<Node*> que;
        vector<vector<int>> result;
        if(root != NULL) que.push(root);
        while(!que.empty()){
            vector<int> cur;
            int size = que.size();
            while(size--){
                Node* node = que.front();
                que.pop();
                cur.push_back(node->val);
                for(int i = 0; i < node->children.size(); i++){
                    if(node->children[i]) que.push(node->children[i]);
                }
            }
            result.push_back(cur);
        }
        return result;
    }
};

        Java 版本:

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        Deque<Node> que = new LinkedList<>();
        List<List<Integer>> result = new ArrayList<>();
        if (root != null) que.add(root);
        while(!que.isEmpty()){
            int size = que.size();
            List<Integer> cur = new ArrayList<>();
            for(int i = 0; i< size; i++){
                Node node = que.poll();
                cur.add(node.val);
                List<Node> children = node.children;
                for (Node child : children) {
                    if (child != null) {
                        que.add(child);
                    }
                }
            }
            result.add(cur);
        }
        return result;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值