二叉树

1、二叉树理论基础

1:二叉树的种类

(1)满二叉树满二叉树的节点数量是 2 k − 1 2^k-1 2k1,完全二叉树的底部从左到右一定是连续的,但是不一定是满的,这是完全二叉树与满二叉树的区别。如大顶堆和小顶堆其实就算完全二叉树。

(2)二叉搜索树二叉搜索树里面的节点是有顺序的,但是对布局没有要求。

**(3)平衡二叉搜索树:**左子树和右子树的高度之差的绝对值不能多于1。map,set等数据结构的底层实现都是平衡二叉搜索树。

2:二叉树的存储方式

链式存储一个结构体,里面有三个成员,值,左指针,右指针。

数组存储将二叉树按顺序存储再数组中,其左子为2i+1,右子为2i+2。其中i为父节点的下标。

3:二叉树的遍历

深度优先搜索深度优先搜索就是一直向下搜索,知道搜索到底,然后在回退。回退之后再进行搜索。

广度优先搜索一层一层的用队列去寻找。

前序遍历:中左右,中序遍历:左中右,后序遍历:左右中。

4:二叉树的定义

struct TreeNode
{
   int val;
   TreeNode *left;
   TreeNode *right;
}

2、二叉树的递归遍历

递归核心:1、确定递归函数的参数和返回值 2、确定终止条件 3、确定单层递归的逻辑

编程语言的低谷递归相当于数据结构里面的栈。

struct TreeNode
{
    int val;
    TreeNode *left;
    TreeNode *right;
};
class pre_bianli{
public:
    void traversal(TreeNode* cur, vector<int> &vec)
    {
        if(cur == nullptr)
            return;
        else
        {
            vec.push_back(cur->val);//中
            traversal(cur->left,vec);//左
            traversal(cur->right,vec);//右
        }
    }
    vector<int> result(TreeNode *cur)
    {
        vector<int> res;
        traversal(cur,res);
        return res;
    }
};

3、非递归遍历

模拟栈的操作

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;
    }
};

4、非递归遍历(中序遍历)

#include 
#include 
#include 
#define main1 main
using namespace  std;
struct  treeNode{
    int val;
    treeNode* left;
    treeNode* right;
};
vector<int> nums(treeNode *root);
int main1()
{
    treeNode* node7 = new treeNode();
    treeNode* node6 = new treeNode();
    treeNode* node5 = new treeNode();
    treeNode* node4 = new treeNode();
    treeNode* node3 = new treeNode();
    treeNode* node2 = new treeNode();
    treeNode* node1 = new treeNode();

    *node6 = {6, NULL, NULL};
    *node7 = {7, nullptr, nullptr};
    *node3 = {3, nullptr, nullptr};
    *node4 = {4, nullptr, nullptr};
    *node5 = {5,node6,node7};
    *node2 = {2,node3,node4};
    *node1 = {1,node2,node5};

    vector<int>b = nums(node1);
    for(int i:b)
    {
        cout << i << endl;
    }
    return 0;
}
vector<int> nums(treeNode *root)
{
    vector<int> result;
    stack<treeNode*> st;
    treeNode *cur = root;
    while(!st.empty() || cur != nullptr)
    {
        if(cur != nullptr)
        {
            st.push(cur);
            cur = cur->left;
        }
        else
        {
            cur = st.top();
            result.push_back(cur->val);
            st.pop();
            cur = cur->right;
        }
    }
    return result;
}

5、层序遍历二叉树

模拟队列来实现广度优先搜索。

vector<int> wide_search(treeNode *root)
{
    queue<treeNode *> que;
    vector<int> result;
    treeNode *cur = root;
    que.push(cur);
    while(!que.empty())
    {
        result.push_back(cur->val);
        que.pop();
        if(cur->left != nullptr)
            que.push(cur->left);
        if(cur->right != nullptr)
            que.push(cur->right);
        cur = que.front();
    }
    return result;
}

6、翻转二叉树

要求:将二叉树的左子树和右子树交换位置。

