二叉树c++(level up)

news2024/12/28 4:48:20

目录

1.搜索二叉树

1.1概念

1.2操作

1.2.1.查找

1.2.2插入

1.2.3遍历

1.2.4删除(重点)

1.3K模型/KV模型

 1.4性能分析

2.二叉树oj

1.创建字符串

2.层序遍历

3.二叉树的最近公共祖先

4.二叉搜索树与双向链表

5.前序中序构建二叉树

6.中序后续构造二叉树

7.二叉树前序遍历(迭代)

8.二叉树中序遍历(迭代)

9.二叉树后序遍历(迭代)


1.搜索二叉树

1.1概念

搜索二叉树又称二叉排序树,可以是空树,也可满足下列性质:

1.若左子树不为空,则左子树上所有节点的值都比根节点的值小

2.若右子树不为空,则右子树上所有节点的值都比根节点的值大

3.左右子树也都是搜索二叉树

1.2操作
 

1.2.1.查找

根据二叉搜索树的特性,只要不停地跟当前节点的值比较大小,查找值大,就往当前节点右子树继续走,小,就往当前节点左子树继续走。

直到遇到空(即该树不存在查找的值)或遇到查找的值。

具体代码为了不水文,就不单独摘出来了,待会放在最下面

1.2.2插入

根据二叉搜索树的特性,前期也是不停的比较,但这时候,是直到找到一个空节点即可。

申请新的节点再赋给该空节点指针,另外,在查找的过程,除了当前节点cur,还有个父节点parent,实时更新,在最后,将赋值了新的空间的原空节点指针作为parent的孩子节点。

具体代码同理看下面

1.2.3遍历

比较方便的中序,而二叉搜索树的中序遍历正好就是一个递增的遍历。

所以直接用中序即可。

1.2.4删除(重点)

删除要分析很多种情况。

1.删除的节点没有孩子。

2.删除的节点有1个孩子。

3.删除的节点有2个孩子。

第一种和第二种可以在实现的时候可以归类为一种。

针对第一种和第二种,我们采取的是托付。都是将当前要删除节点的一个孩子托付给父节点,第一种情况下,托付哪个孩子都一样,都是空。第二种情况,托付那唯一的孩子即可。

而第三种情况。

我们采取替换法。托付法也可以,但是情况会很复杂,要针对复杂的二叉搜索树做预设。

替换进来的对象是哪个呢?有2种,选一个即可

比如上图,我们要删除3,那么第一种就是在3的左子树中找最右边的节点(一直找当前节点的右子树,直到找到右子树为空的节点,比如上图的2.3),第二种,就是3的右子树中找最左边的节点(一直找当前节点的左子树,直到找到左子树为空的节点。如上图的4)。

简单点概括,就是左树的最大节点,右树的最小节点

替换的时候要注意,如果找到的节点不是叶子节点,需要把另一个孩子托付给父节点(因为找到的节点必有一个空节点,但有没有孩子不一定)。

具体代码看下面。

#pragma once

