摘要:也就是说,有个节点的平衡二叉搜索树,它的高度是。以搜索操作为例,如果二叉搜索树的高度为,则时间复杂度为。二叉搜索树的高度的确很重要。树集合,中的或者中的,是由高度平衡的二叉搜索树实现的。
二叉搜索树(BST)是二叉树的一种特殊表示形式,它满足如下特性:
每个节点中的值必须大于(或等于)存储在其左侧子树中的任何值。
每个节点中的值必须小于(或等于)存储在其右子树中的任何值。
法一:利用二叉树的性质,根结点的值大于左子树上的点,小于右子树上的点
const long long MAX=2e31;const long long MIN=-MAX;class Solution {public: bool isValidBST(TreeNode* root) { bool ans=search(root,MIN,MAX); return ans; } bool search(TreeNode *root,long long l,long long r){ if(root==NULL)return true; if(root->val<=l||root->val>=r){ return false; } return search(root->left,l,root->val)&&search(root->right,root->val,r); }};
法二:二叉搜索树中序遍历的结果一定是单调递增的
class Solution {public: bool isValidBST(TreeNode* root) { stack<TreeNode*>st; TreeNode *cur=root; long long num=-2*1e10; while(cur||!st.empty()){ while(cur){ st.push(cur); cur=cur->left; } cur=st.top(); st.pop(); if(num>=cur->val){ return false; } num=cur->val; cur=cur->right; } return true; } };
class BSTIterator {public: TreeNode *cur; stack<TreeNode*>st; BSTIterator(TreeNode* root) { cur=root; } int next() { while(cur){ st.push(cur); cur=cur->left; } cur=st.top(); st.pop(); int ans=cur->val; cur=cur->right; return ans; } bool hasNext() { return cur||!st.empty(); }};
class Solution {public: TreeNode *ans=NULL; TreeNode* searchBST(TreeNode* root, int val) { if(root==NULL)return root; if(root->val==val)return root; if(root->val>val)ans=searchBST(root->left,val); if(root->val<val)ans=searchBST(root->right,val); return ans; }};
class Solution {public: TreeNode* insertIntoBST(TreeNode* root, int val) { if(root==NULL){ root=new TreeNode(val); return root; } if(root->val>val)root->left=insertIntoBST(root->left,val); if(root->val<val)root->right=insertIntoBST(root->right,val); return root; }};
有许多不同的删除节点的方法,为了使整体操作变化最小,用一个合适的子节点来替换要删除的目标节点。根据其子节点的个数,我们需考虑以下三种情况:
Successor 代表的是中序遍历序列的下一个节点。即比当前节点大的最小节点,简称后继节点。 先取当前节点的右节点,然后一直取该节点的左节点,直到左节点为空,则最后指向的节点为后继节点。
TreeNode * successor(TreeNode *root){ root=root->right; while(root->left){ root=root->left; } return root;}
Predecessor 代表的是中序遍历序列的前一个节点。即比当前节点小的最大节点,简称前驱节点。先取当前节点的左节点,然后取该节点的右节点,直到右节点为空,则最后指向的节点为前驱节点。
TreeNode *predecessor(TreeNode *root){ root=root->left; while(root->right){ root=root->right; } return root;}
class Solution {public: TreeNode* deleteNode(TreeNode* root, int key){ if(root==NULL)return NULL; if(root->val==key){ if(!root->left&&!root->right){ return NULL; }else if(root->right){ TreeNode *node=new TreeNode(successor(root)->val); node->left=root->left; node->right=root->right; node->right=deleteNode(node->right,node->val); return node; }else if(!root->right&&root->left){ TreeNode *node=new TreeNode(precessor(root)->val); node->left=root->left; node->right=root->right; node->left=deleteNode(node->left,node->val); return node; } } TreeNode *l=deleteNode(root->left,key); TreeNode *r=deleteNode(root->right,key); root->left=l; root->right=r; return root; } TreeNode *successor(TreeNode *root){ root=root->right; while(root->left){ root=root->left; } return root; } TreeNode *precessor(TreeNode*root){ root=root->left; while(root->right){ root=root->right; } return root; }};
二叉搜索树的有优点是,即便在最坏的情况下,也允许你在O(h)的时间复杂度内执行所有的搜索、插入、删除操作。
法一:自底向上递归,适用于一般二叉树
class Solution {public: TreeNode *ans; TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(root==NULL)return NULL; search(root,p,q); return ans; } bool search(TreeNode *root,TreeNode*p,TreeNode *q){ if(root==NULL)return false; bool pr=search(root->left,p,q); bool qr=search(root->right,p,q); if(pr&&qr||(root==p||root==q)&&(pr||qr)){ ans=root; return true; } return root==q||root==p||qr||pr; }};
法二:
利用二叉搜索树的性质,我们可以快速地找出树中的某个节点以及从根节点到该节点的路径
如果当前节点的值大于 p 和 q 的值,说明 p 和 q 应该在当前节点的左子树,因此将当前节点移动到它的左子节点;
如果当前节点的值小于 p 和 q 的值,说明 p 和 q 应该在当前节点的右子树,因此将当前节点移动到它的右子节点;
如果当前节点的值不满足上述两条要求,那么说明当前节点就是分岔点。此时,p 和 q 要么在当前节点的不同的子树中,要么其中一个就是当前节点。
class Solution {public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(root==NULL)return NULL; TreeNode *ans=root; while(ans){ if(ans->val>p->val&&ans->val>q->val){ ans=ans->left; }else if(ans->val<p->val&&ans->val<q->val){ ans=ans->right; }else{ break; } } return ans; } };
高度平衡的二叉搜索树
一个高度平衡的二叉搜索树(平衡二叉搜索树)是在插入和删除任何节点之后,可以自动保持其高度最小。也就是说,有 N 个节点的平衡二叉搜索树,它的高度是 logN 。并且,每个节点的两个子树的高度不会相差超过 1。
二叉树及其相关操作, 包括搜索、插入、删除。当分析这些操作的时间复杂度时,我们需要注意的是树的高度是十分重要的考量因素。以搜索操作为例,如果二叉搜索树的高度为 h ,则时间复杂度为 O(h) 。二叉搜索树的高度的确很重要。
所以,我们来讨论一下树的节点总数 N 和高度 h 之间的关系。对于一个平衡二叉搜索树, 我们已经在前文中提过,
但一个普通的二叉搜索树,在最坏的情况下,它可以退化成一个链。
因此,具有 N 个节点的二叉搜索树的高度在 logN 到 N 区间变化。也就是说,搜索操作的时间复杂度可以从 logN 变化到 N 。这是一个巨大的性能差异。
所以说,高度平衡的二叉搜索树对提高性能起着重要作用。
高度平衡的二叉搜索树在实际中被广泛使用,因为它可以在 O(logN) 时间复杂度内执行所有搜索、插入和删除操作。
常见的的高度平衡二叉树:
红黑树
AVL树
伸展树
树堆
平衡二叉搜索树的概念经常运用在 Set 和 Map 中。Set 和 Map 的原理相似。
通常,有两种最广泛使用的集合**:散列集合(Hash Set)和 树集合(Tree Set)**。
树集合,Java 中的 Treeset 或者 C++ 中的 set ,是由高度平衡的二叉搜索树实现的。因此,搜索、插入和删除的时间复杂度都是 O(logN) 。
散列集合,Java 中的 HashSet 或者 C++ 中的 unordered_set ,是由哈希实现的,但是平衡二叉搜索树也起到了至关重要的作用。当存在具有相同哈希键的元素过多时,将花费 O(N) 时间复杂度来查找特定元素,其中N是具有相同哈希键的元素的数量。 通常情况下,使用高度平衡的二叉搜索树将把时间复杂度从 O(N) 改善到 O(logN) 。
哈希集和树集之间的本质区别在于树集中的键是有序的。
class Solution {public: bool ans=true; bool isBalanced(TreeNode* root) { if(root==NULL)return ans; search(root); return ans; } int search(TreeNode *root){ if(root==NULL)return 0; int l=search(root->left); int r=search(root->right); if(abs(l-r)>1){ ans=false; return -1; } return l>r?l+1:r+1; }};
class Solution {public: TreeNode* sortedArrayToBST(vector<int>& nums) { return helper(nums, 0, nums.size() - 1); } TreeNode* helper(vector<int>& nums, int left, int right) { if (left > right) { return nullptr; } // 总是选择中间位置左边的数字作为根节点 int mid = (left + right) / 2; TreeNode* root = new TreeNode(nums[mid]); root->left = helper(nums, left, mid - 1); root->right = helper(nums, mid + 1, right); return root; }};
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/123351.html
文章目录 一、题目1、题目描述2、基础框架3、原题链接 二、解题报告1、思路分析2、时间复杂度3、代码详解 三、本题小知识四、加群须知 一、题目 1、题目描述 给你一棵二叉搜索树,请按 中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。 样例输入: [5,3,6,2,4,null,8,1,null,null,nu...
摘要:小鹿题目验证二叉搜索树给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征节点的左子树只包含小于当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。算法思路定义全局的变量,用来返回是否为二叉搜索树。 Time:2019/4/24Title: Vaildata Binary Search TreeDifficulty: MediumAuthor: 小鹿 ...
摘要:小鹿题目路径总和给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。说明叶子节点是指没有子节点的节点。 Time:2019/4/26Title: Path SumDifficulty: EasyAuthor: 小鹿 题目:Path Sum(路径总和) Given a binary tree and a sum, determin...
摘要:大家简单看一下纯递归的解法原题二叉搜索树的范围和解法题目描述给定二叉搜索树的根结点,返回值位于范围之间的所有结点的值的和。 【前言】 今天是力扣打卡第11天! 感谢铁汁们的陪伴,一起加油鸭!! 在第9天的时候写过这道题的递归解题方法,其实DFS使用的解题思想就是递归,所以大同小异啦...
摘要:小鹿题目二叉树中序遍历给定一个二叉树,返回它的中序遍历。通常递归的方法解决二叉树的遍历最方便不过,但是我还是喜欢增加点难度,用一般的迭代循环来实现。 Time:2019/4/25Title:Binary Tree Inorder TraversalDifficulty: MediumAuthor:小鹿 题目:Binary Tree Inorder Traversal(二叉树中序遍历...
阅读 2010·2023-04-26 00:16
阅读 3455·2021-11-15 11:38
阅读 3124·2019-08-30 12:50
阅读 3160·2019-08-29 13:59
阅读 736·2019-08-29 13:54
阅读 2464·2019-08-29 13:42
阅读 3284·2019-08-26 11:45
阅读 2168·2019-08-26 11:36