C++二叉树进阶(个人笔记)


1.二叉搜索树

1.1二叉搜索树该概念

二叉搜索树又称二叉排序树,具有以下性质:

  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

1.2二叉搜索树操作

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

在这里插入图片描述

1.2.1二叉树的查找
  1. 从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
  2. 最多查找高度次,走到到空,还没找到,这个值不存在。
1.2.2二叉搜索树的插入

插入的具体过程如下:

  1. 树为空,则直接新增节点,赋值给root指针
  2. 树不空,按二叉搜索树性质查找插入位置,插入新节点
1.2.3二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:

  1. 要删除的结点无孩子结点
  2. 要删除的结点只有左孩子结点
  3. 要删除的结点只有右孩子结点
  4. 要删除的结点有左、右孩子结点

将第一情况与第二或者第三种情况结合,就有了以下三种解决的办法:

  1. 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
  2. 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
  3. 情况d:在它的右子树中寻找中序下的第一个结点(其实就是右子树找最小节点),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除
    在这里插入图片描述

1.3二叉搜索树的实现

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

template<class K>
struct BSTreeNode
{
	typedef BSTreeNode<K> Node;
	BSTreeNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
	K _key;
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			Node* newnode = new Node(key);
			_root = newnode;
			return true;
		}
		else
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
			cur = new Node(key);
			if (parent->_key > key)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			return true;
		}
	}

	bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
		return false;
	}

	bool Erase(const K& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else if (cur->_left != nullptr && cur->_right != nullptr)
				{
					//找右子树的最左节点
					//这里进行了重新给parent赋值,是解决删除节点是根节点的情况时,parent如果还是nullptr会出现空指针的解引用
					Node* parent = cur;
					Node* subLeft = cur->_right;
					while (subLeft->_left)
					{
						parent = subLeft;
						subLeft = subLeft->_left;
					}
					swap(cur->_key, subLeft->_key);
					if (subLeft == parent->_left)
					{
						parent->_left = subLeft->_right;
					}
					else if (subLeft == parent->_right)
					{
						parent->_right = subLeft->_right;
					}
					delete subLeft;
				}
				return true;
			}
		}
		return false;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool FinR(const K& key)
	{
		return _FinR(_root,key);
	}

	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}

	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}

private:
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_key < key)
		{
			_EraseR(root->_right, key);
		}
		else if (root->_key > key)
		{
			_EraseR(root->_left, key);
		}
		else
		{
			//删除
			if (root->_left == nullptr)
			{
				Node* del = root;
				root = root->_right;
				delete del;
				return true;
			}
			else if (root->_right == nullptr)
			{
				Node* del = root;
				root = root->_left;
				delete del;
				return true;
			}
			else
			{
				Node* subLeft = root->_right;
				while (subLeft->_left)
				{
					subLeft = subLeft->_left;
				}
				swap(subLeft->_key, root->_key);
				//转换成在子树去递归删除
				return _EraseR(root->_right, key);
			}
		}
	}

	bool _InsertR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			root = new Node(key);
			return true;
		}

		if (root->_key < key)
		{
			_InsertR(root->_right, key);
		}
		else if (root->_key > key)
		{
			_InsertR(root->_left, key);
		}
		else
		{
			return false;
		}
	}

	bool _FindR(Node* root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_key < key)
		{
			return _FindR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _FindR(root->_left, key);
		}
		else
		{
			return true;
		}
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

	Node* _root = nullptr;
};

1.4二叉搜索树的应用

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

比如:给一个单词hello,判断该单词是否拼写正确:

  1. 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。(这里是Find函数的返回值设置为bool类型)

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。

  1. 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word,chinese>就构成一种键值对;
  2. 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。
template<class K, class V>
struct BSTreeNode
{
	BSTreeNode<K, V>* _left;
	BSTreeNode<K, V>* _right;
	K _key;
	V _value;

	BSTreeNode(const K& key, const V& value)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
		, _value(value)
	{}
};

