二叉树c++(level up)

目录

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;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值