Tips: 忙着改毕业论文,忙着完成导师布置的任务,只能自己偷偷的在晚上的时候刷题- -
最近主要做了一下二叉顺序树的相关题目,题目总体难度一般。
LintCode 661 Convert BST to Greater Tree
虽然是Easy难度的题,但还是很有新意的:D,稍微思考了一下,树相关的题目一定是和递归紧紧联系在一起的,所以这道题就是从最右的叶子节点开始遍历,按照右子树、根节点、左子树这样的方式遍历,同时开了一个sum变量,统计遍历到某个节点时的值。
int sum = 0;
TreeNode* convertBST(TreeNode* root) {
// Write your code here
if (NULL == root) return NULL;
root->right = convertBST(root->right);
root->val += sum;
sum = root->val;
root->left = convertBST(root->left);
return root;
}
LintCode 85 Insert Node in a Binary Search Tree
往二元顺序树里插入一个值为val的节点。递归的方式就不说了,这里Challenge里提到能否以非递归的形式实现呢?奈何愚笨,想了半天都没做对。问题主要是出在 最后要返回这个二叉树的根节点。
先看我的错误版本:思路是这样的,首先把最初的root存起来,然后循环去寻找该插入的点,直到root == NULL。然后在该地方新建一个节点,最后返回初始的根节点。看起来美滋滋,可惜错啦!
TreeNode* insertNode(TreeNode* root, TreeNode* node) {
// write your code here
TreeNode *tmp = root;
while (root){
if (node->val < root->val) root = root->left;
else root = root->right;
}
root = new TreeNode(node->val);
return tmp;
}
这种方法的问题就在于没有无法保存连接关系。比如往节点A的左子树插入一节点,如果A节点的左子树为空,A = A->left,然后在 new TreeNode()在此新建一个节点,是无法让 A->left == this new TreeNode。而解决这一问题的办法就是用A->left = new TreeNode()来代替。这样就可以保证连接关系。
TreeNode* insertNode(TreeNode* root, TreeNode* node) {
// write your code here
if (NULL == node) return root;
TreeNode *ret = new TreeNode(node->val);
if (NULL == root) return ret;
TreeNode *tmp = root;
while (tmp){
if (node->val < tmp->val){
if (NULL == tmp->left){
tmp->left = node;
return root;
}
tmp = tmp->left;
}else{
if (NULL == tmp->right){
tmp->right = ret;
return root;
}
tmp = tmp->right;
}
}
}
LintCode 11 Search Range in Binary Search Tree
返回二叉树内节点值在k1到k2之间的所有值。二叉顺序树内的一个重要的性质就是 中序遍历是 递增序列。题目要求要返回满足要求的递增序列,所以只需要中序遍历一次二元查找树即可。当然还可以加一些遍历条件,让其减少递归的次数。
vector<int> ans;
void dfs(TreeNode *root, int k1, int k2)
{
if (NULL == root) return;
dfs(root->left, k1, k2);
if (root->val >= k1 && root->val <= k2) ans.push_back(root->val);
dfs(root->right, k1, k2);
}
vector<int> searchRange(TreeNode* root, int k1, int k2) {
// write your code here
ans.clear();
dfs(root, k1, k2);
//sort(ans.begin(), ans.end());
return ans;
}
LintCode 95 Validate Binary Search Tree
判断一棵给定的二叉树是不是二元查找树。利用性质:二元查找树的中序遍历是递增数列(充要条件)。中序遍历一次给定树,然后判断该vector的后一个元素是不是永远>前一个元素。这里说一个细节:顺序容器的size函数返回的是一个unsigned int,即如果遍历该vector时写成这样for (int i = 0; i < v.size()-1; i++)是有问题的,即当v.size()==0时,系统计算v.size()-1 = -1 => 由于unsigned int 是非负的,所以会把-1转成max(long long)好像是42亿多。。所以推荐写成for (int i =1; i < v.size(); i++)这样的形式。
vector<int> ans;
void inorder(TreeNode *root)
{
if (NULL == root) return;
inorder(root->left);
ans.push_back(root->val);
inorder(root->right);
}
bool isValidBST(TreeNode *root) {
// write your code here
//二元查找树的中序遍历是一个递增数列
ans.clear();
inorder(root);
for (int i = 1; i < ans.size(); i++){
if (ans[i] <= ans[i-1]) return false;
}
return true;
}
LintCode 86 Binary Search Tree Iteration
Challenge:Extra memory usage O(h), h is the height of the tree.
Super Star: Extra memory usage O(1).
正常思路:用一个vector和一个int指针分别来存储中序遍历的值和当前所指的值。但是这种方法的空间复杂度为O(n),n是节点个数。题目要求是O(h),也就是O(logn),所以这个肯定是不对的。
后来上网搜了一下,发现要用堆栈可以满足O(logn),也是是考察非递归形式的中序遍历。这种迭代的写法很多面试会考到,是bugfree.
stack<TreeNode*> s;
BSTIterator(TreeNode *root) {
// write your code here
while (root){
s.push(root);
root = root->left;
}
}
//@return: True if there has next node, or false
bool hasNext() {
// write your code here
return s.size() > 0;
}
//@return: return next node
TreeNode* next() {
// write your code here
TreeNode *tmp = s.top();
s.pop();
TreeNode* res = tmp;
tmp = tmp->right;
while (tmp){
s.push(tmp);
tmp = tmp->left;
}
return res;
}
LintCode 87 Remove Node in Binary Tree
删除二叉查找树中的某一个值为val的节点,先递归的find,然后找到之后删除。删除主要要考虑这么几种情况:
1.如果要删除的节点是叶子节点,那么直接删除。
2.如果是有单个孩子的节点,那么把他的孩子节点放到该位置上即可。
3.如果是双孩子节点,那么需要把 他左孩子的最大节点或者是右孩子的最小节点 放到该位置上,然后删掉那么 左孩子的最大节点或者最大节点(因为这些节点肯定是 孩子节点或者单孩子节点)。
int findMaxInLeft(TreeNode* root){
if (!root->right) return root->val;
else return findMaxInLeft(root->right);
}
TreeNode* removeNode(TreeNode* root, int value) {
// write your code here
if (NULL == root) return NULL;
if (value > root->val){
root->right = removeNode(root->right, value);
}else if (value < root->val){
root->left = removeNode(root->left, value);
}else{
//find the node needed to be removed.
if (!root->left && !root->right){
//leaf node
//free(root);// or root = NULL;
root = NULL;
}else if (root->left && !root->right){
TreeNode *q = root;
root = root->left;
free(q);
}else if (!root->left && root->right){
TreeNode *q = root;
root = root->right;
free(q);
}else{
int val = findMaxInLeft(root->left);
root = removeNode(root, val);
root->val = val;
}
}
return root;
}
总结:重点掌握 二元查找树的非递归插值方法、非递归的三种遍历方法。当然二元查找树是很简单的一部分,由此扩展,至少要掌握 avl树的插入节点与删除操作,以及常见的B-tree,B+tree,R-tree的基本概念和应用。