二叉树在前面C数据结构阶段已经讲过,本节取名二叉树进阶是因为:
因此本节借二叉树搜索树,对二叉树部分进行收尾总结。
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
#pragma once
#include
#include
using namespace std;
namespace key
{
template<class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
: _left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
private:
void destoryTree(Node* root)
{
if (root == nullptr)
return;
destoryTree(root->_right);
destoryTree(root->_left);
delete root;
}
Node* CopyTree(const Node* root)// 前序遍历递归
{
if (root = nullptr)
return nullptr;
Node* copyNode = new Node(root->_key);
copyNode->_left = CopyTree(root->_left);
copyNode->_right = CopyTree(root->_right);
return copyNode;
}
public:
//自己写了拷贝构造函数,编译器就不会默认生成构造函数
//default用于强制编译器自己生成构造(C++11)
BSTree() = default;
BSTree(const BSTree<K>& t)
{
_root = CopyTree(t._root);
}
~BSTree()
{
destoryTree(_root);
_root = nullptr;
}
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);
if (key > parent->_key)
{
parent->_right = cur;
}
else if (key < parent->_key)
{
parent->_left = cur;
}
return true;
}
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
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->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
// 一个孩子、没有孩子:托孤
// 两个孩子:替代法
if (cur->_left == nullptr)
{
// 删根节点
// if (parent == nullptr)
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_right)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
delete cur;
}
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;
}
}
delete cur;
}
//两个孩子:替代法。用左子树的最右节点 或 右树的最左节点
else
{
//用右树的最左节点替代
Node* minParent = cur;// 存父亲,不能给nullptr,因为右子树的根可能就是minRight
Node* minRight = cur->_right;// 存
while (minRight->_left)// 找右树的最左
{
minParent = minRight;
minRight = minRight->_left;
}
swap(cur->_key, minRight->_key);
// 开始托孤
// return erase(key);交换后不符合二叉搜索树了。找不到key了。
if (minRight = minParent->_left)
{
minParent->_left = minRight->_right;
}
else
{
minParent->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;// cur为空,找不到要删的
}
void InOrder()// 在类外传递root很麻烦,所以再套一层
{
_InOrder(_root);
cout << endl;
}
// 递归
bool FindR(const K& key)
{
return _FindR(_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)//Node*& root是为了直接链接新节点。root的值是空,但root同时是父节点的left指针的别名
{
if (root == nullptr)
return false;
if (key > root->_key)
return _EraseR(root->_right, key);
else if (key < root->_key)
return _EraseR(root->_left, key);
else
{
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;// root是父节点孩子的别名。
return true;
}
else if (root->_right == nullptr)
{
root = root->_left;
return true;
}
else
{
Node* minRight = root->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
swap(minRight->_key, root->_key);
return _EraseR(root->_right, key);// 递归删除,删除左为空的节点
}
}
}
bool _InsertR(Node*& root, const K& key)//Node*& root是为了直接链接新节点。root的值是空,但root同时是父节点的left指针的别名
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (key > root->_key)
return _InsertR(root->_right, key);
else if (key < root->_key)
return _InsertR(root->_left, key);
else
return false;
}
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
return false;
if (key < root->_key)
_FindR(root->_left, key);
else if (key > root->_key)
_FindR(root->_right, key);
else
return true;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
Node* _root = nullptr;
};
void test1()
{
BSTree<int> t;
int a[] = { 14,1,76,42,13,5 };
for (auto e : a)
{
t.InsertR(e);
}
t.InOrder();
for (auto e : a)
{
t.EraseR(e);
}
t.InOrder();
}
}
K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
KV模型:每一个关键码key,都有与之对应的值Value,即
namespace key_value
{
template<class K, class V>
struct BSTreeNode
{
BSTreeNode<K, V>* _left;
BSTreeNode<K, V>* _right;
const 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:
void InOrder()// 在类外传递root很麻烦,所以再套一层
{
_InOrder(_root);
cout << endl;
}
// 递归
Node* FindR(const K& key)
{
return _FindR(_root, key);
}
bool InsertR(const K& key, const V& value)
{
return _InsertR(_root, key, value);
}
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
private:
bool _EraseR(Node*& root, const K& key)//Node*& root是为了直接链接新节点。root的值是空,但root同时是父节点的left指针的别名
{
if (root == nullptr)
return false;
if (key > root->_key)
return _EraseR(root->_right, key);
else if (key < root->_key)
return _EraseR(root->_left, key);
else
{
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;// root是父节点孩子的别名。
return true;
}
else if (root->_right == nullptr)
{
root = root->_left;
return true;
}
else
{
Node* minRight = root->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
swap(minRight->_key, root->_key);
return _EraseR(root->_right, key);// 递归删除,删除左为空的节点
}
}
}
bool _InsertR(Node*& root, const K& key, const V& value)//Node*& root是为了直接链接新节点。root的值是空,但root同时是父节点的left指针的别名
{
if (root == nullptr)
{
root = new Node(key, value);
return true;
}
if (key > root->_key)
return _InsertR(root->_right, key, value);
else if (key < root->_key)
return _InsertR(root->_left, key, value);
else
return false;
}
Node* _FindR(Node* root, const K& key)
{
if (root == nullptr)
return nullptr;
if (key < root->_key)
_FindR(root->_left, key);
else if (key > root->_key)
_FindR(root->_right, key);
else
return root;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
Node* _root = nullptr;
};
void Test1()
{
BSTree<string, string> ECDict;
ECDict.InsertR("root", "根");
ECDict.InsertR("string", "字符串");
ECDict.InsertR("left", "左边");
ECDict.InsertR("insert", "插入");
//...
string str;
while (cin >> str) //while (scanf() != EOF)
{
//BSTreeNode* ret = ECDict.FindR(str);
auto ret = ECDict.FindR(str);
if (ret != nullptr)
{
cout << "对应的中文:" << ret->_value << endl;
//ret->_key = "";
ret->_value = "";
}
else
{
cout << "无此单词,请重新输入" << endl;
}
}
}
}
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
问题:如果退化成单支树,二叉搜索树的性能就失去了。
那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后续章节学习的AVL树和红黑树就可以上场了
二叉树的最近公共祖先
class Solution {
public:
bool isInSubTree(TreeNode* tree, TreeNode* x)
{
if(tree == nullptr)
return false;
if(tree == x)
return true;
return isInSubTree(tree->left, x)
|| isInSubTree(tree->right, x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr)
return nullptr;
if(root == p || root == q)
return root;
bool pInLeft = isInSubTree(root->left, p);
bool pInRight = !pInLeft;
bool qInLeft = isInSubTree(root->left, q);
bool qInRight = !qInLeft;
if(pInLeft && qInLeft)
{
return lowestCommonAncestor(root->left, p, q);
}
else if(pInRight && qInRight)
{
return lowestCommonAncestor(root->right, p, q);
}
else
{
return root;
}
}
};
要一个个找,效率无法保证。把题目转化为相交链表求交点,把它的路径求出来,在找交点,交点即为最近公共祖先。
/**
* 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 Route(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
{
if(root == nullptr)
return false;
path.push(root);
if(root == x)
return true;
else if(Route(root->left, x, path))
return true;
else if(Route(root->right, x, path))
return true;
else
{
path.pop();
return false;
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
stack<TreeNode*> pPath, qPath;
Route(root, p, pPath);
Route(root, q, qPath);
//将这题转化为相交链表求交点,先让它们长度相同
while(pPath.size() > qPath.size())
pPath.pop();
while(pPath.size() < qPath.size())
qPath.pop();
//再同时走,求交点
while(pPath.top()!= qPath.top())
{
pPath.pop();
qPath.pop();
}
return pPath.top();
}
};
从前序与中序遍历序列构造二叉树
/**
* 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]);
prei++;
int rooti = inBegin;
while(rooti<=inEnd)
{
if(inorder[rooti] == root->val)
break;
else
rooti++;
}
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, inBegin = 0, inEnd = preorder.size()-1;
TreeNode* root = _buildTree(preorder, inorder, prei, inBegin, inEnd);
return root;
}
};
非递归前序遍历
/**
* 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*> st;
TreeNode* cur = root;
while(cur || !st.empty())
{
//遍历左路节点,左路节点入栈——访问一棵树的开始
while(cur)
{
v.push_back(cur->val);
st.push(cur);
cur = cur->left;
}
//依次取左路节点的右子树访问
TreeNode* top = st.top();
st.pop();
//访问左路节点的右子树--子问题
cur = top->right;
}
return v;
}
};