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; }};
我的可以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; }};
- leetcode之二叉树类之二叉树深度系列-----104/111/110/108/109 二叉树最大/最小深度/AVL树的判断和由有序序列生成(牵扯分治相关,OJ105/106)
- 二叉树最大深度和最小深度
- 二叉树最大深度和最小深度
- LeetCode系列之二叉树最大深度求解问题 C++
- 二叉树最大深度最小深度之微妙区别
- 二叉树的最大深度 & 二叉树的最小深度
- 二叉树的最大深度 & 二叉树的最小深度
- 二叉树最大 最小深度
- 二叉树 最大最小深度
- 二叉树的最大深度和最小深度
- 二叉树的最大深度和最小深度实现
- 二叉树深度(最大和最小)
- leetcode 二叉树最小深度
- 二叉树的最大最小深度
- LeetCode—二叉树的最大最小深度
- 求二叉树的最大深度、最小深度、反转二叉树及判断是否为平衡二叉树
- LeetCode 二叉树的最小深度
- leetcode 二叉树的最小深度
- win10上为CLion配置OpenCV
- 目标
- 集群安装规范之linux命令
- z-sort
- C++11中内联函数(inline)
- leetcode之二叉树类之二叉树深度系列-----104/111/110/108/109 二叉树最大/最小深度/AVL树的判断和由有序序列生成(牵扯分治相关,OJ105/106)
- Android 线程池的使用
- js中的函数分析2
- Mixture of Gaussian(高斯混合原型聚类)
- 网关
- 1006. 换个格式输出整数 (15)
- Maven入门
- lex & yacc
- 解决spring mvc No mapping found for HTTP request with URI错误的三种方法