Authur Whywait 做一块努力吸收知识的海绵
想看博主的其他所有leetcode卡片学习笔记链接?传送门点这儿
二叉搜索树是二叉树的一种特殊形式。 二叉搜索树具有以下性质:每个节点中的值必须大于(或等于)其左侧子树中的任何值,但小于(或等于)其右侧子树中的任何值。
二叉搜索树的定义
二叉搜索树
(BST)是二叉树的一种特殊表示形式,它满足如下特性:
- 每个节点中的值必须
大于
(或等于)存储在其左侧子树中的任何值。 - 每个节点中的值必须
小于
(或等于)存储在其右子树中的任何值。
下图是一个二叉搜索树的例子。
像普通的二叉树一样,我们可以按照前序、中序和后序来遍历一个二叉搜索树。
但是值得注意的是,对于二叉搜索树,我们可以通过中序遍历得到一个递增的有序序列。因此,中序遍历是二叉搜索树中最常用的遍历方法。
验证二叉搜索树🚩
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
分析 1
- 由前文所述二叉搜索树在中序遍历下的特殊现象,脑子蹦出来的第一个方法(反正这是我的第一个想法)就应该是中序遍历观察其序列。
至于如何用各种姿势遍历一个树,详情请点击传送门。
Tips
值得注意的是,这里的二叉搜索树有其特殊性:每个节点中的值必须大于或等于存储在其左侧子树中的任何值;每个节点中的值必须小于或等于存储在其右子树中的任何值。
这是和我们前面介绍中略有区别的一点(也不知道你在看前文时有没有注意到这点)
这是我们需要注意的细节,不然作为特例判断时容易出错。
代码实现以及执行结果
利用中序遍历二叉搜索树会得到一个单调递增序列的性质,我另开了一个辅助数组,存储使用递归方法中序遍历得到的序列。
如果出现flag为false(即出现一个小于序列末尾的数想加入序列,我们就让标志flag为false),我们就层层返回。不再执行后面的语句。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int array[10000] = {0}, count=0;;
bool flag = 1;
void isBST(struct TreeNode* root){
if(!root || !flag) return;
isBST(root->left);
if(count>0 && array[count-1]>=root->val) flag = false;
else array[count++] = root->val;
isBST(root->right);
return;
}
bool isValidBST(struct TreeNode* root){
count = 0; flag = 1; //初始化
isBST(root);
return flag;
}
分析 2
- 上面的方法需要有一个一维数组来存储遍历到的序列(当然也可以不用全局变量,不过需要动态申请空间,然后传递的时候需要多传递几个参数,比较麻烦一些)。显然,应该有更好的方法来处理这个问题。(虽然题目中没有时间复杂度和空间复杂度的要求,但是谁都想整个最优的不是吗?)
- 思索上面的方法,是有使用一个数组来存储中序遍历到的序列,但是真正涉及到的比较操作,其实只有序列末尾的数和待加入序列的数这两者的比较,所以我们只要用一个变量把最后一个数存储下来不就好了吗?
- 需要注意的是,作为中序遍历到的第一个元素,我们需要的操作是将其赋值给temp(temp作为中序遍历到的上一个元素),而非比较判断是非。这也是temp没有初始化的原因。
- 下面是修改后的代码:
bool flag = true, isfirst = true;
int temp;
void isBST(struct TreeNode* root){
if(!root || !flag) return;
isBST(root->left);
if(!isfirst && temp>=root->val) flag = false;
else{
temp = root->val;
isfirst = false;
}
isBST(root->right);
return;
}
bool isValidBST(struct TreeNode* root){
isfirst = true, flag = true; //以防万一,此处初始化
isBST(root);
return flag;
}
总结
最后查看官方题解,发现思路大同小异。bingo~
二叉搜索树迭代器🚩
实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。
调用 next() 将返回二叉搜索树中的下一个最小的数。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Your BSTIterator struct will be instantiated and called as such:
* BSTIterator* obj = bSTIteratorCreate(root);
* int param_1 = bSTIteratorNext(obj);
* bool param_2 = bSTIteratorHasNext(obj);
* bSTIteratorFree(obj);
*/
分析
回想我们之前在前面学习的中序遍历二叉树,无非就是将二叉树中序遍历一遍,然后存储到一个数组里面,相较于此题,就是每次next一下,就将头指针后移一个元素。
然而,我们存储那个序列,应该用什么数据结构呢?
用一维数组?也曾考虑过,但是题目直接丢给你一个结构体,意思是我们需要存储到一个结构体里头(当然结构体里面的具体内容由我们自己定义)。其实也不是不能实现,我们先把它存储到数组里头,然后再把数组搞成一个链表。(至于链表如何实现迭代器的功能?我认为可以这样:每次输出头节点的val,然后删除头节点;至于判断就是判断头节点是否为NULL;至于释放就直接顺着链表逐个释放就好啦~)
看到这里,你心里肯定有个小人这么在嘀咕:烦不烦?转来转去的?
想想时间复杂度和空间复杂度,就知道此举措必定是一个憨憨行为。
那用队列,让他们按照中序遍历的顺序入队。至于next操作就是输出头指针,然后头指针后移···
如果用栈,如果按照中序遍历的顺序入了栈,我们如何才可以对栈底的元素进行操作?(这明显和栈的性质不符嘛)
如此一路分析下来,似乎我们只能用队列了,两个指针,动来动去好麻烦··· 可是也没别的办法了····
BUT!!!
如果我们用栈,不用中序遍历,而是反着来中序遍历呢?
栈顶元素不就是我们需要的了么?
不要668,不要998,一个栈顶指针解决一切烦恼!
代码实现以及执行结果
typedef struct Stack{
int val[10000];
int top;
} BSTIterator;
void reverseInorder(struct TreeNode* root, BSTIterator* obj){
if(!root) return;
reverseInorder(root->right, obj);
obj->val[obj->top++] = root->val;
reverseInorder(root->left, obj);
}
BSTIterator* bSTIteratorCreate(struct TreeNode* root) {
if(!root) return NULL;
BSTIterator* obj = (BSTIterator *)malloc(sizeof(BSTIterator));
if(!obj) return NULL;
obj->top = 0;
reverseInorder(root, obj);
return obj;
}
/** @return the next smallest number */
int bSTIteratorNext(BSTIterator* obj) {
return obj->val[(obj->top--)-1];
}
/** @return whether we have a next smallest number */
bool bSTIteratorHasNext(BSTIterator* obj) {
return (obj)&&(obj->top>0);
}
void bSTIteratorFree(BSTIterator* obj) {
if(obj && obj->top) free(obj->val);
free(obj);
}
报错
身在代码中,报错少不了。
下面是在写此题代码中遇到的报错,请点击传送门
都看到这里了,确定不点个👍再走? (~ ̄(OO) ̄)ブ (疯狂暗示.jpg)