//搜索二叉树节点
template<class K>
struct BSTreeNode {
	typedef BSTreeNode<K> Node;
	BSTreeNode(const K&key) 
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
	Node* _left;//左孩子指针
	Node* _right;//右孩子指针
	K _key;//存的值
};
//搜索二叉树
template<class K>
class BSTree {
	typedef BSTreeNode<K> Node;
public:
	//强制生成默认构造,因为我们手动写了拷贝构造就不会自动生成默认构造了
	BSTree() = default;
	//拷贝构造函数
	BSTree(const BSTree<K>& t)
	{
		_root = Copy(t._root);
	}
	//赋值
	BSTree<K>& operator=(BSTree<K> t)
	{
		//利用传值拷贝,会生成一个新的节点值相同但地址不同的新搜索二叉树的特性
		//外面的二叉树会传值拷贝给t,t等于一个空间地址不同,但节点内,存的值跟外面的二叉树一模一样的新二叉树
		//这时候将两者_root交换,这样本二叉树就等于有了跟原先t一模一样的值和节点地址。
		//再利用局部变量在函数结束会自动销毁的特性,完全不需要理会形参t,其会自动销毁。
		swap(_root, t._root);
		return *this;
	}
	//插入-循坏
	bool Insert(const K& key)
	{
		if (!_root)//第一个值插入,直接放
		{
			_root = new Node(key);
			return  true;
		}
		//cur是用来搜索的指针,parent是cur的父节点指针
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)//cur不为空,就继续,直到找到了值或cur为空
		{
			if (cur->_key > key)//cur当前的存的值比我们插入的值大
			{
				parent = cur;//层层递进
				cur = cur->_left;
			}
			else if (cur->_key < key)//同理,右子树
			{
				parent = cur;//同理
				cur = cur->_right;
			}
			else//说明找到了,但我们这里是插入,而二叉搜索树根据定义,是各个值都是严格大于小于,所以是不能存在重复的
			//所以,这里遇到了已经存在情况,那就返回false,不执行插入
			{
				return false;
			}
		}
		//执行到这里,是说明cur已经是空指针了。
		cur = new Node(key);
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;//
	}
	//插入-递归
	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}
	//查找-循坏
	bool Find(const K& key)
	{
		//cur是用来搜索的指针
		Node* cur = _root;
		while (cur)//cur不为空,就继续,直到找到了值或cur为空
		{
			if (cur->_key > key)//cur当前的存的值比我们找的大,说明目标最多可能在cur的左子树
			{
				cur = cur->_left;
			}
			else if (cur->_key < key)//同理,右子树
			{
				cur = cur->_right;
			}
			else//说明找到了。
			{
				return true;
			}
		}
		return false;//如果是以为空条件退出循坏,那说明二叉树中没有我们指定的数,所以默认false
	}
	//查找-递归
	bool FindR(const K& key)
	{
		return _FindR(_root, key);
	}
	//中序-递归
	void InOrder()//注意,因为_root是private成员。不能在外面调用,所以写个子函数,在类里面调用即可
	{
		_InOrder(_root);
		cout << endl;
	}
	//删除-循坏
	bool Erase(const K&key) {
		if (_root == nullptr)return false;//检查树是否为空
		Node* partent = nullptr;//父节点
		Node* cur = _root;//当前节点
		while (cur)
		{
			//跟前面一样
			if (cur->_key > key)
			{
				partent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				partent = cur;
				cur = cur->_right;
			}
			else
			{
				//这里找到之后,就要开始判断了。
				//根据分析,第一种和第二种情况是一起的,因为叶子节点的孩子是两个空节点,不影响托付

				//左孩子为空或右孩子为空的情况,不管另一个孩子是不是空节点,都把另一个孩子托付给父节点
				if (cur->_left == nullptr)
				{
					//注意,可能出现cur正好就是整个树的根节点,这时候partent是空的
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						//托付的时候,要确定当前节点是父节点的左孩子还是右孩子,因为父节点可能是有2个孩子
						if (partent->_left == cur)
						{
							partent->_left = cur->_right;
						}
						else
						{
							partent->_right = cur->_right;
						}
					}
					delete cur;
					return true;
				}
				else if (cur->_right==nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (partent->_left == cur)
						{
							partent->_left = cur->_left;
						}
						else
						{
							partent->_right = cur->_left;
						}
					}
					delete cur;
					return true;
				}
				else
				{
				//左右孩子都不为空情况
				//替换法,本次写右树最小
					
					//右树最小的父节点,注意,要直接把cur赋给他
					//因为如果右树最小有可能正好就是cur的右孩子,如果给空的话,后面执行托付的
					//时候可能出现访问空指针的情况。
					Node* rightMinPartent = cur;
					Node* rightMin = cur->_right;
					while (rightMin->_left)
					{
						rightMinPartent = rightMin;
						rightMin = rightMin->_left;
					}
					cur->_key = rightMin->_key;//替换值
					//注意,右树最小节点可能是其父节点的左孩子也可能右孩子,托付的时候要判断先。
					if (rightMinPartent->_right == rightMin)
					{
						rightMinPartent->_right = rightMin->_right;
					}
					else
					{
						rightMinPartent->_left = rightMin->_right;
					}
					delete rightMin;
					return true;
				}
			}
		}
		return false;
	}
	//删除-递归
	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}
	//析构函数
	~BSTree() {
		Destroy(_root);
	}