template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	bool Insert(const K& key, const V& value)
	{
		if (_root == nullptr)
		{
			_root = new Node(key, value);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;

			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(key, value);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		return true;
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}

	bool Erase(const K& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				if (cur->_left == nullptr)
				{//左为空
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
				}
				else if (cur->_right == nullptr)
				{//右为空
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
				}
				else
				{//左右都不为空

					// 右树的最小节点(最左节点)
					Node* parent = cur;
					Node* subLeft = cur->_right;
					while (subLeft->_left)
					{
						parent = subLeft;
						subLeft = subLeft->_left;
					}

					swap(cur->_key, subLeft->_key);

					if (subLeft == parent->_left)
						parent->_left = subLeft->_right;
					else
						parent->_right = subLeft->_right;
				}

				return true;
			}
		}

		return false;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_key << ":" << root->_value << endl;
		_InOrder(root->_right);
	}
private:
	Node* _root = nullptr;
};

1.5二叉搜索树的笔试题

根据二叉树创建字符串
在这里插入图片描述

class Solution {
public:
    string tree2str(TreeNode* root)
    {
        string s;
        if(root==nullptr)
        {
            return s;
        }
        
        s+=to_string(root->val);
        
        if(root->left||root->right)
        {
            s+='(';
            s+=tree2str(root->left);
            s+=')';
        }
        
        if(root->right)
        {
            s+='(';
            s+=tree2str(root->right);
            s+=')';
        }
        return s;
    }
};

二叉树的层序遍历

在这里插入图片描述

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root)
    {
        vector<vector<int>> vv;
        queue<TreeNode*> q;
        int leveSize=0;
        if(root==nullptr)
        {
            return vv;
        }
        q.push(root);
        leveSize=1;

        while(!q.empty())
        {
            vector<int> v;
            while(leveSize--)
            {
                TreeNode* front=q.front();
                v.push_back(front->val);
                q.pop();
                if(front->left)
                {
                    q.push(front->left);
                }
                if(front->right)
                {
                    q.push(front->right);
                }
            }
            leveSize=q.size();
            vv.push_back(v);
        }
        return vv;
    }
};

从前序与中序遍历序列构造二叉树
在这里插入图片描述

class Solution {
public:
    TreeNode* _build(vector<int>& preorder,vector<int>& inorder,int& prei,int inbegin,int inend)
    {
        if(inbegin>inend)
        {
            return nullptr;
        }
        int rooti=inbegin;
        while(rooti<=inend)
        {
            if(preorder[prei]==inorder[rooti])
            {
                break;
            }
            rooti++;
        }

        //前序确定根
        TreeNode* root=new TreeNode(preorder[prei++]);

        //中序分割左右子树
        root->left=_build(preorder,inorder,prei,inbegin,rooti-1);
        root->right=_build(preorder,inorder,prei,rooti+1,inend);
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
    {
        int prei=0;
        int inbegin=0;
        int inend=inorder.size()-1;
        TreeNode* root=_build(preorder,inorder,prei,inbegin,inend);
        return root;
    }
};

从中序与后序遍历序列构造二叉树
在这里插入图片描述


class Solution {
public:
    TreeNode* _build(vector<int>& inorder,vector<int>& postorder,int& posti,int inbegin,int inend)
    {
        if(inbegin>inend)
        {
            return nullptr;
        }
        int rooti=inbegin;
        while(rooti<=inend)
        {
            if(postorder[posti]==inorder[rooti])
            {
                break;
            }
            rooti++;
        }
        //后序确定根
        TreeNode* root=new TreeNode(postorder[posti--]);
        
        //中序分割左右区间
        root->right=_build(inorder,postorder,posti,rooti+1,inend);
        root->left=_build(inorder,postorder,posti,inbegin,rooti-1);
        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
    {
        int posti=postorder.size()-1;
        int inbegin=0;
        int inend=inorder.size()-1;
        TreeNode*root=_build(inorder,postorder,posti,inbegin,postorder.size()-1);
        return root;
    }
};

二叉树的层序遍历 II
在这里插入图片描述

//没有什么特别的,就是层序遍历之后逆置一下就好了
class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root)
    {
        queue<TreeNode*> q;
        int levelSize=0;
        if(root)
        {
            q.push(root);
            levelSize=1;
        }
        vector<vector<int>> vv;
        while(!q.empty())
        {
            vector<int> v;
            while(levelSize--)
            {
                TreeNode* front=q.front();
                v.push_back(front->val);
                q.pop();
                if(front->left)
                {
                    q.push(front->left);
                }
                if(front->right)
                {
                    q.push(front->right);
                }
            }
            levelSize=q.size();
            vv.push_back(v);
        }
        vector<vector<int>> Rvv(vv.rbegin(),vv.rend());
        return Rvv;
    }
};

