算法与数据结构之查找二叉树
1 MAP类模板
建立一个MAP类模板,其中有两个参数
template<typename T1,typename T2>
class MAP
{
//源代码
};
T1表示键类型,T2表示值类型
2 Node结构体
template<typename T1, typename T2>
class Node
{
Node(T1 key, T2 val)
{
_Key = key;
_Val = val;
_Left = nullptr;
_Right = nullptr;
_N = 1;
}
T1 _Key;
T2 _Val;
Node<T1, T2>*_Left;
Node<T1, T2>*_Right;
int _N;//该节点所含节点个数
};
3 得到总节点数的方法实现
公有调用:
int size() { return size(_root); }
私有实现:
template<typename T1, typename T2>
int MAP<T1, T2>::size(Node<T1, T2>* x)
{
if (x == nullptr)
return 0;
return size(x->_Left) + size(x->_Right) + 1;
}
如果节点为空,默认为0
4 根据键取值的方法
公有调用
T2 get(T1 key) { return get(_root, key); }
私有实现
template<typename T1,typename T2>
T2 MAP<T1,T2>::get(Node<T1, T2>*x, T1 key)
{
if (x == nullptr)
return T2();
if (x->_Key < key)
return get(x->_Right, key);
else if (x->_Key > key)
return get(x->_Left, key);
else
return x->_Val;
}
5 改变键对应的值的方法
公有调用
void put(T1 key, T2 val) { _root = put(_root, key, val);
私有实现
template<typename T1,typename T2>
Node<T1, T2>*MAP<T1,T2>::put(Node<T1, T2> *x, T1 key, T2 val)
{
if (x == nullptr)
{
Node<T1, T2>*temp = new Node<T1, T2>(key, val);
return temp;
}
if (x->_Key < key)
x->_Right = put(x->_Right, key, val);
else if (x->_Key > key)
x->_Left = put(x->_Left, key, val);
else
x->_Val = val;
x->_N = size(x->_Left) + size(x->_Right) + 1;
return x;
}
6 得到最大值与最小值的方法
公有调用
T2 GetMax() { return GetMax(_root); }
T2 GetMin() { return GetMin(_root); }
私有实现
template<typename T1,typename T2>
T2 MAP<T1, T2>::GetMax(Node<T1, T2>*x)
{
if (x->_Right == nullptr)
return x->_Val;
return GetMax(x->_Right);
}
template<typename T1,typename T2>
T2 MAP<T1,T2>::GetMin(Node<T1, T2>*x)
{
if (x->_Left == nullptr)
return x->_Val;
return GetMin(x->_Left);
}
7 向上取整与向下取整
公有调用
T1 Floor(T1 key)
{
Node<T1,T2>*temp = Floor(_root, key);
if (temp == nullptr)
return T1();
return temp->_Key;
}
T1 Ceiling(T1 key)
{
Node<T1, T2>*temp = Ceiling(_root, key);
if (temp == nullptr)
return T1();
return temp->_Key;
}
私有实现
template<typename T1, typename T2>
Node<T1,T2>* MAP<T1, T2>::Floor(Node<T1, T2>* x, T1 key)
{
if (x == nullptr)
return nullptr;
if (x->_Key > key)
return Floor(x->_Left, key);
else if (x->_Key == key)
return x;
Node<T1, T2>*temp = Floor(x->_Right, key);
if (temp != nullptr)
return temp;
return x;
}
template<typename T1, typename T2>
Node<T1, T2>* MAP<T1, T2>::Ceiling(Node<T1, T2>* x, T1 key)
{
if (x == nullptr)
return nullptr;
if (x->_Key < key)
return Floor(x->_Right, key);
else if (x->_Key == key)
return x;
Node<T1, T2>*temp = Ceiling(x->_Left, key);
if (temp != nullptr)
return temp;
return x;
}
8.排名
公有调用
int Rank(T1 key) { return Rank(_root, key); }
T1 Select(int k)
{
Node<T1, T2>*temp = Select(_root, k);
if (temp == nullptr)
return T1();
return temp->_Key;
}
私有实现
template<typename T1, typename T2>
Node<T1, T2>* MAP<T1, T2>::Select(Node<T1, T2>* x, int k)
{
if (x == nullptr)
return nullptr;
int t = size(x->_Left);
if (t > k)
return Select(x->_Left, k);
else if (t < k)
return Select(x->_Right, k - t - 1);
else
return x;
}
template<typename T1, typename T2>
int MAP<T1, T2>::Rank(Node<T1, T2>* x, T1 key)
{
if (x == nullptr)
return 0;
if (x->_Key < key)
return 1 + size(x->_Left) + Rank(x->_Right, key);
else if (x->_Key > key)
return Rank(x->_Left, key);
else
return size(x->_Left);
}
9.删除
9.1 删除最大值与最小值
私有调用:
void DeleteMin() { DeleteMin(_root); }
void DeleteMax() { DeleteMax(_root); }
共有实现:
template<typename T1, typename T2>
void MAP<T1, T2>::DeleteMin(Node<T1, T2>* x)
{
if (x == nullptr)
return;
Node<T1, T2>*temp1 = x;
Node<T1, T2>*temp2 = nullptr;
while (temp1->_Left)
{
temp2 = temp1;
temp1 = temp1->_Left;
}
if (temp2 == nullptr)
x = x->_Right;
else
temp2->_Left = temp1->_Right;
delete temp1;
std::cout << "delete" << std::endl;
x->_N -= 1;
}
template<typename T1, typename T2>
void MAP<T1, T2>::DeleteMax(Node<T1, T2>* x)
{
if (x == nullptr)
return;
Node<T1, T2>*temp1 = x;
Node<T1, T2>*temp2 = nullptr;
while (temp1->_Right)
{
temp2 = temp1;
temp1 = temp1->_Right;
}
if (temp2 == nullptr)
x = x->_Left;
else
temp2->_Right = temp1->_Left;
delete temp1;
std::cout << "delete" << std::endl;
x->_N -= 1;
}
9.2删除某键
template<typename T1, typename T2>
Node<T1,T2>* MAP<T1,T2>::DeleteKey(Node<T1,T2>*x,T1 key)
{
Node<T1, T2>*temp1 = nullptr;
Node<T1, T2>*temp2 = nullptr;
if (x == nullptr)
return nullptr;
if (x->_Key > key)
x->_Left = DeleteKey(x->_Left, key);
else if (x->_Key < key)
x->_Right = DeleteKey(x->_Right, key);
else
{
if (x->_Left == nullptr)
{
temp1 = x->_Right;
delete x;
std::cout << "delete" << std::endl;
return temp1;
}
else if (x->_Right == nullptr)
{
temp1 = x->_Left;
delete x;
std::cout << "delete" << std::endl;
return temp1;
}
else
{
temp2 = x;
temp1 = GetMin(x->_Right);
x = new Node<T1, T2>(temp1->_Key, temp1->_Val);
DeleteMin(temp2->_Right);
x->_Left = temp2->_Left;
x->_Right = temp2->_Right;
std::cout << "delete" << std::endl;
delete temp2;
x->_N = size(x->_Left) + size(x->_Right) + 1;
}
}
return x;
}
找到某键后,判断左右是否为空,若左右一方为空,则选择另一方直接连上,随后用delete删除
若不为空,则在右支中找到最小值,用temp2保存节点位置,随后用temp1找寻最小键
找到之后,将temp1的节点信息新建节点,这是由于删除最小键方法局限导致的已经delete掉节点
随后将找到的键的左右支给予新节点,随后返回节点x,看似复杂,其实结构还好
10.遍历
10.1 递归遍历
我们的任务是将元素放置于一个容器之中
先来一个中序遍历,中序遍历意思是先访问左节点再访问父节点再访问右节点,该遍历方式可以按照升序方式排列二叉树
前序遍历与后序遍历顾名思义
只以中序遍历作为代码例子
私有调用
void keys() { vector<T1>Keys; T1 lo = GetMin(); T1 hi = GetMax(); keys(_root,Keys, lo,hi); Show(Keys); }
公有实现
template<typename T1, typename T2>
inline void MAP<T1, T2>::Show(vector<T1>&Keys)
{
for (int i = 0; i < Keys.size(); i++)
std::cout << Keys[i] << " ";
}
template<typename T1, typename T2>
void MAP<T1, T2>::keys(Node<T1,T2>*x,vector<T1>& Keys, T1 lo, T1 hi)
{
if (x == nullptr)
return;
if (lo < x->_Key)
keys(x->_Left, Keys, lo, hi);
if (lo <= x->_Key &&hi >= x->_Key)
Keys.push_back(x->_Key);
if (hi > x->_Key)
keys(x->_Right, Keys, lo, hi);
}
该遍历可变形,若遍历没有范围,则可以去除lo与hi的条件限制
若遍历有范围,则需要按照上述条件进行筛选,其中不取等于号的意思是可以节省迭代次数提高效率
10.2 非递归中序遍历
template<typename T1, typename T2>
void MAP<T1, T2>::InOrder(Node<T1, T2>* x, vector<T1>& result)
{
Node<T1, T2>*p = x;
stack<Node<T1, T2>*>s;
while (!s.empty() || p)
{
if (p)
{
s.push(p);
p = p->_Left;
}
else
{
p = s.top();
s.pop();
result.push_back(p->_Key);
p = p->_Right;
}
}
}
10.3 非递归前序遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*>s;
vector<int>result;
while(!s.empty()||root)
{
if(root)
{
s.push(root);
result.push_back(root->val);
root=root->left;
}
else
{
root = s.top();
s.pop();
root=root->right;
}
}
return result;
}
};
二叉树通用遍历法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*>s;
vector<int>result;
if(root==nullptr)
return result;
s.push(root);
while(!s.empty())
{
root = s.top();
result.push_back(root->val);
s.pop();
if(root->right)
s.push(root->right);
if(root->left)
s.push(root->left);
}
return result;
}
};
10.4 非递归后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int>result;
stack<TreeNode*>s;
TreeNode* pre = nullptr;
while(!s.empty()||root)
{
if(root)
{
s.push(root);
root = root->left;
}
else
{
TreeNode* top = s.top();
if(top->right && top->right != pre)
root = top->right;
else
{
result.push_back(top->val);
pre = top;
s.pop();
}
}
}
return result;
}
};
后序遍历无疑是最难的,我们需要判断之前保存的节点为左节点还是右节点
我们首先要遍历至左下角,边遍历边入栈,直到为空,随后我们取出top,如果top还有右节点的话
我们继续将二叉树遍历至最左下角,如果这时右节点为空了,我们就可以考虑保存数据
保存完数据后,我们需要出栈并且保存该遍历过的节点,因为root继续为空,所以我们继续进行else内的代码块
随后进行到根节点,根节点的右节点若存在,则进入,进入后假设没有节点了,那么我们保存完数据回到根节点
根节点虽然还有右节点,但是我们上一个保存的节点pre与他的节点相同,所以我们这时可以保存根节点的数据了
就这样一个思路进行下去