算法--树

一、知识点介绍

树的基本概念

在这里插入图片描述
子树:树是一个有限集合,子树则是该集合的子集。就像套娃一样,一棵树下面还包含着其子树。

比如,树T1 的子树为 树T2、T3、T4,树T2的子树为 T5、T6. 上图中还有许多子树没有标记出来。

结点(Node):一个结点包括一个数据元素和若干指向其子树分支。

比如,在树T1 中,结点A 包括一个数据元素A 和 三个指向其子树的分支。上图中共有 17 个结点。

根结点(Root):一颗树只有一个树根,这是常识。在数据结构中,“树根”即根节点。

比如,结点A 是树 T1 的根结点;结点C 是树T1 的子结点,是树 T3 的根结点。

度(Degree):一个结点拥有的子树数。

比如,结点A 的度为 3,结点G 的度为 3,结点H 的度为 1.

叶子(Leaf)/ 终端结点:度为 0 的结点被称为叶子结点,很形象吧。

比如,对于树 T1来说,结点F、I、K、L、M、N、O、P、Q 均为叶子。

分支结点 / 非终端结点:和叶子结点相对,即度不为 0 的结点。

内部结点:顾名思义,在树内部的结点,即不是根结点和叶子结点的结点。

孩子(Child)、双亲(Parent)、兄弟(Sibling)、堂兄弟、祖先、子孙这些概念和族谱上的相同。

比如,对于结点B 来说:结点A 是其双亲结点,结点E、F 是其孩子结点,结点C、D 是其兄弟结点,结点K 是其子孙结点。

层次(Level):从根结点开始,根为第一次,根的孩子为第二层,依次往下。

比如,结点K 在树 T1 中的层次为 4.

深度(Depth)/ 高度:指树的最大层次。

比如,树 T1 的深度为 4.

有序树:如果结点的各子树从左到右是有次序的、不能颠倒,则为有序树,否则为无序树。对于有序树的孩子来说,最左边的孩子称为第一个孩子,最右边的孩子称为最后一个孩子。

比如,如果树T1是一个有序树,则其根结点的第一个孩子为结点B,最后一个孩子为结点D.

二、练习题目

        (1) 1361. 验证二叉树
        (2) 1367. 二叉树中的列表

三、算法思路

1. 验证二叉树

        (1) 算法思路:1. 找出树的root;2. 判断树是否合理
        (2) 如何找出树的root呢?root既不再左子树中,也不在右子树中而且有且只有一个,所以可以用一个哈希表deg存放,左子树和右子树所有元素的次数。遍历这个哈希表,如果元素个数为0说明就是root。如果有两个或以上的root,说明树不合理;
        (3) 树不合理的情况:1. 1~n-1没有都出现一遍;2. 有多个root(第二点已经说过了);3. 1~n-1中的数字出现了两次及以上; 4. 每个结点的度超过2.
        (4) 1. 用一个哈希表hash存放每个元素出现的次数,如果哈希表的值不为零的话,说明树合理;2. 这个在第二点已经解决了; 3. 哈希表deg存放每个结点度的大小,deg[i]大于1的话,就返回false;4. cnt代表当前的度的大小,如果cnt大于1的话,返回false

class Solution {
    int hash[10010];
    void dfs(vector<int>& leftChild, vector<int>& rightChild, int x) {
        hash[x] = 1;
        if(leftChild[x] != -1) {
            dfs(leftChild, rightChild, leftChild[x]);
        }
        if(rightChild[x] != -1) {
            dfs(leftChild, rightChild, rightChild[x]);
        }
    }
public:
    bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
        int deg[10010];
        memset(hash, 0, sizeof(hash));
        memset(deg, 0, sizeof(deg));
        //bool ans = true;
        for(int i = 0; i < leftChild.size(); ++i) {
            if(leftChild[i] != -1) {
                ++deg[leftChild[i]];
            }
            if(rightChild[i] != -1) {
                ++deg[rightChild[i]];
            }
        }
        int cnt = 0;
        int root = -1;
        for(int i = 0; i < n; ++i) {
            if(deg[i] == 0) {
                ++cnt;
                root = i;
                if(cnt > 1) return false;
            }
            if(deg[i] > 1) return false;
        }
        if(cnt == 0) return false;
        dfs(leftChild, rightChild, root);
        for(int i = 0 ; i < n; ++i) {
            if(!hash[i]) return false;
        }
        return true;

    }
};

2. 二叉树中的列表

        (1) 先递归找到二叉树中和链表第一个元素相等的元素;如果没找到的话直接返回false
        (2)如果找到的话,在通过向下遍历找到是否有连续的值(也就是找一遍左子树再找一遍右子树和链表的值相等;如果没有就返回false
        (3) 处理一下特殊情况:如果链表有值,树没值返回false;如果链表没值,树有值返回true。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool check(TreeNode* root, ListNode* head) {
        if(root == NULL) {
            return (head == NULL);
        }
        if(head == NULL) return true;
        if(root->val != head->val) {
            return false;
        }
        return check(root->left, head->next) || check(root->right, head->next);
    }

    bool isSubPath(ListNode* head, TreeNode* root) {
        if(check(root, head)) {
            return true;
        }
        if(root == NULL) return false;
        return isSubPath(head, root->left) || isSubPath(head, root->right);
    }
};

四、 总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值