private:
	//复制
	Node* Copy(Node* root)
	{
		if (root == nullptr)return nullptr;
		Node* newNode = new Node(root->_key);
		newNode->_left = Copy(root->_left);
		newNode->_right = Copy(root->_right);
		return newNode;
	}
	//销毁-递归
	void Destroy(Node* root) {
		if (root == nullptr)return;
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}
	//插入-递归-子
	bool _InsertR(Node*&root,const K& key)
		//注意,这里加了引用,是因为当我们递归到空指针的时候,除非加参数,否则找不到父节点
		//但是,加了引用,这个空指针是父节点的孩子指针的别名,这样直接赋值给root,就可以实现插入新节点
	{
		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 _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
		{
			Node* del = root;
			//依靠&,甚至不需要考虑root是父节点的哪个孩子
			//因为递归传递的是父节点的某个孩子指针,接受的是对应的别名。
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				//这里不能依靠&,因为&是针对递归不停的传参,从而达到目的
				//但是循环的时候,&的对象是不能改变的,只能改变引用对象的值。
				Node* rightMin = root->_right;
				while (rightMin->_left)
				{
					rightMin = rightMin->_left;
				}
				//这里取巧一手,交换两者的值,继续递归,因为删是根据值来删,
				//这样递归下去,最后删除会采用上面的逻辑,因为右子树最小节点的左孩子必是空指针。
				swap(root->_key, rightMin->_key);
				return _EraseR(root->_right, key);
			}
			delete del;
			return true;
		}
	}
	//查找-递归-子
	bool _FindR(Node* root, const K& key)
	{
		if (root == nullptr)return false;
		if (root->_key < key)
		{
			_FindR(root->_right, key);
		}
		else if (root->_key > key)
		{
			_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.3K模型/KV模型

上面的代码是K模型。

K模型:查找key是否存在搜索二叉树中,例如:小区车库,门禁

KV模型:根据key在搜索二叉树中查找value,例如:商城车库(跟小区车库有很大区别,因为小区车库除非保安手动开杠,否则基本只有小区的人才能进,逻辑很简单,是小区的人就开,不是就不开。但是商城不同,商城有些是收费的,这个时候车牌号是key,进入时间是value)、字典查询、高铁站进站刷身份证。

KV模型:

	//搜索二叉树节点
	template<class K,class V>
	struct BSTreeNode {
		typedef BSTreeNode<K,V> Node;
		BSTreeNode(const K& key,const V&value)
			:_key(key)
			, _left(nullptr)
			, _right(nullptr)
			,_value(value)
		{}
		Node* _left;//左孩子指针
		Node* _right;//右孩子指针
		K _key;//存的值
		V _value;
	};
	//搜索二叉树
	template<class K,class V>
	class BSTree {
	public:
		typedef BSTreeNode<K, V> Node;
		//插入-循坏
		bool Insert(const K& key,const V&value)
		{
			//value不会影响节点在搜索二叉树里面的位置,只需要记得new的时候加上即可
			if (!_root)//第一个值插入,直接放
			{
				_root = new Node(key,value);
				return  true;
			}
			//cur是用来搜索的指针,parent是cur的父节点指针
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)//cur不为空,就继续,直到找到了值或cur为空
			{
				if (cur->_key > key)//cur当前的存的值比我们插入的值大
				{
					parent = cur;//层层递进
					cur = cur->_left;
				}
				else if (cur->_key < key)//同理,右子树
				{
					parent = cur;//同理
					cur = cur->_right;
				}
				else//说明找到了,但我们这里是插入,而二叉搜索树根据定义,是各个值都是严格大于小于,所以是不能存在重复的
				//所以,这里遇到了已经存在情况,那就返回false,不执行插入
				{
					return false;
				}
			}
			//执行到这里,是说明cur已经是空指针了。
			cur = new Node(key,value);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;//
		}
		//查找-循坏
		Node* Find(const K& key)
		{
			//find在这里需要做到,给key,返回相应节点
			//cur是用来搜索的指针
			Node* cur = _root;
			while (cur)//cur不为空,就继续,直到找到了值或cur为空
			{
				if (cur->_key > key)//cur当前的存的值比我们找的大,说明目标最多可能在cur的左子树
				{
					cur = cur->_left;
				}
				else if (cur->_key < key)//同理,右子树
				{
					cur = cur->_right;
				}
				else//说明找到了。
				{
					return cur;
				}
			}
			return nullptr;//如果是以为空条件退出循坏,那说明二叉树中没有我们指定的数,所以默认false
		}
		//中序-递归
		void InOrder()//注意,因为_root是private成员。不能在外面调用,所以写个子函数,在类里面调用即可
		{
			_InOrder(_root);
			cout << endl;
		}
		//删除-循坏
		bool Erase(const K& key) {
		//没有变化,value不影响节点在搜索二叉树里的位置
			if (_root == nullptr)return false;//检查树是否为空
			Node* partent = nullptr;//父节点
			Node* cur = _root;//当前节点
			while (cur)
			{
				//跟前面一样
				if (cur->_key > key)
				{
					partent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					partent = cur;
					cur = cur->_right;
				}
				else
				{
					//这里找到之后,就要开始判断了。
					//根据分析,第一种和第二种情况是一起的,因为叶子节点的孩子是两个空节点,不影响托付

					//左孩子为空或右孩子为空的情况,不管另一个孩子是不是空节点,都把另一个孩子托付给父节点
					if (cur->_left == nullptr)
					{
						//注意,可能出现cur正好就是整个树的根节点,这时候partent是空的
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							//托付的时候,要确定当前节点是父节点的左孩子还是右孩子,因为父节点可能是有2个孩子
							if (partent->_left == cur)
							{
								partent->_left = cur->_right;
							}
							else
							{
								partent->_right = cur->_right;
							}
						}
						delete cur;
						return true;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (partent->_left == cur)
							{
								partent->_left = cur->_left;
							}
							else
							{
								partent->_right = cur->_left;
							}
						}
						delete cur;
						return true;
					}
					else
					{
						//左右孩子都不为空情况
						//替换法,本次写右树最小

							//右树最小的父节点,注意,要直接把cur赋给他
							//因为如果右树最小有可能正好就是cur的右孩子,如果给空的话,后面执行托付的
							//时候可能出现访问空指针的情况。
						Node* rightMinPartent = cur;
						Node* rightMin = cur->_right;
						while (rightMin->_left)
						{
							rightMinPartent = rightMin;
							rightMin = rightMin->_left;
						}
						cur->_key = rightMin->_key;//替换值
						//注意,右树最小节点可能是其父节点的左孩子也可能右孩子,托付的时候要判断先。
						if (rightMinPartent->_right == rightMin)
						{
							rightMinPartent->_right = rightMin->_right;
						}
						else
						{
							rightMinPartent->_left = rightMin->_right;
						}
						delete rightMin;
						return true;
					}
				}
			}
			return false;
		}

	private:
		//中序-递归
		void _InOrder(Node* root) {
			if (root == nullptr)return;
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}
		//根节点
		Node* _root = nullptr;
	};

 1.4性能分析

