【数据结构从0到1】第七篇:二叉树进阶

文章目录

  • 一、搜索二叉树
    • 1.1 二叉搜索树概念
    • 1.2 二叉搜索树操作
    • 1.3 二叉搜索树的实现
    • 1.4 二叉搜索树的应用
    • 1.5 二叉搜索树的性能分析
  • 二、叉树oj题
    • 2.1 二叉树创建字符串
    • 2.2 二叉树的分层遍历1
    • 2.3 二叉树的分层遍历2
    • 2.4 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先
    • 2.5 二叉树搜索树转换成排序双向链表
    • 2.6 根据一棵树的前序遍历与中序遍历构造二叉树
    • 2.7 二叉树的前序遍历,非递归迭代实现
    • 2.8 二叉树中序遍历 ,非递归迭代实现
    • 2.9 二叉树的后序遍历 ,非递归迭代实现


一、搜索二叉树

1.1 二叉搜索树概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
【数据结构从0到1】第七篇:二叉树进阶_第1张图片
int a [] = {5,3,4,1,7,8,2,6,0,9};

1.2 二叉搜索树操作

  1. 二叉搜索树的查找

【数据结构从0到1】第七篇:二叉树进阶_第2张图片
2. 二叉搜索树的插入
插入的具体过程如下:
a. 树为空,则直接插入
【数据结构从0到1】第七篇:二叉树进阶_第3张图片
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
【数据结构从0到1】第七篇:二叉树进阶_第4张图片
3. 二叉搜索树的删除
4. 首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
  • 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题

1.3 二叉搜索树的实现

#pragma once
#include 
using namespace std;

template<class K>
struct BSTreeNode
{
	BSTreeNode(const K& key)
	{
		_left = _right = nullptr;
		_key = key;
	}

	BSTreeNode* _left;
	BSTreeNode* _right;
	K _key;
};

template<class K>
class BSTree
{

public:
	typedef BSTreeNode<K> Node;
	BSTree()
		:_root(nullptr)
	{}
	
	Node* FindR(const K& key)
	{
		return _FindR(_root, key);
	}

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

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

	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}

		else
		{
			Node* cur = _root;
			Node* parent = nullptr;
			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为空
			if (parent->_key < key)
			{
				parent->_right = new Node(key);
			}
			else 
			{
				parent->_left = new Node(key);
			}
			return true;
		}
	}

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

	bool Erase(const K& key)
	{
		if (_root == nullptr)
		{
			return false;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				//找到了
				if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = _root->_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;
					}
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
				}

				else//左右节点都在
				{
					Node* min = cur->_right;
					Node* min_parent = cur;
					while (min->_left != nullptr)
					{
						min_parent = min;
						min = min->_left;
					}
					cur->_key = min->_key;//替换
					if (min_parent->_left == min)
					{
						min_parent->_left = min->_right;
					}
					else
					{
						min_parent->_right = min->_right;
					}
					delete min;
				}
				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 << " ";
		_InOrder(root->_right);
	}

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

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

		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;
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				Node* min = root->_right;
				while (min->_left != nullptr)
				{
					min = min->_left;
				}
				swap(root->_key, min->_key);
				 //_EraseR(root, key);
				return _EraseR(root->_right, key);
			}

			delete del;
		}
	}
	Node* _root;
};

1.4 二叉搜索树的应用

  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
    • 以单词集合中的每个单词作为key,构建一棵二叉搜索树
    • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误
  1. KV模型:每一个关键码key,都有与之对应的值Value,即的键值对。该种方式在现实生活中非常常见:比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文就构成一种键值对;再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是就构成一种键值对。
    • 比如:实现一个简单的英汉词典dict,可以通过英文找到与其对应的中文,具体实现方式如下:
    • <单词,中文含义>为键值对构造二叉搜索树,注意:二叉搜索树需要比较,键值对比较时只比较Key查询英文单词时,只需给出英文单词,就可快速找到与其对应的key
#pragma once
#include 
#include 
using namespace std;

namespace ts
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode(const K& key, const V& value)
		{
			_left = _right = nullptr;
			_key = key;
			_value = value;
		}

		BSTreeNode* _left;
		BSTreeNode* _right;
		K _key;
		V _value;
	};

	template< class K, class V>
	class BSTree
	{

	public:
		typedef BSTreeNode<K, V> Node;
		BSTree()
			:_root(nullptr)
		{}

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

		bool Insert(const K& key, const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(key, value);
				return true;
			}

			else
			{
				Node* cur = _root;
				Node* parent = nullptr;
				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为空
				if (parent->_key < key)
				{
					parent->_right = new Node(key, value);
				}
				else
				{
					parent->_left = new Node(key, value);
				}
				return true;
			}
		}

		bool Erase(const K& key)
		{
			if (_root == nullptr)
			{
				return false;
			}
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					//找到了
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = _root->_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;
						}
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
						delete cur;
					}

					else//左右节点都在
					{
						Node* min = cur->_right;
						Node* min_parent = cur;
						while (min->_left != nullptr)
						{
							min_parent = min;
							min = min->_left;
						}
						cur->_key = min->_key;//替换
						cur->_value = min->_value;
						if (min_parent->_left == min)
						{
							min_parent->_left = min->_right;
						}
						else
						{
							min_parent->_right = min->_right;
						}
						delete min;
					}
					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);
		}

		Node* _root;
	};
}

1.5 二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的
深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
【数据结构从0到1】第七篇:二叉树进阶_第5张图片
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log2^N
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2
问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,都可以是二叉搜索树的性能最佳?

二、叉树oj题

2.1 二叉树创建字符串

oj链接
【数据结构从0到1】第七篇:二叉树进阶_第6张图片