**思路:**使用前序遍历或者后序遍历

伪代码:

TreeNode *invertTree(root)
{
    if((root == nullptr))
        return root;
    swap(root->left,root->right);
    invertTree(root->left);
    invertTree(root->right);
}

7、对称二叉树

要求:判断一个二叉树是否是左右对称的

如果是优先需要子节点的话,那么往往选择后序遍历。

一层一层的遍历,直到遍历到叶子节点

bool compate_lr(treeNode *left, treeNode *right)
{
    if (left == nullptr && right != nullptr) return false;
    else if (left != nullptr && right == nullptr) return false;
    else if (left == nullptr && right == nullptr) return true;
    else if (left->val != right->val) return false; // 注意这里我没有使用else
    bool outside = compate_lr(left->left,right->right);
    bool inside = compate_lr(left->right,right->left);
    return (outside)&&(inside);
}

8、二叉树的最大深度

什么是深度和高度: 深度是二叉树里面到根节点的距离,高度是二叉树的任意节点到叶子节点的距离。求深度用后续遍历,求高度用前序遍历。

思路(操作的是中间节点):

**注意:**使用递归时优先出现的是返回值,不能等到递归结束后才有返回值

int shengdu(treeNode *root)
{
    if(root == nullptr) return 0;
    int left_s = shengdu(root->left);
    int right_s = shengdu(root->left);
    int tol = 1 + max(left_s,right_s);
    return tol;
}

9、二叉树的最小深度

**最小深度的定义:**根节点到最近的叶子节点的距离。

遍历顺序依然选择后序遍历。

int min_shengdu(treeNode *root)
{
    if(root == nullptr)
        return 0;
    int left_s = min_shengdu(root->left);
    int right_s = min_shengdu(root->right);
    if(root->right == nullptr &&root->left != nullptr)
        return 1 + left_s;
    if(root->left == nullptr &&root->right != nullptr)
        return 1 + right_s;
    return 1+ min(left_s,right_s);
}

10、完全二叉树节点的数量

当树是普通二叉树时,使用后序遍历求节点数量即可,伪代码如下:

int get_num(node)
{
    if(node == null)
        return 0;
    left = get_num(node->left);
    right = get_num(node->right);
    return left + right + 1;
}

完全二叉树指的是除了底层节点,其他的节点都是满的。并且底层节点是从左边到右边是连续的。满二叉树的节点数量为 2 i − 1 2^i-1 2i1

int getNum(node)
{
    if(node == null)  return 0;
    left = num->left;
    right = num->right;
    left_num = 0;
    right_num = 0;
    while(left)
    {
        left = left->left;
        left_num++;
    }
    while(right)
    {
        right = right->right;
        right_num++;
    }
    if(left_num = right_mum)  return (2<<left_num) - 1;
 	left_num = get_num(left);
    right_num = get_num(right);
    return left_num + right_num + 1;   
}

11、平衡二叉树

平衡二叉树指的是任何一个节点的左子树和右子树的高度差小于等于1。

int get_height(node)
{
    if(node = null)  return 0;
    int left_height = get_height(node);
    if(left_height == -1)  return -1;
    int right_height = get_height(node);
    if(right_height == -1) return -1;
    if(abs(right_height - left_height)>1) 
        result = -1;
    else
        return 1 + max(left_height,right_height);
    return result;
}

12、二叉树的所有路径

二叉树的路径指的是根节点到叶子节点的距离。

**回溯:**递归到return返回就是回溯,即把当前操作完的中间节点弹出。

  void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {
        path.push_back(cur->val); // 中,中为什么写在这里,因为最后一个节点也要加入到path中 
        // 这才到了叶子节点
        if (cur->left == NULL && cur->right == NULL) {
            string sPath;
            for (int i = 0; i < path.size() - 1; i++) {
                sPath += to_string(path[i]);
                sPath += "->";
            }
            sPath += to_string(path[path.size() - 1]);
            result.push_back(sPath);
            return;
        }
        if (cur->left) { // 左 
            traversal(cur->left, path, result);
            path.pop_back(); // 回溯
        }
        if (cur->right) { // 右
            traversal(cur->right, path, result);
            path.pop_back(); // 回溯
        }
    }