对于搜索二叉树来说,不管是插入还是删除,效率主要取决于查找的速度。

而对于查找,最优的情况可以接近于log2 N,如左图,但最坏情况下,O(N)如右图。

2.二叉树oj

1.创建字符串

 606. 根据二叉树创建字符串 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    // bool dfs(TreeNode*root)
    // {
    //     if(root==nullptr)return false;
    //     res+=(to_string(root->val));
    //     res+='(';
    //     if(dfs(root->left))res+=')';
    //     else res.pop_back();
    //     if(root->left==nullptr&&root->right!=nullptr)res+="()";
    //     res+='(';
    //     if(dfs(root->right))res+=')';
    //     else res.pop_back();
    //     return true;
    // }
    string tree2str(TreeNode* root) {
        // dfs(root);
        // return res;
        if(root==nullptr)return "";
        res+=to_string(root->val);
        if(root->left||root->right)
        {
            res+='(';
            tree2str(root->left);
            res+=')';
        }
        if(root->right)
        {
            res+='(';
            tree2str(root->right);
            res+=')';
        }
        return res;
    }
    string res="";

//被我注释的代码,在速度上是更快点的,因为每次下面的代码每次都要返回string类型,需要消耗时间创建string返回对象,而我注释的代码返回一个bool类型,是比较节省的,下面的代码是条理比较清晰的
};

2.层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    struct kp{
        kp(TreeNode*r=NULL,int l=-1)
        {
            rt=r;
            level=l;
        }

        TreeNode* rt;
        int level;    
    };
//存对象,rt是节点指点,level是第几层
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<kp>mp;
        mp.emplace(root,0);
