一、机缘
成为创作者的初衷是从学习C/C++语法与数据结构过程中获得的灵感。在日常学习和项目实践中,我发现这些知识既丰富又复杂,对初学者而言尤为困难。因此,我决定通过博客记录自己的学习过程、解决思路以及代码实现,帮助更多人在C/C++的学习中少走弯路。
在写作中,特别关注:
- 语法详解:如指针与引用、面向对象编程等关键概念的讲解。
- 数据结构实现:链表、栈、队列、树等核心结构的代码演示与优化。
- 算法应用:结合数据结构的实际场景,展示如何高效解决问题。
二、收获
-
粉丝支持
- 随着文章的发布,逐渐吸引了一群热爱C/C++的读者。通过他们的反馈和交流,不断提升创作能力。
- 一些关键文章,如动态内存管理和二叉树遍历,获得了较高的阅读量和互动。
-
技术提升
- 深入学习C++高级特性,如STL(标准模板库)的使用和模板编程的优化,进一步加深了对语言的理解。
- 通过总结和写作,将学习与实践中的知识体系化,大幅提升了逻辑思维和代码能力。
-
人脉拓展
- 与许多程序员建立了联系,他们的建议和讨论对我的技术成长起到了重要作用。
- 参与了C++相关的在线社区和开源项目,开拓了视野。
三、日常
创作已经融入我的日常:
- 学习与实践结合:在日常编码中,遇到有价值的知识点或问题时,会第一时间记录,并在博客中详细讲解。
- 合理安排时间:通过设定清晰的目标和计划,将工作、学习与创作有机结合。例如,利用碎片时间阅读技术文档,晚上或周末总结成文。
- 创作的意义:通过博客,我发现不仅可以帮自己总结知识,还能为读者提供清晰的解决方案,成就感十足。
四、成就
让我感到有所成就的代码其中之一就是AVL树旋转的逻辑:
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
template <class K,class V>
struct AVLTreeNode
{
pair<K, V> _kv; //结点的每个元素类型是pair,搜索场景是key/value
int _bf; //记录平衡因子,balance factor
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent; //需要父结点的指针,方便更新平衡因子
//结点的构造函数
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0) //新结点的平衡因子都是0(因为它的左右子树均为空树,高度都是0,相减也为0)
{}
};
template <class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
//右单旋(左子树高度高于右子树),结合抽象图分析代码逻辑
void RotateR(Node* parent) //parent就是旋转点
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* pParent = parent->_parent; //记录parent->_parent修改前的内容
parent->_left = subLR; //核心代码
subL->_right = parent; //核心代码
if(subLR) //subLR可能为空,需要判断
subLR->_parent = parent; //需要修改subLR的父结点指针
parent->_parent = subL;
//旋转点可能是整棵树的根结点,那么需要更新根
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
//旋转点也可能是局部子树的根结点,那么需要进行链接操作
else
{
if (pParent->_left == parent)
pParent->_left = subL;
else
pParent->_right = subL;
subL->_parent = pParent;
}
//至此,旋转逻辑完毕,接下来需要更新平衡因子
subL->_bf = 0;
parent->_bf = 0;
}
//左单旋(右子树高度高于左子树),结合抽象图分析代码逻辑
void RotateL(Node* parent) //parent就是旋转点
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* pParent = parent->_parent; //记录parent->_parent修改前的内容
parent->_right = subRL; //核心代码
subR->_left = parent; //核心代码
if (subRL) //subRL可能为空,需要判断
subRL->_parent = parent; //需要修改subRL的父结点指针
parent->_parent = subR;
//旋转点可能是整棵树的根结点,那么需要更新根
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
//旋转点也可能是局部子树的根结点,那么需要进行链接操作
else
{
if (pParent->_left == parent)
pParent->_left = subR;
else
pParent->_right = subR;
subR->_parent = pParent;
}
//至此,旋转逻辑完毕,接下来需要更新平衡因子
subR->_bf = 0;
parent->_bf = 0;
}
//左右双旋,结合图例分析代码逻辑
void RotateLR(Node* parent)
{
//因为单旋会调整平衡因子,所以需要提前记录,防止丢失
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left); //复用左单旋
RotateR(parent); //复用右单旋
//场景1
if (bf == -1)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 1;
}
//场景2
else if (bf == 1)
{
subLR->_bf = 0;
subL->_bf = -1;
parent->_bf = 0;
}
//场景3
else if (bf == 0)
{
subLR->_bf = 0;
subL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);//中断程序,说明平衡因子出现问题了,这时候就需要检查我们写的代码
}
}
//右左双旋,结合图例分析代码逻辑
void RotateRL(Node* parent)
{
//因为单旋会调整平衡因子,所以需要提前记录,防止丢失
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
//场景1
if (bf == -1)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
//场景2
else if (bf == 1)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
//场景3
else if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);//中断程序,说明平衡因子出现问题了,这时候就需要检查我们写的代码
}
}
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv); //根结点的_parent指针为nullptr
return true;
}
Node* parent = nullptr; //注意这里的parent是结点的父结点,不是结点中指向父结点的指针
Node* cur = _root;
while (cur) //利用循环找到合适的位置进行插入
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
//不允许插入相等的值
return false;
}
}
//插入新结点
cur= new Node(kv); //创建新结点
if (parent->_kv.first < kv.first) //比较的是key值,value值不参与比较
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
//链接父亲
cur->_parent = parent;
//插入结点结束后还要控制平衡
//更新平衡因子,沿着cur和parent向上更新
while (parent) //保证parent不为空,如果parent为空就更新结束了,此时cur就是根结点
{
if (cur == parent->_left) //parent的平衡因子需要--
parent->_bf--;
else //parent的平衡因子需要++
parent->_bf++;
//对应更新停止的第一种情况
if (parent->_bf == 0)
{
break;
}
//对应更新停止的第二种情况
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->_parent;
}
//对应更新停止的第三种情况,这里写else if而不写else是为了防止插入前不是AVL树的情况
else if (parent->_bf == 2 || parent->_bf == -2)
{
//这里就需要写旋转的逻辑,旋转的部分我们单独讲解
//...
if (parent->_bf == -2 && cur->_bf == -1) //parent和cur都满足左边高右边低,右单旋
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1)//parent和cur都满足右边高左边低,左单旋
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1) //左右双旋
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1) //右左双旋
{
RotateRL(parent);
}
else
{
assert(false);
}
break;
}
else
{
assert(false);//如果插入前不是AVL树,就中断程序,说明平衡因子出现问题了,这时候就需要检查我们写的代码
}
}
return true; //控制平衡后,就插入成功了,放回true
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
int Height()
{
return _Height(_root);
}
int Size()
{
return _Size(_root);
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
int _Size(Node* root)
{
if (root == nullptr)
return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
bool _IsBalanceTree(Node* root)
{
// 空树也是AVL树
if (nullptr == root)
return true;
// 计算pRoot结点的平衡因子:即pRoot左右子树的高度差
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
// pRoot平衡因子的绝对值超过1,则一定不是AVL树
if (abs(diff) >= 2)
{
cout << root->_kv.first << "高度差异常" << endl;
return false;
}
if (root->_bf != diff)
{
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
// pRoot的左和右如果都是AVL树,则该树一定是AVL树
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
private:
Node* _root = nullptr; //只需记录树的根节点
};
五、憧憬
- 职业目标
- 希望未来能成为一名算法工程师,专注于高效算法的设计与实现,并将编程能力应用到实际项目中。
- 创作规划
- 持续更新高质量内容,深入讲解C++ STL应用、设计模式与多线程编程等话题。
- 开展系列教程,比如“零基础学习C++”或“算法竞赛实战”,通过图文并茂的方式让更多人受益。
- 长期目标
- 探索更多学习和分享的形式,比如制作视频教程、参与线下分享会,让技术创作产生更大的影响力。
通过不断努力,希望能够把技术与分享结合得更紧密,让创作成为推动技术进步的一部分。