算法训练Day22&23&二叉树总结

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.代码随想录中的总结

        代码随想录 (programmercarl.com)

 # 心得

        二叉树章节自我感觉是初步入门了,希望能将这60天坚持下来,加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
叉树是一种常见的数据结构,在递归算法中也有很多应用。创建二叉树的递归算法可以分为前序遍历和中序遍历两种方式。 前序遍历方式下,我们按照&ldquo;根-左-右&rdquo;的顺序来构建二叉树。具体步骤如下: 1. 如果输入的节点值为null,则返回空节点。 2. 创建一个新的节点,将其值设置为当前的节点值。 3. 递归调用函数,将左子树的根节点设置为当前节点的左子节点。 4. 递归调用函数,将右子树的根节点设置为当前节点的右子节点。 5. 返回当前节点。 中序遍历方式下,我们按照&ldquo;左-根-右&rdquo;的顺序来构建二叉树。具体步骤如下: 1. 如果输入的节点值为null,则返回空节点。 2. 递归调用函数,将左子树的根节点设置为当前节点的左子节点。 3. 创建一个新的节点,将其值设置为当前的节点值。 4. 递归调用函数,将右子树的根节点设置为当前节点的右子节点。 5. 返回当前节点。 下面是一个示例代码,以前序遍历方式为例: ``` class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { return build(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1); } private TreeNode build(int[] preorder, int[] inorder, int preStart, int preEnd, int inStart, int inEnd) { if (preStart &gt; preEnd || inStart &gt; inEnd) { return null; } int rootVal = preorder[preStart]; int index = 0; for (int i = inStart; i &lt;= inEnd; i++) { if (inorder[i] == rootVal) { index = i; break; } } TreeNode root = new TreeNode(rootVal); root.left = build(preorder, inorder, preStart + 1, preStart + index - inStart, inStart, index - 1); root.right = build(preorder, inorder, preStart + index - inStart + 1, preEnd, index + 1, inEnd); return root; } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值