//root默认0层
        vector<vector<int>>res(0);
        if(root==nullptr)return res;
        vector<int>lp(0);
        while(!mp.empty())
        {
            auto tmp=mp.front();
            TreeNode*a=tmp.rt;
            int h=tmp.level;
            mp.pop();
            if(a!=nullptr)
            {
                
                mp.emplace(a->left,h+1);
                mp.emplace(a->right,h+1);
                if(h>res.size())
                {
                    res.push_back(lp);
                    lp.resize(0);
                }
                lp.push_back(a->val);
            }
        }
        res.push_back(lp);
        return res;
    }
    
};

107. 二叉树的层序遍历 II - 力扣(LeetCode)

这题看似很难,自下而上,但我们换个角度,自上而下,再逆置结果,返回即可。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
        struct kp{
        kp(TreeNode*r=NULL,int l=-1)
        {
            rt=r;
            level=l;
        }

        TreeNode* rt;
        int level;    
    };
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<kp>mp;
        mp.emplace(root,0);
        vector<vector<int>>res(0);
        if(root==nullptr)return res;
        vector<int>lp(0);
        while(!mp.empty())
        {
            auto tmp=mp.front();
            TreeNode*a=tmp.rt;
            int h=tmp.level;
            mp.pop();
            if(a!=nullptr)
            {
                
                mp.emplace(a->left,h+1);
                mp.emplace(a->right,h+1);
                if(h>res.size())
                {
                    res.push_back(lp);
                    lp.resize(0);
                }
                lp.push_back(a->val);
            }
        }
        res.push_back(lp);
        return res;
    }
    
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>>res=levelOrder(root);
        reverse(res.begin(),res.end());
        return res;
    }
};

3.二叉树的最近公共祖先

236. 二叉树的最近公共祖先 - 力扣(LeetCode)

O(N^2),核心是如果两个节点分别在左子树和右子树,自己即为最近公共祖先。

基本是压线过的,稍微为难点就嘎了

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool intree(TreeNode*root,TreeNode*x)
    {
        if(root==NULL)return false;
        return root==x||intree(root->left,x)||intree(root->right,x);
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL)return NULL;
        bool pl,pr,ql,qr;
        if(root==p||root==q)return root;
        pl=intree(root->left,p);
        pr=!pl;
        ql=intree(root->left,q);
        qr=!ql;
        if((pl&&qr)||(pr&&ql))
        {
            return root;
        }
        else if(pl&&ql)
        {
            return lowestCommonAncestor(root->left,p,q);
        }
        else{
            return lowestCommonAncestor(root->right,p,q);
        }
    }
};

O(N),利用栈,记录路径。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool dfs(TreeNode*root,TreeNode*x,int i)
    {
        if(root==NULL)return false;
        if(i==1)p1.push(root);
        if(i==2)q1.push(root);
        if(root==x)return true;
        if(dfs(root->left,x,i))return true;
        if(dfs(root->right,x,i))return true;
        if(i==1)p1.pop();
        if(i==2)q1.pop();
        return false;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        dfs(root,p,1);
        dfs(root,q,2);
        while(p1.size()>q1.size())p1.pop();
        while(p1.size()<q1.size())q1.pop();
        while(p1.top()!=q1.top())p1.pop(),q1.pop();
        return p1.top();
    }
    stack<TreeNode*> p1,q1;
};

4.二叉搜索树与双向链表

二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

中序遍历,prev是前驱的节点,root是当前节点,本质上是将二叉树线索化

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
	void InOrder(TreeNode*root,TreeNode*&prev)
	{
		if(root==nullptr)return;
		InOrder(root->left,prev);
		if(prev==nullptr)res=root;
		root->left=prev;
		if(prev)
		prev->right=root;
		prev=root;
		InOrder(root->right,prev);
	}
    TreeNode* Convert(TreeNode* pRootOfTree) {
		TreeNode*p=nullptr;
        InOrder(pRootOfTree,p);
		return res;
    }
	TreeNode*res;
};

5.前序中序构建二叉树

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    unordered_map<int,int>mp;
    TreeNode*  dfs(vector<int>& preorder, vector<int>& inorder,int l,int r,int &i)
    {
        if(l>r)return NULL;
        TreeNode* root=new TreeNode(preorder[i++]);
        int x=mp[root->val];
        root->left=dfs(preorder,inorder,l,x-1,i);
        root->right=dfs(preorder,inorder,x+1,r,i);
        return root;
        
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int index=0;
        for(int i=0;i<inorder.size();i++)mp[inorder[i]]=i;
        return dfs(preorder,inorder,0,preorder.size()-1,index);
    }
};
照着前序走,前序确定根,中序确定子树区间