13、左叶子之和

使用后序遍历(如果是对叶子节点进行处理,一般都用后序遍历)

int sum_left_leaf(treeNode *root)
{
    if(root == nullptr)
        return 0;
    if(root != nullptr && root->left == nullptr)
        return 0;
    int left_ls = sum_left_leaf(root->left);
    if(root->left != nullptr && root->left->left == nullptr)
        left_ls = root->left->val;
    int right_ls = sum_left_leaf(root->right);
    return left_ls + right_ls;
}

14、找左下角的值

要求:找二叉树最后一行最左边的值

回溯的过程一般隐藏在递归后面。

伪代码:

int max_Depth = 0;
int result;
void travel(root,depth)
{
    if(root->left == null && root->right == null)
    {
        if(depth > max_Depth)
        {
            max_Depth = depth;
            result = root->val;
        }
    }
    if(root->left)
    {
        depth++;
        travel(root->left,depth);
        depth--;//回溯
    }
    if(root->right)
    {
        depth++;
        travel(root->right,depth);
        depth--;
    }
}

15、路径总和

要求:给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

bool traverse(node,count)
{
    if(node->left == null && node->right == null && count == 0)
		return true;
    if(node->left == null && node->right == null && count != 0)
        return false;
    if(node->left)
    {
    	count = count - left->val;
        if(traverse(node->left,count))
            return true;
        else
            count = count + left->val;
    }
    if(node->right)
    {
    	count = count - right->val;
        if(traverse(node->right,count))
            return true;
        else
            count = count + right->val;
    } 
    return false
}

16、构造二叉树

要求:给定中序遍历和后序遍历来构造一颗二叉树(这些里面没有重复数字)

如:中序:9 3 15 20 7 后序:9 15 7 20 3

**思路:**1.后续数组为0,空节点 2.后续数组最后一个元素为节点元素 3.寻找中续数组位置作为切割点 4.切割中续数组 5.切割后续数组 6.递归处理左区间和有区间

伪代码:

/*确定函数返回值的参量*/
TreeNode* traversel(inorder,postorder)//输入为一个中序数组和后序数组
{
    if(postorder.size == 0)
        return null;
    root_value = postorder[posterorder.size -1];
    TreeNode* root = new TreeNode(rootvalue);
    if(postorder.size == 0)
        return root;
    index = 0;//用于切割中序数组
    for(index = 0; index < inorder.size;index ++)
        if(indexorder == rootvalue)
            break;
    inorder_left = inorder[0:index-1];
    inorder_right = inorder[index+1:inorder.size-1];
    postorder = postorder[0:postorder.size-1];//舍弃末尾元素
    postorder_left = postorder[0:inorder_left.size-1];
    postorder_right = postorder[inorder_left.size:postorder.size-1];
    root->left = traversel(inorder_left,postorder_left);
    root->right = traversel(inorder_right,postorder_right);
}

17、最大二叉树

要求:给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

  • 二叉树的根是数组中的最大元素。
  • 左子树是通过数组中最大值左边部分构造出的最大二叉树。
  • 右子树是通过数组中最大值右边部分构造出的最大二叉树。

​ 通过给定的数组构建最大二叉树,并且输出这个树的根节点。

构造二叉树的题目必须用前序遍历

/*确定函数返回值的参量*/
TreeNode* traversel(nums)
{
    if(nums.size == 0)
        return null;
    int max_num = nums.max();
    TreeNode *root = new TreeNode(max_num);
    int index = nums.find(max_num);
    //切割左右数组
    nums_left = nums[0:index-1];
    nums_right = nums[index+1;nums.size-1];
    root->left = traversel(nums_left);
    root->right = traversel(nums_right);
	return root;
}

18、合并二叉树

要求:

​ 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

​ 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

二叉树_第1张图片

