2023年3月24日
首先对前几天鸽的题做一个解决
# 二叉搜索树的最近公共祖先 235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)
这道题简单来说就是寻找二叉树中任意两个节点的最近父节点,并且节点本身也可以充当父节点来看。有了这个思路,就显而易见的说明要使用递归这个方法。
第一步确认递归的参数和返回值,因为要寻找节点,所以返回值肯定是TreeNode*,函数参数就是传入的两个节点,以及一个cur节点。
第二步确认递归的出口,即当我找到某个节点,此节点是null就说明访问“到头了”,就需要return了
第三步确定单层递归逻辑,由于此题的二叉树是二叉搜索树,所以可以根据传入节点的值来确定节点的位置。当我的cur的值比传入的两个节点都大的时候,说明要去左子树进行遍历;当我的cur值比传入的两个节点都小的时候,说明我要去右子树进行遍历;当我cur值在两个节点之间的时候,说明cur就是最近的父节点
class Solution {
private:
TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {
if (cur == NULL) return cur;
// 中
if (cur->val > p->val && cur->val > q->val) { // 左
TreeNode* left = traversal(cur->left, p, q);
if (left != NULL) {
return left;
}
}
if (cur->val < p->val && cur->val < q->val) { // 右
TreeNode* right = traversal(cur->right, p, q);
if (right != NULL) {
return right;
}
}
return cur;
}
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return traversal(root, p, q);
}
};
# 二叉搜索树中的插入操作 701. 二叉搜索树中的插入操作 - 力扣(LeetCode)
由于这道题的本质就是往BST中插入值,所以这里不再解释,在后面的总结中会有我所学的完整的关于BST的所有操作,这里只给出结果
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == NULL) {
TreeNode* node = new TreeNode(val);
return node;
}
if (root->val > val) root->left = insertIntoBST(root->left, val);
if (root->val < val) root->right = insertIntoBST(root->right, val);
return root;
}
};
# 删除二叉搜索树中的节点
这里同上
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == nullptr) return root; // 第一种情况:没找到删除的节点,遍历到空节点直接返回了
if (root->val == key) {
// 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
if (root->left == nullptr && root->right == nullptr) {
///! 内存释放
delete root;
return nullptr;
}
// 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点
else if (root->left == nullptr) {
auto retNode = root->right;
///! 内存释放
delete root;
return retNode;
}
// 第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
else if (root->right == nullptr) {
auto retNode = root->left;
///! 内存释放
delete root;
return retNode;
}
// 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置
// 并返回删除节点右孩子为新的根节点。
else {
TreeNode* cur = root->right; // 找右子树最左面的节点
while(cur->left != nullptr) {
cur = cur->left;
}
cur->left = root->left; // 把要删除的节点(root)左子树放在cur的左孩子的位置
TreeNode* tmp = root; // 把root节点保存一下,下面来删除
root = root->right; // 返回旧root的右孩子作为新root
delete tmp; // 释放节点内存(这里不写也可以,但C++最好手动释放一下吧)
return root;
}
}
if (root->val > key) root->left = deleteNode(root->left, key);
if (root->val < key) root->right = deleteNode(root->right, key);
return root;
}
};
# 修剪二叉搜索树 669. 修剪二叉搜索树 - 力扣(LeetCode)
这道就是给定一个范围,让你把BST中的所有值都变成范围之内的。
那就很明显需要用递归了。当我的值小于给定的低边界的时候,我就去遍历右子树,然后再用右节点“接住”修剪过后的右子树;同理,当我的值大于给定的高边界的时候,我就去遍历左子树,然后再用左节点“接住”修剪过后的左子树。
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (root == nullptr ) return nullptr;
if (root->val < low) {
TreeNode* right = trimBST(root->right, low, high); // 寻找符合区间[low, high]的节点
return right;
}
if (root->val > high) {
TreeNode* left = trimBST(root->left, low, high); // 寻找符合区间[low, high]的节点
return left;
}
root->left = trimBST(root->left, low, high); // root->left接入符合条件的左孩子
root->right = trimBST(root->right, low, high); // root->right接入符合条件的右孩子
return root;
}
};
# 将有序数组转换为二叉搜索树 108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
这道题就是用中序遍历和后序遍历构造二叉树的翻版,就是将给定的数组进行拆分,由于数组是递增的,我直接选定中间的数作为根节点,然后再分别递归左右两部分,最后将有序数组转换为二叉树
class Solution {
private:
TreeNode* traversal(vector<int>& nums, int left, int right) {
if (left > right) return nullptr;
int mid = left + ((right - left) / 2);
TreeNode* root = new TreeNode(nums[mid]);
root->left = traversal(nums, left, mid - 1);
root->right = traversal(nums, mid + 1, right);
return root;
}
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
TreeNode* root = traversal(nums, 0, nums.size() - 1);
return root;
}
};
# 把二叉搜索树转换为累加树 538. 把二叉搜索树转换为累加树 - 力扣(LeetCode)
这道题简单来说就是把中序遍历的结果从后往前进行递增求值。再仔细观察一下,并不是严格中序遍历,而是反着的中序遍历,即右中左。
class Solution {
private:
int pre = 0; // 记录前一个节点的数值
void traversal(TreeNode* cur) { // 右中左遍历
if (cur == NULL) return;
traversal(cur->right);
cur->val += pre;
pre = cur->val;
traversal(cur->left);
}
public:
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
};
# 总结
1.二叉搜索树
二叉搜索树(Binary Search Tree)是一棵二叉树,可能为空;一棵非空的二叉搜索树满足以下特征:
①每个元素有一个关键字,并且任意两个元素的关键字都不同;因此,所有的关键字都是唯一的
②在根节点的左子树中,元素的关键字(如果有的话)都小于根节点的关键字
③在根节点的右子树中,元素的关键字(如果有的话)都大于根节点的关键字
④根节点的左、右子树也都是二叉搜索树
特殊的,如果允许关键字有重复,即用小于等于、大于等于替代原定义中的小于和大于,这样的二叉树称为有重复值的二叉搜索树(Binary Search Tree with Duplicates)
2.索引二叉搜索树
索引二叉搜索树(Indexed Binary Search Tree)源于普通的二叉搜索树,只是在每个节点中添加一个leftSize域。这个域的值是该节点左子树的元素个数。
3.ADT
template<class K, class E>
class dictionary
{
public:
virtual ~dictionary() {}
virtual bool empty() const = 0;
// return true iff dictionary is empty
virtual int size() const = 0;
// return number of pairs in dictionary
virtual pair<const K, E>* find(const K&) const = 0;
// return pointer to matching pair
virtual void erase(const K&) = 0;
// remove matching pair
virtual void insert(const pair<const K, E>&) = 0;
// insert a (key, value) pair into the dictionary
};
template<class K, class E>
class bsTree : public dictionary<K,E>
{
public:
virtual void ascend() = 0;
// output in ascending order of key
};
template<class K, class E>
class indexedBSTree : public bsTree<K,E>
{
public:
virtual pair<const K, E>* get(int) const = 0;
// return pointer to pair with given index
virtual void delete(int) = 0;
// delete pair with given index
};
4.binarySearchTree
template<class E>
class linkedBinaryTree : public binaryTree<binaryTreeNode<E> >
{
public:
linkedBinaryTree() {root = NULL; treeSize = 0;}
~linkedBinaryTree(){erase();};
bool empty() const {return treeSize == 0;}
int size() const {return treeSize;}
E* rootElement() const;
void makeTree(const E& element,
linkedBinaryTree<E>&, linkedBinaryTree<E>&);
linkedBinaryTree<E>& removeLeftSubtree();
linkedBinaryTree<E>& removeRightSubtree();
void preOrder(void(*theVisit)(binaryTreeNode<E>*))
{visit = theVisit; preOrder(root);}
void inOrder(void(*theVisit)(binaryTreeNode<E>*))
{visit = theVisit; inOrder(root);}
void postOrder(void(*theVisit)(binaryTreeNode<E>*))
{visit = theVisit; postOrder(root);}
void levelOrder(void(*)(binaryTreeNode<E> *));
void preOrderOutput() {preOrder(output); cout << endl;}
void inOrderOutput() {inOrder(output); cout << endl;}
void postOrderOutput() {postOrder(output); cout << endl;}
void levelOrderOutput() {levelOrder(output); cout << endl;}
void erase()
{
postOrder(dispose);
root = NULL;
treeSize = 0;
}
int height() const {return height(root);}
protected:
binaryTreeNode<E> *root; // pointer to root
int treeSize; // number of nodes in tree
static void (*visit)(binaryTreeNode<E>*); // visit function
static int count; // used to count nodes in a subtree
static void preOrder(binaryTreeNode<E> *t);
static void inOrder(binaryTreeNode<E> *t);
static void postOrder(binaryTreeNode<E> *t);
static void countNodes(binaryTreeNode<E> *t)
{
visit = addToCount;
count = 0;
preOrder(t);
}
static void dispose(binaryTreeNode<E> *t) {delete t;}
static void output(binaryTreeNode<E> *t)
{cout << t->element << ' ';}
static void addToCount(binaryTreeNode<E> *t)
{count++;}
static int height(binaryTreeNode<E> *t);
};
class binarySearchTree : public bsTree<K,E>,
public linkedBinaryTree<pair<const K, E> >
{
public:
// methods of dictionary
bool empty() const {return treeSize == 0;}
int size() const {return treeSize;}
pair<const K, E>* find(const K& theKey) const;
void insert(const pair<const K, E>& thePair);
void erase(const K& theKey);
// additional method of bsTree
void ascend() {inOrderOutput();}
};
template<class K, class E>
pair<const K, E>* binarySearchTree<K,E>::find(const K& theKey) const
{// Return pointer to matching pair.
// Return NULL if no matching pair.
// p starts at the root and moves through
// the tree looking for an element with key theKey
binaryTreeNode<pair<const K, E> > *p = root;
while (p != NULL)
// examine p->element
if (theKey < p->element.first)
p = p->leftChild;
else
if (theKey > p->element.first)
p = p->rightChild;
else // found matching pair
return &p->element;
// no matching pair
return NULL;
}
template<class K, class E>
void binarySearchTree<K,E>::insert(const pair<const K, E>& thePair)
{// Insert thePair into the tree. Overwrite existing
// pair, if any, with same key.
// find place to insert
binaryTreeNode<pair<const K, E> > *p = root,
*pp = NULL;
while (p != NULL)
{// examine p->element
pp = p;
// move p to a child
if (thePair.first < p->element.first)
p = p->leftChild;
else
if (thePair.first > p->element.first)
p = p->rightChild;
else
{// replace old value
p->element.second = thePair.second;
return;
}
}
// get a node for thePair and attach to pp
binaryTreeNode<pair<const K, E> > *newNode
= new binaryTreeNode<pair<const K, E> > (thePair);
if (root != NULL) // the tree is not empty
if (thePair.first < pp->element.first)
pp->leftChild = newNode;
else
pp->rightChild = newNode;
else
root = newNode; // insertion into empty tree
treeSize++;
}
template<class K, class E>
void binarySearchTree<K,E>::erase(const K& theKey)
{// Delete the pair, if any, whose key equals theKey.
// search for node with key theKey
binaryTreeNode<pair<const K, E> > *p = root,
*pp = NULL;
while (p != NULL && p->element.first != theKey)
{// move to a child of p
pp = p;
if (theKey < p->element.first)
p = p->leftChild;
else
p = p->rightChild;
}
if (p == NULL)
return; // no pair with key theKey
// restructure tree
// handle case when p has two children
if (p->leftChild != NULL && p->rightChild != NULL)
{// two children
// convert to zero or one child case
// find largest element in left subtree of p
binaryTreeNode<pair<const K, E> > *s = p->leftChild,
*ps = p; // parent of s
while (s->rightChild != NULL)
{// move to larger element
ps = s;
s = s->rightChild;
}
// move largest from s to p, can't do a simple move
// p->element = s->element as key is const
binaryTreeNode<pair<const K, E> > *q =
new binaryTreeNode<pair<const K, E> >
(s->element, p->leftChild, p->rightChild);
if (pp == NULL)
root = q;
else if (p == pp->leftChild)
pp->leftChild = q;
else
pp->rightChild = q;
if (ps == p) pp = q;
else pp = ps;
delete p;
p = s;
}
// p has at most one child
// save child pointer in c
binaryTreeNode<pair<const K, E> > *c;
if (p->leftChild != NULL)
c = p->leftChild;
else
c = p->rightChild;
// delete p
if (p == root)
root = c;
else
{// is p left or right child of pp?
if (p == pp->leftChild)
pp->leftChild = c;
else pp->rightChild = c;
}
treeSize--;
delete p;
}
// overload << for pair
template <class K, class E>
ostream& operator<<(ostream& out, const pair<K, E>& x)
{out << x.first << ' ' << x.second; return out;}
5.代码随想录中的总结
# 心得
二叉树章节自我感觉是初步入门了,希望能将这60天坚持下来,加油!