6.中序后续构造二叉树

106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    unordered_map<int,int>mp;
    TreeNode*dfs(vector<int>& inorder,vector<int>& postorder,int l,int r,int &in)
    {
        if(l>r)return NULL;
        TreeNode*root=new TreeNode(postorder[in--]);
        int x=mp[root->val];
        root->right=dfs(inorder,postorder,x+1,r,in);
        root->left=dfs(inorder,postorder,l,x-1,in);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int i=postorder.size()-1;
        for(int i=0;i<inorder.size();i++)mp[inorder[i]]=i;
        return dfs(inorder,postorder,0,inorder.size()-1,i);
    }
};
注意,我们递归只能按根的顺序下去,所以,后序遍历的话,我们要从根,右子树,左子树的顺序构建二叉树
而前面前序遍历的题,之所以看着递归顺序很像前序遍历,其实最重要的前序遍历是根左子树右子树,所以不用额外内容。这里后序遍历就要考虑如何从根开始构建二叉树。

7.二叉树前序遍历(迭代)

144. 二叉树的前序遍历 - 力扣(LeetCode)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        TreeNode* cur = root;
        stack<TreeNode*>s;
        vector<int>res(0);
        while (!s.empty()||cur)//当栈不为空说明还有某个节点的右子树没遍历\
//cur是为了针对,此时cur正是最后一个右子树的根节点,但是栈已经pop掉了,为空了的情况
        {
            while (cur)//以cur为根节点,一直往左走,把东西放进去res,s
            {
                res.push_back(cur->val);
                s.push(cur);
                cur = cur->left;
            }
            cur = s.top()->right;//此时,开始提出栈顶节点,访问这个节点的右子树
            s.pop();
        }
        return res;
    }
};

 先访问左路,再访问右树,因为是前序遍历,所以访问左路的时候就要放入数组

8.二叉树中序遍历(迭代)

94. 二叉树的中序遍历 - 力扣(LeetCode)

跟前序很像,主要是,中序是访问完左路,才输出自己,所以,访问左路的操作不变,

放入数组是在访问完之后,再把栈顶元素放入数组,因为栈顶元素取出来,意外着,栈顶节点的左树已经访问完了,此时要访问右子树了,根据中序遍历,此时先放入数组,再继续访问右子树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    
    vector<int> inorderTraversal(TreeNode* root) {
        TreeNode* cur = root;
        stack<TreeNode*>s;
        vector<int>res(0);
        while (!s.empty()||cur)
        {
            while (cur)
            {
                s.push(cur);
                cur = cur->left;
            }
            TreeNode*top=s.top();
            res.push_back(top->val);
            cur = top->right;
            s.pop();
        }
        return res;
    }
};

9.二叉树后序遍历(迭代)

145. 二叉树的后序遍历 - 力扣(LeetCode)

根本的思路跟前面的前序中序差不多。

后序遍历的时候,我们会发现一个问题,那就是根节点,是可能会被访问两次的。

第一次,是左子树访问完,然后到了根节点,再去右子树。

第二次,是右子树访问完,又回到了根节点。

一种方案是每个节点都配一个标记变量,一种就是如下的。

首先我们注意,一个根节点第一次被取到的时候,说明该根节点的左子树已经被访问完了,这时候我们前一个访问的节点(也可以认为是当前栈顶元素的前一个栈顶元素),正是左子树的根节点(或空,反正左子树肯定是被访问完了)。第二次被访问到的时候,前一个访问的节点有多种可能(一个是当前根节点的兄弟,当前根节点的左子树根节点(说明右子树是空),当前根节点的右子树根节点,总归而言右子树肯定是被访问了的)。

