赞
踩
带父指针的BST树。
typedef int Type;
typedef struct BSTreeNode{
Type key; // 关键字(键值)
struct BSTreeNode *left; // 左孩子
struct BSTreeNode *right; // 右孩子
struct BSTreeNode *parent; // 父结点
}BstNode, *BSTree;
BstNode * First(BstNode * ptr); // 找左孩子,即最小元素节点
BstNode * Last(BstNode * ptr); // 找右孩子,即最大元素节点
void NiceInOrder(BstNode * ptr)
{
for( BstNode * p = First(ptr); p != nullptr; p = Next(p) )
{
cour << p->key;
}
}
// 需要设计实现Next函数
BstNode * Next(BstNode * ptr)
{
if(ptr == nullptr) return nullptr;
if(ptr->right != nullptr) // 情况1,有右子树,需要遍历右子树
{
return First(ptr->right);
}
else
{
BstNode * pa = ptr->parent; // 情况2/3 先得到父结点
while(pa != nullptr && pa->right == ptr)// 情况3,证明刚才是从右孩子回退的。则当前结点不取。
{ // pa->left != ptr 也是可以的,如果是父结点的左孩子(从左边回退),我们取当前结点。否则不取
ptr = pa;
pa = pa->next; // 反复操作,因为存在多次的回退操作
}
return pa;
}
上述的几部操作,类似使用树形结构的容器的迭代器操作。其中:
根据上述操作经验,Prev和 逆向的迭代中序即可实现。
void ResNiceInOrder(BstNode * ptr)
{
for(BstNode * p = Last(ptr); p != nullptr; p = Prev(p) )
{
cout << p->key;
}
}
// 与Next的操作为镜像的操作
BstNode * Prev(BstNode * ptr)
{
if(ptr == nullptr) return nullptr;
if(ptr->left != nullptr)
{
return Last(ptr->left);
}
else
{
BstNode * pa = ptr->parent;
while(pa != nullptr && pa->left == ptr) // or pa->right != ptr
{
ptr = pa;
pa = pa->parent;
}
return pa;
}
}
全部代码:
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
typedef int KeyType;
typedef struct BstNode
{
KeyType key;
BstNode* leftchild;
BstNode* parent;
BstNode* rightchild;
}BstNode, * BSTree;
BstNode* Buynode(KeyType kx)
{
BstNode* s = (BstNode*)malloc(sizeof(BstNode));
if (nullptr == s) exit(1);
memset(s, 0, sizeof(BstNode));
s->key = kx;
return s;
}
void Freenode(BstNode* p)
{
free(p);
}
bool Insert(BstNode*& ptr, KeyType kx)
{
if (ptr == nullptr)
{
ptr = Buynode(kx);
return true;
}
BstNode* pa = nullptr;
BstNode* p = ptr;
while (p != nullptr && p->key != kx)
{
pa = p;
p = kx < p->key ? p->leftchild : p->rightchild;
}
if (p != nullptr && p->key == kx) return false;
p = Buynode(kx);
p->parent = pa;
if (p->key < pa->key)
{
pa->leftchild = p;
}
else
{
pa->rightchild = p;
}
return true;
}
void InOrder(BstNode* ptr)
{
if (ptr != nullptr)
{
InOrder(ptr->leftchild);
cout << ptr->key << " ";
InOrder(ptr->rightchild);
}
}
BstNode* First(BstNode* ptr) // Min
{
while (ptr != nullptr && ptr->leftchild != nullptr)
{
ptr = ptr->leftchild;
}
return ptr;
}
BstNode* Last(BstNode* ptr) // Max
{
while (ptr != nullptr && ptr->rightchild != nullptr)
{
ptr = ptr->rightchild;
}
return ptr;
}
BstNode* Next(BstNode* ptr)
{
if (ptr == nullptr) return nullptr;
if (ptr->rightchild != nullptr)
{
return First(ptr->rightchild);
}
else
{
BstNode* pa = ptr->parent;
while (pa != nullptr && pa->leftchild != ptr)
{
ptr = pa;
pa = ptr->parent;
}
return pa;
}
}
BstNode* Prev(BstNode* ptr)
{
if (ptr == nullptr) return nullptr;
if (ptr->leftchild != nullptr)
{
return Last(ptr->leftchild);
}
else
{
BstNode* pa = ptr->parent;
while (pa != nullptr && pa->rightchild != ptr)
{
ptr = pa;
pa = ptr->parent;
}
return pa;
}
}
void NiceInOrder(BstNode* ptr)
{
for (BstNode* p = First(ptr); p != nullptr; p = Next(p))
{
cout << p->key << " ";
}
cout << endl;
}
void ResNiceInOrder(BstNode* ptr)
{
for (BstNode* p = Last(ptr); p != nullptr; p = Prev(p))
{
cout << p->key << " ";
}
cout << endl;
}
int main()
{
BSTree root = nullptr;
vector<int> ar = { 53,17,78,9,45,65,87,23,81,94,88,92 };
for (auto& x : ar)
{
Insert(root, x);
}
InOrder(root);
cout << endl;
NiceInOrder(root);
ResNiceInOrder(root);
}
不带双亲结点(不含父指针)的BST树,按照中序遍历的方式改造成链表(线索化)。
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
typedef int KeyType;
typedef struct BstNode
{
KeyType key;
BstNode* leftchild;
BstNode* rightchild;
}BstNode, * BSTree;
BstNode* Buynode(KeyType kx)
{
BstNode* s = (BstNode*)malloc(sizeof(BstNode));
if (nullptr == s) exit(1);
memset(s, 0, sizeof(BstNode));
s->key = kx;
return s;
}
void Freenode(BstNode* p)
{
free(p);
}
bool Insert(BstNode*& ptr, KeyType kx)
{
if (ptr == nullptr)
{
ptr = Buynode(kx);
return true;
}
BstNode* pa = nullptr;
BstNode* p = ptr;
while (p != nullptr && p->key != kx)
{
pa = p;
p = kx < p->key ? p->leftchild : p->rightchild;
}
if (p != nullptr && p->key == kx) return false;
p = Buynode(kx);
if (p->key < pa->key)
{
pa->leftchild = p;
}
else
{
pa->rightchild = p;
}
return true;
}
void InOrder(BstNode* ptr)
{
if (ptr != nullptr)
{
InOrder(ptr->leftchild);
cout << ptr->key << " ";
InOrder(ptr->rightchild);
}
}
// 借助栈,非递归的中序遍历
void NiceInOrder(BstNode* ptr)
{
stack<BstNode*> st;
while (ptr != nullptr || !st.empty())
{
while (ptr != nullptr)
{
st.push(ptr);
ptr = ptr->leftchild;
}
ptr = st.top(); st.pop();
cout << ptr->key << " ";
ptr = ptr->rightchild;
}
}
// 将BST树按照中序遍历的方式,线索化
BstNode* InOrderList(BstNode* ptr)
{
if (ptr == nullptr) return nullptr;
stack<BstNode*> st;
BstNode* newroot = nullptr;
BstNode* pre = nullptr;
while (ptr != nullptr || !st.empty())
{
while (ptr != nullptr)
{
st.push(ptr);
ptr = ptr->leftchild;
}
ptr = st.top(); st.pop();
if (pre == nullptr)
{
newroot = ptr;
pre = ptr;
}
else
{
pre->rightchild = ptr;
ptr->leftchild = pre;
pre = ptr;
}
ptr = ptr->rightchild;
}
return newroot;
}
int main()
{
BSTree root = nullptr;
vector<int> ar = { 53,17,78,9,45,65,87,23,81,94,88,92 };
for (auto& x : ar)
{
Insert(root, x);
}
InOrder(root);
cout << endl;
NiceInOrder(root);
cout << endl;
root = InOrderList(root);
BstNode* p = root;
// 正向打印
for ( ; p->rightchild != nullptr; p = p->rightchild)
{
cout << p->key << "->";
}
cout << p->key << endl;
// 逆向打印
for ( ; p != nullptr; p = p->leftchild)
{
cout << p->key << "->";
}
cout << endl;
return 0;
}
typedef int KeyType;
typedef struct AVLNode
{
AVLNode* leftchild;
AVLNode* parent;
AVLNode* rightchild;
int balance; // -1 ,0 ,1
KeyType key;
}AVLNode, * AVLTree;
左单旋转:
第一步:先将左右孩子结点完成旋转。
void RotateLeft(AVLTree& root, AVLNode* ptr)
{
AVLNode* newroot = ptr->rightchild;
ptr->rightchild = newroot->leftchild;
newroot->leftchild = ptr;
}
第二步:将父指针部分补充完整
void RotateLeft(AVLTree& root, AVLNode* ptr)
{
AVLNode* newroot = ptr->rightchild;
newroot->parent = ptr->parent;// 1 新的头结点指向原先的根节点的父节点
if (ptr->parent == nullptr)// 如果ptr是整棵树的根节点,则将root更新为 新的根节点newroot
{
root = newroot;
}
else// 如果ptr原先不是根节点,则查看ptr是其父节点的左孩子还是右孩子,然后更新父节点的孩子指向
{
if (ptr->parent->leftchild == ptr)
{
ptr->parent->leftchild = newroot;
}
else
{
ptr->parent->rightchild = newroot;
}
}
ptr->rightchild = newroot->leftchild;
if (newroot->leftchild != nullptr)//60结点的父节点指向50结点,注意60结点可能不存在
{
newroot->leftchild->parent = ptr;// 2
}
newroot->leftchild = ptr;
ptr->parent = newroot; // 3
}
右单旋转同理。
双旋:
总结:如果结点是一条斜线,则我们需要进行单旋。如果是成“折线”状,则我们需要进行两次旋转。
实现左平衡
参考代码:
// 左边平衡的调整。 ptr 指向balance为-2的结点
void LeftBalance(AVLTree& tree, AVLNode* ptr)
{ // 指向其左孩子,balance为-1或1 // 指向其左孩子的右孩子
AVLNode* leftsub = ptr->leftchild, * rightsub = nullptr;
switch (leftsub->balance)
{
case 0: // 当前平衡,无需旋转
cout << "Left balance \n"; break;
case -1: // 需要(右)单旋转
ptr->balance = 0; // 进行左旋后,ptr的balance当为0
leftsub->balance = 0; // 。。。。
RotateRight(tree, ptr);
break;
case 1: // 需要双旋转
rightsub = leftsub->rightchild;
switch (rightsub->balance)
{
case 1:
ptr->balance = 0;
leftsub->balance = -1;
break;
case 0:
ptr->balance = 0;
leftsub->balance = 0;
break;
case -1:
ptr->balance = 1;
leftsub->balance = 0;
break;
}
rightsub->balance = 0;// 该结点最终成为根节点,且平衡因子为0
RotateLeft(tree, leftsub);
RotateRight(tree, ptr);
break;
}
}
对于以上switch()中给出的几种情况,分别如下:
同样到,右子树平衡是左子树平衡的镜像操作。
void RightBalance(AVLTree& tree, AVLNode* ptr)
{
AVLNode* rightsub = ptr->rightchild, * leftsub = nullptr;
switch (rightsub->balance)
{
case 0: cout << "right balance \n"; break;
case 1:
ptr->balance = 0;
rightsub->balance = 0;
RotateLeft(tree, ptr);
break;
case -1:
leftsub = rightsub->leftchild;
switch (leftsub->balance)
{
case 1:
ptr->balance = -1;
rightsub->balance = 0;
break;
case 0:
ptr->balance = 0;
rightsub->balance = 0;
break;
case -1:
ptr->balance = 0;
rightsub->balance = 1;
break;
}
leftsub->balance = 0;
RotateRight(tree, rightsub);
RotateLeft(tree, ptr);
break;
}
}
接下来就是调整树上结点的时机了。需要调整的时机是,我们插入结点之后,进行调整。
bool Insert(AVLTree& tree, KeyType kx)
{
Adjust_AVL(tree, p); // 调整结点
return true;
}
同时,我们需要先补完调整结点的函数过程。通过不断的向父节点回溯,直至到达根节点。将沿途的结点的平衡因子修改。
void Adjust_AVL(AVLTree& tree, AVLNode* ptr)
{
AVLNode* pa = ptr->parent; // ptr为刚插入的结点
bool high = true; // ptr改变,对树的高度是否产生影响
while (high && pa != nullptr) // 向上遍历,设置平衡因子
{
if (pa->rightchild == ptr) // 在pa的右边插入ptr
{
switch (pa->balance) // 原pa的平衡因子
{
case 0: pa->balance = 1; break;
case -1:
pa->balance = 0;
high = false;
break;
case 1: // 如果ptr插入,则pa将变为2,这里可以先进行一次调整
RightBalance(tree, pa);
high = false;
break;
}
}
else // left
{
switch (pa->balance)
{
case 0: pa->balance = -1; break;
case 1:
pa->balance = 0;
high = false;
break;
case -1:
LeftBalance(tree, pa);
high = false;
break;
}
}
ptr = pa; // 向树根回溯
pa = ptr->parent;
}
}
完整代码如下:
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
typedef int KeyType;
typedef struct AVLNode
{
AVLNode* leftchild;
AVLNode* parent;
AVLNode* rightchild;
int balance; // -1 ,0 ,1
KeyType key;
}AVLNode, * AVLTree;
AVLNode* Buynode(KeyType kx)
{
AVLNode* s = (AVLNode*)malloc(sizeof(AVLNode));
if (nullptr == s) exit(1);
memset(s, 0, sizeof(AVLNode));
s->key = kx;
return s;
}
void Freenode(AVLNode* p)
{
free(p);
}
void RotateLeft(AVLTree& root, AVLNode* ptr)
{
AVLNode* newroot = ptr->rightchild;
newroot->parent = ptr->parent;// 1 新的头结点指向原先的根节点的父节点
if (ptr->parent == nullptr)// 如果ptr是整棵树的根节点,则将root更新为 新的根节点newroot
{
root = newroot;
}
else// 如果ptr原先不是根节点,则查看ptr是其父节点的左孩子还是右孩子,然后更新父节点的孩子指向
{
if (ptr->parent->leftchild == ptr)
{
ptr->parent->leftchild = newroot;
}
else
{
ptr->parent->rightchild = newroot;
}
}
ptr->rightchild = newroot->leftchild;
if (newroot->leftchild != nullptr)//60结点的父节点指向50结点,注意60结点可能不存在
{
newroot->leftchild->parent = ptr;// 2
}
newroot->leftchild = ptr;
ptr->parent = newroot; // 3
}
void RotateRight(AVLTree& root, AVLNode* ptr)
{
AVLNode* newroot = ptr->leftchild;
newroot->parent = ptr->parent;//1
if (ptr->parent == nullptr)
{
root = newroot;
}
else
{
if (ptr->parent->leftchild == ptr)
{
ptr->parent->leftchild = newroot;
}
else
{
ptr->parent->rightchild = newroot;
}
}
ptr->leftchild = newroot->rightchild;
if (newroot->rightchild != nullptr)
{
newroot->rightchild->parent = ptr;
}
newroot->rightchild = ptr;
ptr->parent = newroot;
}
// 左边平衡的调整。 ptr 指向balance为-2的结点
void LeftBalance(AVLTree& tree, AVLNode* ptr)
{ // 指向其左孩子,balance为-1或1 // 指向其左孩子的右孩子
AVLNode* leftsub = ptr->leftchild, * rightsub = nullptr;
switch (leftsub->balance)
{
case 0: // 当前平衡,无需旋转
cout << "Left balance \n"; break;
case -1: // 需要(右)单旋转
ptr->balance = 0; // 进行左旋后,ptr的balance当为0
leftsub->balance = 0; // 。。。。
RotateRight(tree, ptr);
break;
case 1: // 需要双旋转
rightsub = leftsub->rightchild;
switch (rightsub->balance)
{
case 1:
ptr->balance = 0;
leftsub->balance = -1;
break;
case 0:
ptr->balance = 0;
leftsub->balance = 0;
break;
case -1:
ptr->balance = 1;
leftsub->balance = 0;
break;
}
rightsub->balance = 0; // 该结点最终成为根节点,且平衡因子为0
RotateLeft(tree, leftsub);
RotateRight(tree, ptr);
break;
}
}
void RightBalance(AVLTree& tree, AVLNode* ptr)
{
AVLNode* rightsub = ptr->rightchild, * leftsub = nullptr;
switch (rightsub->balance)
{
case 0: cout << "right balance \n"; break;
case 1:
ptr->balance = 0;
rightsub->balance = 0;
RotateLeft(tree, ptr);
break;
case -1:
leftsub = rightsub->leftchild;
switch (leftsub->balance)
{
case 1:
ptr->balance = -1;
rightsub->balance = 0;
break;
case 0:
ptr->balance = 0;
rightsub->balance = 0;
break;
case -1:
ptr->balance = 0;
rightsub->balance = 1;
break;
}
leftsub->balance = 0;
RotateRight(tree, rightsub);
RotateLeft(tree, ptr);
break;
}
}
void Adjust_AVL(AVLTree& tree, AVLNode* ptr)
{
AVLNode* pa = ptr->parent; // ptr为刚插入的结点
bool high = true; // ptr改变,对树的高度是否产生影响
while (high && pa != nullptr) // 向上遍历,设置平衡因子
{
if (pa->rightchild == ptr) // 在pa的右边插入ptr
{
switch (pa->balance) // 原pa的平衡因子
{
case 0: pa->balance = 1; break;
case -1:
pa->balance = 0;
high = false;
break;
case 1: // 如果ptr插入,则pa将变为2,这里可以先进行一次调整
RightBalance(tree, pa);
high = false;
break;
}
}
else // left
{
switch (pa->balance)
{
case 0: pa->balance = -1; break;
case 1:
pa->balance = 0;
high = false;
break;
case -1:
LeftBalance(tree, pa);
high = false;
break;
}
}
ptr = pa; // 向树根回溯
pa = ptr->parent;
}
}
bool Insert(AVLTree& ptr, KeyType kx)
{
if (ptr == nullptr)
{
ptr = Buynode(kx);
return true;
}
AVLNode* pa = nullptr;
AVLNode* p = ptr;
while (p != nullptr && p->key != kx)
{
pa = p;
p = kx < p->key ? p->leftchild : p->rightchild;
}
if (p != nullptr && p->key == kx) return false;
p = Buynode(kx);
p->parent = pa;
if (p->key < pa->key)
{
pa->leftchild = p;
}
else
{
pa->rightchild = p;
}
Adjust_AVL(ptr, p); // 调整结点
return true;
}
void InOrder(AVLTree ptr)
{
if (ptr != nullptr)
{
InOrder(ptr->leftchild);
cout << ptr->key << " ";
InOrder(ptr->rightchild);
}
}
/* ---------------- 打印二叉树结构 ----------------------*/
// https://blog.csdn.net/weixin_41990671/article/details/108885298
#include <unordered_map>
#include <queue>
#include <string>
/**
* 中序遍历返回节点数组
* @param root 根节点
* @return 中序遍历节点数组
*/
std::vector<AVLNode*> inorderTraversal(AVLNode* root) {
std::vector<AVLNode*> res;
std::stack<AVLNode*> stk;
while (root != nullptr || !stk.empty()) {
while (root != nullptr) {
stk.push(root);
root = root->leftchild;
}
root = stk.top();
stk.pop();
res.push_back(root);
root = root->rightchild;
}
return res;
}
/**
* 利用下划线和正反斜杠打印出美观的二叉树,没有破坏二叉树结构,但传入的root会有变化
* @param root 二叉树根节点
*/
void printTree(AVLNode* root) {
if (!root)return;
auto tmp = root;
std::vector<AVLNode*> intv = inorderTraversal(tmp);//中序遍历节点数组
std::string template_str;//模板string,表示每行打印string的长度
int location = 0;
std::unordered_map<AVLNode*, int> first_locations;//存储节点对应在本行string中的首位置
for (auto& i : intv) {
location = template_str.size();
template_str += std::to_string(i->key) + " ";
first_locations[i] = location;
}
for (auto& i : template_str)i = ' ';//把模板全部置为空格方便后续使用
//层序遍历
std::queue<AVLNode*> q;
q.push(root);
while (!q.empty()) {
int currentLevelSize = q.size();
int cur_loc = 0;
std::string tmp_str1 = template_str, tmp_str2 = template_str;//1为节点所在行,2为其下一行
for (int i = 1; i <= currentLevelSize; ++i) {
auto node = q.front();
q.pop();
cur_loc = first_locations[node];
std::string num_str = std::to_string(node->key);
//左边,如果存在左孩子,那么在第二行对应位置打印'/',在第一行补上'_'
if (node->leftchild) {
q.push(node->leftchild);
int first_loc = first_locations[node->leftchild] + 1;
tmp_str2[first_loc++] = '/';
while (first_loc < cur_loc)tmp_str1[first_loc++] = '_';
}
//中间,对应位置打印节点值(有可能为多位数)
for (int j = 0; j < num_str.length(); ++j, ++cur_loc) {
tmp_str1[cur_loc] = num_str[j];
}
//右边,如果存在右孩子,那么在第二行对应位置打印'\',在第一行补上'_'
if (node->rightchild) {
q.push(node->rightchild);
int last_loc = first_locations[node->rightchild] - 1;
tmp_str2[last_loc] = '\\';
while (cur_loc < last_loc) {
tmp_str1[cur_loc++] = '_';
}
}
}
//打印两行
std::cout << tmp_str1 << std::endl;
std::cout << tmp_str2 << std::endl;
}
}
/*----------------------------------------------------*/
int main()
{
AVLTree root = nullptr;
vector<int> ar = { 53,17,78,9,45,65,87,23,81,94,88,92 };
for (auto& x : ar)
{
Insert(root, x);
}
cout << "中序遍历: { ";
InOrder(root);
cout << " }\n树型结构为:\n";
printTree(root);
cout << endl;
}
/* 输出:
中序遍历: { 9 17 23 45 53 65 78 81 87 88 92 94 }
树型结构为:
_______53_________
/ \
17___ ____87___
/ \ / \
9 _45 _78 _92
/ / \ / \
23 65 81 88 94
*/
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。