伪代码:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
        if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1
        // 修改了t1的数值和结构
        t1->val += t2->val;                             // 中
        t1->left = mergeTrees(t1->left, t2->left);      // 左
        t1->right = mergeTrees(t1->right, t2->right);   // 右
        return t1;
    }
}

19、搜索二叉树

**要求:**给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

二叉树_第2张图片

迭代法伪代码:

TreeNode *search(TreeNode *root,int val)
{
    if(root == null || root->val = val)
        return root;
    if(val > root->val)
        return search(root->right,val);
    if(val < root->val)
        return search(root->left,val);
}

迭代法伪代码:

while(root != null)
{
   if(val < root->val)
       root = root->left;
    else if(val > root->val)
        root = root->right;
    else
        return root;
}

20、验证二叉搜索树

要求:验证一个二叉树是否为一个搜索二叉树

伪代码:

class Solution {
private:
    vector<int> vec;
    void traversal(TreeNode* root) 
    {
        if (root == NULL) return;
        traversal(root->left);
        vec.push_back(root->val); // 将二叉搜索树转换为有序数组
        traversal(root->right);
    }
public:
    bool isValidBST(TreeNode* root) 
    {
        vec.clear(); // 不加这句在leetcode上也可以过,但最好加上
        traversal(root);
        for (int i = 1; i < vec.size(); i++) 
        {
            // 注意要小于等于,搜索树里不能有相同元素
            if (vec[i] <= vec[i - 1]) return false;
        }
        return true;
    }
};

使用类似迭代的伪代码:

class Solution {
public:
    long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值
    bool isValidBST(TreeNode* root) 
    {
        if (root == NULL) return true;

        bool left = isValidBST(root->left);
        // 中序遍历,验证遍历的元素是不是从小到大
        if (maxVal < root->val) maxVal = root->val;
        else return false;
        bool right = isValidBST(root->right);

        return left && right;
    }
};

21、二叉树最小的绝对值差

要求:给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。

方法1:使用中序遍历将搜索二叉树化为一个有序数组,然后再求结果。

方法2:使用迭代法

class Solution
{
    private:
    int result = INT_MAX;
    TreeNode* pre = NULL:
    void traverse(TreeNode *node)
    {
        if(cur == null)
            return
        traverse(node->left);
        if(pre != null)
        {
            result = min(result,cur->val-pre->val);
        }
        pre = cur;
        traverse(node->right);
    }
}

22、二叉搜索树中的众数

要求:找到二叉搜索树中出现次数最多的数字。

伪代码:

class Solution
{
    private:
    int max_count = 0;
	int count = 0;
    TreeNode *pre = null;
    vector<result> result;
    void searchBST(TreeNode *cur)
    {
        if(cur == null)
        {
            count = 1;
            return;
        }
        search(cur->left);
        else if(pre->val == cur->val)
        {
            count++;
        }    
        else
        {
        	count = 1;    
        }
        pre = cur;
        if(count == max_count)
        {
            max_count = count;
        	result.push_back(cur->val);
        }
        else if(count > max_count)
        {
            max_count = count;
            result.clear();
            result.push_back(cur->val);
        }
        search(cur->right);
    }
    public:
    vector<int> findMode(TreeNode *root)
    {
        count = 0;
        max_count = 0;
        TreeNode *pre = null;
        result.clear();
        searchBST(root);
        return result;
    }
}

23、寻找二叉树最近的公共祖先

要求:找到两个二叉树节点的公共祖先(没有重复数字)

思路:如果找到一个节点,发现其左子树存在节点p,右子树存在节点q,那么就说该节点是公共祖先。

伪代码:

TreeNode* lowesrCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
{
    if(root == p || root == q ||root == null)
    {
        return root;
    }
    TreeNode *left = lowsrCommonAncestor(root->left,p,q);
    TreeRight *right = lowsrCommonAncestor(root->right,p,q);
    if(left != null && right != null)
        return root;
    if(left != null && right == null)
        return left;
    if(left == null && right != null)
        return right;
    else
        return null;
}

你可能感兴趣的:(代码随想录,c语言,c++算法刷题笔记,c++,算法)