leetcode之二叉树类之二叉树深度系列-----104/111/110/108/109 二叉树最大/最小深度/AVL树的判断和由有序序列生成(牵扯分治相关,OJ105/106)

来源:互联网 发布:mac播放视频卡顿 编辑:程序博客网 时间:2024/06/03 18:22

二叉树的最大、最小深度,首先明确一点,不论最大还是最小,二叉树的深度都是基于leaf节点的,即二叉树的leaf节点的层数可以称之为深度,而求最大深度或最小深度,就是找深度最大是多少,最小是多少,前提必须是合法的深度。

同样是到leaf才见分晓的判断,走先序遍历,到达leaf就返回深度,而求最大或最小深度,就是返回左深度和右深度的max或min

利用极端例子思考,一个二叉树,root的左子树有很多层节点,右子树只有一个节点,那么最大深度是左子树到最深叶子的层数,最小深度就是2

如果root只有一侧有节点,如左或右某一侧为空,那么最小深度为2,最大深度依然需要计算

如果root没有任何子节点,那么最大深度和最小深度都是1


OJ104代码:

class Solution {public:    int helper (TreeNode *cur, int h) {        if (cur) {            if (!cur->left && !cur->right) {                return h;            } else {                if (cur->left && cur->right) {                    int l = helper(cur->left, h + 1);                    int r = helper(cur->right, h + 1);                    return std::max(l, r);                } else {                    if (cur->left) {                        return helper(cur->left, h + 1);                    }                    if (cur->right) {                        return helper(cur->right, h + 1);                    }                }            }        }    }        int maxDepth(TreeNode* root) {        if (!root) {            return 0;        } else if (!root->left && !root->right) {            return 1;        }                return helper(root, 1);    }};

OJ111代码:

class Solution {public:    int helper (TreeNode *cur, int h) {        if (cur) {            if (!cur->left && !cur->right) {                return h;            } else {                if (cur->left && cur->right) {                    int l = helper(cur->left, h + 1);                    int r = helper(cur->right, h + 1);                    return std::min(l, r);                } else {                    if (cur->left) {                        return helper(cur->left, h + 1);                    } else {                        return helper(cur->right, h + 1);                    }                }            }        } else {            return h - 1;        }    }        int minDepth(TreeNode* root) {        if (!root) {            return 0;        } else if (!root->left && !root->right) {            return 1;        }                return helper(root, 1);    }};

OJ110,判断一个搜索二叉树是否为AVL树,AVL树的定义是,任意一个节点的左子树和右子树的深度差别不能超过1,所以需要对每一个节点,求出左子树深度和右子树深度,典型需要后序遍历,然后判断是否出现不符合要求case

递归函数的返回值需要用于返回树的深度,所以可以传一个引用来保存不符合要求的时候。这是二叉树题的典型方式。


OJ110代码:

class Solution {public:    int helper (TreeNode *cur, int h, bool &res) {        if (cur) {            int left = helper(cur->left, h + 1, res);            int right = helper(cur->right, h + 1, res);                        if (left - right >= 2 || right - left >= 2) {                res = false;            }            return (left > right)?left:right;        } else {            return h - 1;        }    }        bool isBalanced(TreeNode* root) {        if (!root) {            return true;        }                bool res = true;        helper(root, 0, res);        return res;    }};

OJ108和OJ109都是给定一个有序序列,然后生成AVL树,方法都显然是以该序列的不断的二分,生成AVL树一层层的节点,差异是OJ108是数组,可以直接取索引定位mid节点及左右范围,而OJ109是链表,总需要通过快慢指针找mid节点;这两道题考察重点为分治;

注意有些题的题意与OJ108不同,但也是基于分治,如"由先序遍历序列和中序遍历序列,或由后序遍历序列和中序遍历序列,生成二叉树"(OJ105/106);而OJ109在考察AVL树原理之外,更多考察的是单链表的快慢指针找中点和边界处理细节,OJ109比较容易做错,在实战中可以作为一道比较恶心的题出现;


OJ108,给定有序数组生成AVL树,不断二分找mid节点生成每一层的,进而用左半部分生成左子树,右半部分生成右子树

OJ108代码:

class Solution {public:    void helper (vector<int> nums, TreeNode *&cur, int st, int ed) {        if (st == ed) {            cur = new TreeNode(nums[st]);            return;        } else if (st > ed) {            return;        }                int mid = (st + ed)/2;        cur = new TreeNode(nums[mid]);        helper(nums, cur->left, st, mid - 1);        helper(nums, cur->right, mid + 1, ed);    }        TreeNode* sortedArrayToBST(vector<int>& nums) {        TreeNode *root = nullptr;        if (nums.empty()) {            return root;        }                int st = 0, ed = nums.size() - 1;        helper(nums, root, st, ed);        return root;    }};


OJ109,和OJ108唯一区别是由有序数组变为有序链表,麻烦点在于找中点更麻烦