/**
 * 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:
    void _tree2str(TreeNode* root, string& s)
    {
        if(root == nullptr)
        {
            return;
        }
        
        s += to_string(root->val);
        if(root->left || root->right)//root的右边为空的时候,左边不能为空
        {
            s += '(';
            _tree2str(root->left, s);
            s += ')';
        }
        
        if(root->right)
        {
            s += '(';
            _tree2str(root->right, s);
            s += ')';
        }
    }


    string tree2str(TreeNode* root) 
    {
        string s;
        _tree2str(root, s);
        return s;
    }
};

2.2 二叉树的分层遍历1

oj链接
【数据结构从0到1】第七篇:二叉树进阶_第7张图片

/**
 * 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<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> vv;
        queue<TreeNode*> q;
        int levelSize = 0;//记录每一层的个数
        if(root == nullptr)
        {
            return vv;
        }        
        q.push(root);
        levelSize = 1;

        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);
            }
            vv.push_back(v);
            levelSize = q.size();
        }
        return vv;
    }
};

2.3 二叉树的分层遍历2

oj连接诶
【数据结构从0到1】第七篇:二叉树进阶_第8张图片

/**
 * 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<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> vv;
        queue<TreeNode*> q;
        int levelSize = 0;
        if(root == nullptr)
        {
            return vv;
        }
        q.push(root);
        levelSize = 1;
        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);
            }
            vv.push_back(v);
            levelSize = q.size();
        }
        reverse(vv.begin(), vv.end());
        return vv;
    }

};

2.4 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先

oj链接
【数据结构从0到1】第七篇:二叉树进阶_第9张图片
方法一:

/**
 * 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 {
    //查找节点的路径
    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;
        else if(FindPath(root->right, x, path))
            return true;
        path.pop();
        return false;
    }

public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath;
        stack<TreeNode*> qPath;
        FindPath(root, p, pPath);//将p的路径保存在pPath中
        FindPath(root, q, qPath);//将q的路径保存在qPath总

        //将路径长度变为相同
        while(pPath.size() != qPath.size())
        {
            if(pPath.size() < qPath.size())
                qPath.pop();
            else 
                pPath.pop();
        }
        //同时查找,判断是否相同
        while(!pPath.empty())
        {
            if(pPath.top() == qPath.top())
                return pPath.top();
            pPath.pop();
            qPath.pop();
        }
        return nullptr;
    }
};

方法二:

/**
 * 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<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> vv;
        queue<TreeNode*> q;
        int levelSize = 0;
        if(root == nullptr)
        {
            return vv;
        }
        q.push(root);
        levelSize = 1;
        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);
            }
            vv.push_back(v);
            levelSize = q.size();
        }
        reverse(vv.begin(), vv.end());
        return vv;
    }

};

2.5 二叉树搜索树转换成排序双向链表

oj链接
【数据结构从0到1】第七篇:二叉树进阶_第10张图片

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/

void InOrder(TreeNode* cur, TreeNode*& prev)
{
    if(cur == nullptr)
        return;
    InOrder(cur->left, prev);
    //链接
    if(prev == nullptr)
        prev = cur;
    else
    {
        prev->right = cur;
        cur->left = prev;
        prev = cur;
    }
    InOrder(cur->right, prev);
}

class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree) {
        if(pRootOfTree == nullptr)
            return nullptr;
        TreeNode* prev = nullptr;
        InOrder(pRootOfTree, prev);
        //找头结点
        TreeNode* head = pRootOfTree;
        while(head->left)
        {
            head = head->left;
        }
        return head;
    }
};

2.6 根据一棵树的前序遍历与中序遍历构造二叉树

oj链接
【数据结构从0到1】第七篇:二叉树进阶_第11张图片

/**
 * 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:
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int inbegin, int inend ) 
    {
        if(inbegin > inend)
            return nullptr;
        TreeNode* root = new TreeNode(preorder[prei]);
        int rooti = inbegin;
        while(rooti <= inend)
        {
            if(preorder[prei] == inorder[rooti])
                break;
            else 
                ++rooti;
        }
        ++prei;
        //[inbegin, rooti-1] [rooti+1, inend]
        root->left = _buildTree(preorder, inorder, prei, inbegin, rooti-1);
        root->right = _buildTree(preorder, inorder, prei, rooti+1, inend);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int prei = 0;
        return _buildTree(preorder, inorder, prei, 0, inorder.size() - 1);
    }
};

2.7 二叉树的前序遍历,非递归迭代实现

添加链接描述
【数据结构从0到1】第七篇:二叉树进阶_第12张图片

/**
 * 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) {
        vector<int> v;
        stack<TreeNode*> s;
        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;
    }
};

2.8 二叉树中序遍历 ,非递归迭代实现

oj链接
【数据结构从0到1】第七篇:二叉树进阶_第13张图片

/**
 * 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) {
        vector<int> v;
        stack<TreeNode*> s;
        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;
    }
};

2.9 二叉树的后序遍历 ,非递归迭代实现

oj链接
【数据结构从0到1】第七篇:二叉树进阶_第14张图片

/**
 * 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> postorderTraversal(TreeNode* root) 
    {
        vector<int> v;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        TreeNode* prev = nullptr;

        while(cur || !s.empty())
        {
            while(cur)
            {
                s.push(cur);
                cur = cur->left;
            }
            TreeNode* top = s.top();
    
            if(top->right == nullptr || top->right == prev)
            {
                v.push_back(top->val);
                prev = top;
                s.pop();
            }
            else 
            {
                cur = top->right;
            }
        }
        return v;
    }
};

你可能感兴趣的:(数据结构,二叉树)