据此,我们可以这样判断,每次if判断,当前根节点的右子树是否是空,以及是否是前一个访问的节点。当前根节点的右子树是空,按照遍历的思路,也可以认为这就是后序遍历中的当前根节点的前一个访问节点(只是是空罢了);如果当前根节点的右子树直接或间接(空)是前一个访问节点,那么说明,右子树已经被访问过了,结合前面的,左子树肯定被访问过了,那么这时候,就是把根节点放入遍历数组,同时记得取走栈顶元素,更新 前一个访问的节点。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        TreeNode* cur = root;
        stack<TreeNode*>s;
        vector<int>res(0);
        while (!s.empty()||cur)
        {
            while (cur)
            {
                s.push(cur);
                cur = cur->left;
            }
            TreeNode*top=s.top();
            res.push_back(top->val);
            cur = top->right;
            s.pop();
        }
        return res;
    }
    vector<int> postorderTraversal(TreeNode* root) {
        TreeNode* cur = root;
        stack<TreeNode*>s;
        TreeNode*prev=nullptr;
        vector<int>res(0);
        while (!s.empty()||cur)
        {
            while (cur)
            {
                s.push(cur);
                cur = cur->left;
            }
            TreeNode*top=s.top();
            if(top->right==nullptr||top->right==prev)
            {
                s.pop();
                res.push_back(top->val);
                prev=top;
            }
            else
            {
                cur = top->right;
            }
            
            
        }
        return res;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2132050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于"WT2605C的智能血压计:AI对话引领个性化健康管理新时代,健康守护随时在线

在当今快节奏的生活中&#xff0c;健康管理已成为我们日常不可或缺的一部分。随着科技的进步&#xff0c;智能设备正逐步融入我们的日常生活&#xff0c;为健康管理带来前所未有的便捷与智能化。今天&#xff0c;让我们共同探索WT2605C AI在线方案如何在血压计中发挥革命性作用…

线性回归_最小二乘法

from numpy import * import matplotlib.pyplot as plt1.导入数据 # 1.导入数据 # 模型 y wx b points genfromtxt(linear_regress_lsm_data.csv, delimiter,) N len(points) print(point count %d%N) x array(points[:, 0]) y array(points[:, 1]) plt.scatter(x, y) p…

超详细超实用!!!零基础java开发之云风笔记笔记列表接口模糊查询(八)

云风网 云风笔记 云风知识库 笔记列表数据接口除了获取全部数据&#xff0c;还需要考虑分页获取/以及各个参数查询获取数据。这样才符合日常需要。 模糊查询一般是定义一个固定的查询参数searchName,查询同数据类型的参数&#xff0c;比如都是字符串数据类型。由于我们名称、类…

网络插件 Cilium 更换 Calico

网络插件 Cilium 更换 Calico 集群使用 submariner &#xff0c;通过网络检测发现 Cilium 插件可能兼容性不太好 subctl diagnose allCilium 彻底卸载 helm uninstall cilium -n kube-system# 检查集群中的所有 CNI 插件&#xff08;集群的每个节点都需要删除&#xff09; s…

ELK在Linux服务器下使用docker快速部署(超详细)

ELK是什么&#xff1f; 首先说说什么是ELK ELK 是一个开源的日志管理和分析平台&#xff0c;由三个主要组件组成&#xff1a; Elasticsearch&#xff1a;一个分布式搜索和分析引擎&#xff0c;能够快速存储、搜索和分析大量数据。它是 ELK 堆栈的核心&#xff0c;负责数据的…

【FOC】【001】Clarke变换--- 2024.9.13

目录 前言背景知识介绍Clarke变换结束语 前言 在电机控制领域&#xff0c;想必大家都听过foc矢量控制&#xff0c;比我们耳熟能详的pid控制效果更好&#xff0c;那具体foc干了哪几件事呢&#xff1f;接下来本栏中会详细的讲解foc的原理。 该栏目部分参考于《慧驱动》 背景知识介…

gin配置swagger文档

一、基本准备工作 1、安装依赖包 go get -u github.com/swaggo/swag/cmd/swag go get -u github.com/swaggo/gin-swagger go get -u github.com/swaggo/files2、在根目录上配置swagger的路由文件 //2.初始化路由router : initialize.Routers()// 配置swaggerdocs.SwaggerInfo…

Redis系列之底层数据结构SDS

Redis系列之底层数据结构SDS 实验的环境 Redis 6.0VSCode 1.88.1 什么是SDS? SDS&#xff1a;Simple Dynamic String&#xff0c;翻译为简单动态字符串。SDS是一种用于存储二进制数据的数据结构&#xff0c;具有动态扩容的特点&#xff0c;代码位于src/sds.h和src/sds.c …

解决“找不到msvcp140.dll无法继续执行代码”问题:技术困境与解决方案

在现代计算机技术的发展中&#xff0c;软件依赖性问题日益凸显&#xff0c;其中“找不到msvcp140.dll无法继续执行代码”错误尤为常见。本文将从技术背景、问题成因、解决方案及预防措施四个方面&#xff0c;深入探讨这一技术困境&#xff0c;旨在为读者提供全面的理解和有效的…

来云栖大会!探展云上开发,沉浸式体验云原生 + AI 新奇玩法

2024 云栖大会来了&#xff01; 本届云栖大会将于 9 月 19 日至 9 月 21 日 在杭州云栖小镇召开 汇集全球最新云计算、AI 硬科技 云栖大会的主论坛将设置三个对话环节&#xff0c;大模型、自动驾驶和机器人领域的领军技术人和明星创业者将分享 AI 前沿趋势和应用进展。 20…

北京网页制作-网站策划

北京网页制作与网站策划 随着互联网的快速发展&#xff0c;网页制作和网站策划逐渐成为企业成功的重要因素。在北京这座现代化的大都市&#xff0c;各行各业都意识到网络的重要性&#xff0c;纷纷投身于网页制作与网站策划的浪潮中。本文将探讨北京网页制作的现状、常见的策划流…

基于Java web技术开发设计的学生信息管理系统

学生信息管理系统&#xff1a;Java Web技术与MySQL数据库的融合应用 包含初始化sql脚本 本毕业设计聚焦于构建一套高效、安全的学生信息管理系统&#xff0c;该系统采用先进的Java Web技术进行开发&#xff0c;并依托MySQL数据库实现数据的高效存储与管理。 背景与挑战&…

超声波清洗机的优势有哪些?四款精品超声波清洗机推荐,闭眼入

在这个快节奏的时代里&#xff0c;清洁和卫生变得尤为重要。对于那些追求精致生活的人们来说&#xff0c;家中配备一台超声波清洗机无疑是一件居家的好选择。本文将为您介绍四款广受好评的超声波清洗机&#xff0c;让您在日常清洗中也能享受到科技带来的便捷与舒适。无论是眼镜…

九、Redis 的实际使用与Redis的设计

一、多级缓存架构 在线上系统中&#xff0c;一定不会单纯的只部署一个Redis集群&#xff0c;而是使用Redis结合其他的多级缓存应用进行架构。 使用多级缓存架构的优点就是可以使不同类型的数据分布在不同的应用中&#xff0c;比如redis的热点key可以存储到nginx本地缓存、服务…

[渗透测试]-CVE-2016-4437-Shiro550漏洞复现

Shiro550漏洞爆出的时间是2016年为第4437个漏洞&#xff0c;所以它的CVE编码是2016-4437 文章目录 前言 什么是Shiro550漏洞&#xff1f; 1.Shiro550漏洞原理&#xff1a; 2.漏洞利用 3.漏洞复现&#xff1a; 前提&#xff1a;下载并打开vulhub靶场。 CVE-2016-4437-shiro550漏…

基于react native的锚点

基于react native的锚点 效果示例图示例代码 效果示例图 示例代码 /* eslint-disable react-native/no-inline-styles */ import React, { useEffect, useRef, useState } from react; import {Image,ImageBackground,ScrollView,StyleSheet,Text,TouchableOpacity,View, } fr…

怎么选到好用的运动耳机?这五款年度精选骨传导耳机非常好用!

在过去的两年里&#xff0c;骨传导耳机逐渐被大众的所熟知。可能毕竟长时间使用音量过大的传统入耳式耳机&#xff0c;多多少少会对我们的听力健康构成威胁。所以很多人就想找一款不伤耳朵的耳机。然后就了解到了骨传导耳机&#xff0c;所以就会延伸出这些问题——骨传导耳机好…

【Canvas与密铺】90年代马赛克密铺效果 1920x1080

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>20世纪90年代马赛克瓷砖效果1920x1080</title><style type&…

美创获评CNVD年度原创漏洞发现贡献单位!

9月10日&#xff0c;第21届中国网络安全年会暨网络安全协同治理分论坛在广州成功举办。会上&#xff0c;美创科技首次获评“CNVD年度原创漏洞发现贡献单位”。 美创科技依托第59号安全实验室&#xff0c;专注数据安全技术和攻防研究。凭借深厚的技术积累与优势&#xff0c;被遴…

SEAFARING靶场渗透

一.SQL注入漏洞 1.输入id1 -- 下方出现数据说明闭合成功 2.测试得出数据库有三列 3.三处都是回显点 4.联合查询爆出库名 5.查表名 ?id-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schematest-- 6.查字段名 ?id-1 union sele…