我的可以AC的方法是,用一个函数做链表的二分,同时将原先链表分裂为两个新链表,即mid左边节点如果存在的话,不再指向mid而是指向nullptr,这里要注意mid左边没有节点的时候,head其实是和mid是一个节点,如果不做判断mid和head是否相同就继续处理,会出现重复不停的生成左子树的节点

程序总体结构和OJ108是差不多的,唯一就是多了找中点部分


OJ109代码:

class Solution {public:    ListNode *GetMid (ListNode *head, ListNode *&right) {        if (!head || !head->next || !head->next->next) {            if (head->next) {                right = head->next;            }            return head;        }                ListNode *cur = head, *prev = head, *pprev = nullptr;        while (cur && cur->next && cur->next->next) {            pprev = prev;            prev = prev->next;            cur = cur->next->next;        }                if (pprev) {            pprev->next = nullptr;        }        right = prev->next;        prev->next = nullptr;        return prev;    }        void helper (ListNode *head, TreeNode *&cur) {        if (!head) {            return;        }                ListNode *right = nullptr;        ListNode *mid = GetMid(head, right);        if (mid) {            cur = new TreeNode(mid->val);            if (mid != head) {                helper(head, cur->left);            }            helper(right, cur->right);        }    }        TreeNode* sortedListToBST(ListNode* head) {        TreeNode *root = nullptr;        if (!head) {            return root;        }                helper(head, root);        return root;    }};

OJ105和OJ106,由先序+中序,或由后序+中序,生成对应的二叉树,这两个题考察点和OJ108/OJ109不同,它们更侧重二叉树的深度遍历,但解题办法很相似,在这一并记录一下;

核心思路是:

1、由中序遍历自己无法还原对应的二叉树,因为中序遍历反映不了左子树右子树的顺序,如中序遍历123,可以是

     1      \       2        \         3
但也可以是:

     1      \       3      /     2
到底是哪个,这就需要先序或后序遍历,比如对于第1个图,先序遍历是123,后序遍历是321,对于第2个图先序遍历是132,后序遍历是321

2、先序遍历或后序遍历怎么样和中序遍历一起生成对应的二叉树

方法:临场在纸上画就行,如随意画下图:

     3    / \   2   5  /   / \ 1   4   6
先序遍历是321546,中序遍历是123456,后序遍历是124653

同时看先序+中序和后序+中序,步骤:

1、对于先序+中序,第0个节点是root,对于后序+中序,最后一个节点是root

2、生成了root后,中序遍历中,root节点的左半部分就是左子树,右半部分是右子树,对于图中例子就是,12是左子树,456是右子树

      对于左子树12,先序中第0个节点是2,则2是左子树12的头节点,而在后序中最后一个节点是2,2是头节点

      对于右子树456,同理,4是头节点

如果已经看了前面OJ108/OJ109的解法,看到这里应该知道程序大概怎么写,可见这道题同样属于分治,具体的,不论是先序+中序,还是后序+中序,每次定位到头节点并生成当前节点后,定位先序或后序的左子树、右子树部分,具体来说就是定位左子树、右子树部分,在数组的首尾索引,不断继续递归生成节点,递归停止条件就是左子树、右子树部分为空


OJ105代码:

class Solution {public:    void helper (const vector<int> &preorder, const vector<int> &inorder, int prest, int preed, int midst, int mided, TreeNode *&cur) {        if (!(prest <= preed && midst <= mided && mided - midst == preed - prest && preed < preorder.size() && midst < inorder.size())) {            return;        }                cur = new TreeNode(preorder[prest]);        if (prest == preed && midst == mided) {            return;        }                int count = 0;        int mididx;        for (int i = midst; i <= mided; i++) {            if (preorder[prest] == inorder[i]) {                mididx = i;                break;            } else {                ++count;            }        }                helper(preorder, inorder, prest + 1, prest + count, midst, mididx - 1, cur->left);        helper(preorder, inorder, prest + 1 + count, preed, mididx + 1, mided, cur->right);    }        TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {        if (preorder.empty() || inorder.empty()) {            return nullptr;        }                TreeNode *root = nullptr;        helper(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1, root);        return root;    }};

OJ106代码:

class Solution {public:    void helper (const vector<int> &inorder, const vector<int> &postorder, int midst, int mided, int postst, int posted, TreeNode *&cur) {        if (!(midst <= mided && postst <= posted && posted - postst == mided - midst && mided < inorder.size() && postst >= 0)) {            return;        }                cur = new TreeNode(postorder[posted]);        if (midst == mided && postst == posted) {            return;        }                int count = 0, mididx;        for (int i = mided; i >= midst; i--) {            if (postorder[posted] == inorder[i]) {                mididx = i;                break;            } else {                ++count;            }        }                helper(inorder, postorder, midst, mididx - 1, postst, posted - count - 1, cur->left);        helper(inorder, postorder, mididx + 1, mided, posted - count, posted - 1, cur->right);    }        TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {        if (inorder.empty() || postorder.empty()) {            return nullptr;        }                TreeNode *root = nullptr;        helper(inorder, postorder, 0, inorder.size() - 1, 0, postorder.size() - 1, root);        return root;    }};


原创粉丝点击