二叉树前序遍历
在这里插入图片描述

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root)
    {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur=root;
        while(cur||!s.empty())
        {
            //左路节点入栈
            while(cur)
            {
                v.push_back(cur->val);
                s.push(cur);
                cur=cur->left;
            }
            TreeNode* top=s.top();
            s.pop();
            cur=top->right;
        }
        return v;
    }
};

二叉树中序遍历
在这里插入图片描述

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root)
    {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur=root;
        while(cur||!s.empty())
        {
            while(cur)
            {
                s.push(cur);
                cur=cur->left;
            }
            
            TreeNode* top=s.top();
            s.pop();
            v.push_back(top->val);
            cur=top->right;
        }
        return v;
    }
};

二叉树后序遍历
在这里插入图片描述

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root)
    {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur=root;
        TreeNode* prev=nullptr;
        while(cur||!s.empty())
        {
            while(cur)
            {
                s.push(cur);
                cur=cur->left;
            }
            TreeNode* top=s.top();
            //top节点的右为空或者上一个访问节点等于它的右孩子,那么说明(空)不用访问或者(不为空)右子树已经访问过了
            if(top->right==nullptr||top->right==prev)
            {
                s.pop();
                v.push_back(top->val);
                prev=top;
            }
            else
            {
                cur=top->right;
            }
        }
        return v;
    }
};

二叉树的最近公共祖先
在这里插入图片描述

//写法一:
class Solution {
public:
    bool isTree(TreeNode* root,TreeNode* x)
    {
        if(root==nullptr)
        {
            return false;
        }
        if(root==x)
        {
            return true;
        }
        return isTree(root->left,x)||isTree(root->right,x);
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
    {
        if(root==p||root==q)
        {
            return root;
        }
        bool pleft=isTree(root->left,p);
        bool pright=!pleft;
        bool qleft=isTree(root->left,q);
        bool qright=!qleft;
        if((pleft && qright) || (pright&&qleft))
        {
            return root;
        }
        if(pleft&&qleft)
        {
            return lowestCommonAncestor(root->left,p,q);
        }
        if(pright&&qright)
        {
            return lowestCommonAncestor(root->right,p,q);
        }
        return nullptr;
    }
};

//写法二:
class Solution {
public:
    bool FindPath(TreeNode* root,TreeNode* x,stack<TreeNode*>& path)
    {
        if(root==nullptr)
        {
            return false;
        }
        path.push(root);
        if(root==x)
        {
            return true;
        }
        if(FindPath(root->left,x,path))
        {
            return true;
        }
        if(FindPath(root->right,x,path))
        {
            return true;
        }
        path.pop();
        return false;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
    {
        stack<TreeNode*> pPath;
        stack<TreeNode*> qPath;
        FindPath(root,p,pPath);
        FindPath(root,q,qPath);
        while(pPath.size()!=qPath.size())
        {
            if(pPath.size()>qPath.size())
            {
                pPath.pop();
            }
            else
            {
                qPath.pop();
            }
        }
        while(pPath.top()!=qPath.top())
        {
            qPath.pop();
            pPath.pop();
        }
        return pPath.top();
    }
};

二叉搜索树与双向链表
在这里插入图片描述

class Solution {
public:
    void InOrderConvert(TreeNode* cur,TreeNode*& prev)
	{
		if(cur==nullptr)
		{
			return;
		}
		InOrderConvert(cur->left, prev);
		cur->left=prev;
		if(prev)
		{
			prev->right=cur;
		}
		prev=cur;
		InOrderConvert(cur->right,prev);
	}

    TreeNode* Convert(TreeNode* pRootOfTree)
	{
        TreeNode* cur=pRootOfTree;
		TreeNode* prev=nullptr;
		InOrderConvert(cur,prev);
		TreeNode* head=pRootOfTree;
		while(head&&head->left)
		{
			head=head->left;
		}
		return head;
    }
};
  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

索隆43

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值