进阶算法(二) 《算法笔记》自定义实现 1、创建、遍历、二叉树 2、排序二叉树 3、AVL树 4、哈夫曼树 5、并查集

文章详细介绍了二叉树的各种操作,包括创建、遍历(先序、中序、后序、层次遍历)、查找、插入和删除。此外,还讨论了二叉排序树的特性以及平衡二叉树的概念,如AVL树的插入平衡调整。最后,提到了哈夫曼树的构造和并查集的合并查找操作。
摘要由CSDN通过智能技术生成

1、二叉树的创建

#include <cstdio>
// 二叉树
struct node
{
    int data;
    node *lchild;
    node *rchild;
};
// 建立1棵二叉树(只有1个根结点)
node *Create(int val)
{
    node *root = new node({val, nullptr, nullptr});
    return root;
}
// 查找二叉树中值为x的结点并修改其数据域
void Search(node *root, int x, int newdata)
{
    if (!root) // 树不空 找
    {
        if (root->data == x) // 找到了, 修改退出
        {
            root->data = newdata;
            return;
        }
        else // 没找到接着分别去左右子树找
        {
            Search(root->lchild, x, newdata);
            Search(root->rchild, x, newdata);
        }
    }
    else // 树为空 结束
        return;
}
// 二叉树结点的插入 //引用--操作原数据 int & a 等价 node * & root;
void Insert(node * &root, int x)
{
    //插入位置? 结点的左右子树都有可能 
    if (!root)
    {
        //插入规则未定
        Insert(root->lchild, x);
        Insert(root->rchild, x);
    }
    else
    {
        //在空位置新建1个结点
        root = new node({x, nullptr, nullptr});
        return ;//结束递归
    }
}
//二叉树的创建(1整棵非空树)
node * Create(int arr[], int n)
{
    node * root = new node;
    for (int i = 0; i < n; ++i)
        Insert(root, arr[i]);
    return root;
}

int main()
{

    return 0;
}

1、二叉树的遍历 

#include <cstdio>
#include <queue>
struct node
{
    int data;
    node *lchild;
    node *rchild;
};
// 树结点增加层次
struct Layernode
{
    int data;
    int layer;
    Layernode *lchild;
    Layernode *rchild;
};
// 先根遍历 根-左-右
void PreOrderTraverse(node *root)
{
    // 深度优先DFS 一条路走到底--不行就返回上一个(位置)函数调用
    // 树为空结束--递归出口
    // 树不为空 1 访问根结点
    //         2 先根遍历左子树
    //         3 先根遍历右子树
    if (!root)
        return;
    else
    {
        printf("%d ", root->data);
        PreOrderTraverse(root->lchild);
        PreOrderTraverse(root->rchild);
    }
}
// 中根遍历 左-根-右
void InOrderTraverse(node *root)
{
    // 深度优先搜索DFS 一条路走到底 不行就回退上一个位置(返给函数的调用者)
    // 树为空--递归边界
    // 树不空 中根遍历左子树
    //       访问根结点
    //       中根遍历右子树
    if (!root)
        return;
    else
    {
        InOrderTraverse(root->lchild);
        printf("%d ", root->data);
        InOrderTraverse(root->rchild);
    }
}
// 后根遍历 左-右-根
void PostOrderTraverse(node *root)
{
    // 深度优先搜索DFS 一条路走到底 不行就回退上一个位置(返给函数的调用者)
    // 树为空 --递归边界
    // 树不空 后根遍历左子树
    //       后根遍历右子树
    //       访问根结点
    if (!root)
        return;
    else
    {
        PostOrderTraverse(root->lchild);
        PostOrderTraverse(root->rchild);
        printf("%d ", root->data);
    }
}
// 层次遍历
void LayerOrderTraverse(node *root)
{
    // 广度优先遍历BFS 一层一层来
    // 初始化: 树的根结点入队
    // 队列非空 访问队头结点
    //         队头结点出队 前队头结点的左右孩子结点入队
    // 队列空 结束
    if (!root)
        return;
    else
    {
        std::queue<node *> q;
        node *tmp = nullptr; // 用来保存队头元素
        q.push(root);
        while (!q.empty())
        {
            tmp = q.front();
            printf("%d ", tmp->data); // 访问队头结点
            q.pop();                  // 队头结点出队
            if (tmp->lchild)
                q.push(tmp->lchild);
            if (tmp->rchild)
                q.push(tmp->rchild); // 前队头结点的左右孩子入队
        }
    }
}
// 层次遍历计算出每个结点所处的层次
void LayerOrderTraverse(Layernode *root)
{
    if (!root)
        return;
    else
    {
        // 根结点层次置1 根结点入队
        std::queue<Layernode *> q;
        Layernode *tmp = nullptr; // 用来记录队头结点
        root->layer = 1;
        q.push(root);
        while (!q.empty())
        {
            tmp = q.front();
            // 访问队头结点
            printf("data = %d layer = %d\n", tmp->data, tmp->layer);
            // 队头结点出队
            q.pop();
            // 原队头结点的左右孩子入队
            if (tmp->lchild)
            {
                tmp->lchild->layer = tmp->layer + 1;
                q.push(tmp->lchild);
            }
            if (tmp->rchild)
            {
                tmp->rchild->layer = tmp->layer + 1;
                q.push(tmp->rchild);
            }
        }
    }
}

int main()
{

    return 0;
}

 1、从先序+中序遍历序列推导二叉树

#include <cstdio>
#include <queue>
// 二叉树结点
struct node
{
    char data;
    node *lchild;
    node *rchild;
};
// 由先序序列和中序序列创建二叉树
node *Create(char pre[], int pbegin, int pend, char cur[], int cbegin, int cend)
{
    // 递归出口
    if (pbegin > pend)
        return nullptr;
    // 递归
    //  1、开辟根结点
    node *root = new node({pre[pbegin], nullptr, nullptr});
    // 查找根结点在中序数组的位置
    int i, j;
    for (i = cbegin; i <= cend; ++i)
    {
        if (cur[i] == pre[pbegin])
        {
            j = i; // 以该位置划分左右子树的位置
            break;
        }
    }
    // 2、创建根结点的左子树  左子树结点个数 = j - cbegin;
    root->lchild = Create(pre, pbegin + 1, pbegin + j - cbegin, cur, cbegin, j - 1);
    // 3、创建根结点的右子树  右子树结点个数 = cend - (j + 1);
    root->rchild = Create(pre, pbegin + j - cbegin + 1, pend, cur, j + 1, cend);
    // 二叉树创建结束
    return root;
}
// 层次遍历
void LayerOrderTraverse(node *root)
{
    // 根结点入队
    // 队列不空 访问队头结点 队头出队 原队头的左右孩子入队
    // 队列空 结束
    if (!root)
        return;
    std::queue<node *> q;
    node *tmp = nullptr;
    q.push(root);
    while (!q.empty())
    {
        tmp = q.front();
        printf("%c ", tmp->data);
        q.pop();
        if (tmp->lchild)
            q.push(tmp->lchild);
        if (tmp->rchild)
            q.push(tmp->rchild);
    }
    printf("\n");
}

int main()
{
    // 先序序列
    char pre[8] = {'A', 'B', 'D', 'E', 'C', 'F', 'G'};
    // 中序序列
    char cur[8] = {'B', 'E', 'D', 'A', 'F', 'C', 'G'};
    // 创建二叉树
    node *root = Create(pre, 0, 6, cur, 0, 6);
    // 层次遍历检查二叉树
    LayerOrderTraverse(root);

    return 0;
}

2、二叉排序树

#include <cstdio>
#include <queue>
// 二叉排序树 二叉搜索树 二叉查找树
struct node
{
    int data;
    node *lchild;
    node *rchild;
};
// 二叉排序树的查找
node *Search(node *root, int x)
{
    // 树为空 查找失败 结束
    if (!root)
        return nullptr;
    // 树不空 递归查找
    //       root->data < x 去root的左子树查找
    //       root->data > x 去root的右子树查找
    //       root->data = x 查找成功 结束
    if (root->data < x)
        Search(root->lchild, x);
    else if (root->data > x)
        Search(root->rchild, x);
    else
    {
        printf("查找成功\n");
        return root;
    }
}
// 二叉排序树结点的插入
void Insert(node *&root, int x)
{
    // 结点已经存在--不用插入
    // 插入--空结点的位置--查找失败的位置
    if (!root)
    {
        // 查找失败
        root = new node({x, nullptr, nullptr});
        return;
    }
    else
    {
        if (root->data > x)
            Insert(root->lchild, x);
        else if (root->data < x)
            Insert(root->rchild, x);
        else
            return;
    }
}
// 二叉排序树的建立
node *Create(int data[], int n)
{
    // 挨个插入
    node *root = nullptr; // 新建根结点
    for (int i = 0; i < n; ++i)
        Insert(root, data[i]); // 每个结点挨个插入
    return root;
}
// 二叉排序树结点的删除
void Del(node *&root, int x)
{
    // 找到该结点
    if (!root)
        return; // 没有该结点--删除失败
    else
    {
        if (root->data < x)
            Del(root->rchild, x); // 去右子树上寻找
        else if (root->data > x)
            Del(root->lchild, x); // 去左子树上寻找
        else                      // 找到了, 怎么释放该结点
        {
            // 找双亲结点不现实, 每一轮root都更新了 无从找起
            // 另一种删除方式, 找某个结点值代替它 释放某个结点
            node * tmp = nullptr;
            if (!root->lchild && !root->rchild)
            {
                // 该结点没有孩子结点(叶子直接置空) // 叶子最好删除
                tmp = root;
                root = nullptr;
                delete tmp;
                return ;
            }
            else if (!root->lchild)
            {
                // 该结点左子树为空时
                tmp = root;
                // 在右子树中寻找最小结点(叶子 或第一个左孩子为空的结点)取代
                node * pre = root;
                node * cur = root->rchild;
                while (cur)
                {
                    pre = cur;
                    cur = cur->lchild; // 往左一直找
                } // pre指向最小结点
                tmp->data = pre->data;
                //递归到叶子情况
                // 右子树开始继续删除
                Del(root->rchild, pre->data);
            }
            else
            {
                // 该结点右子树为空
                tmp = root;
                // 在左子树中寻找最大结点(叶子 或第一个右孩子为空的结点)取代
                node * pre = root;
                node * cur = root->lchild;
                while (cur)
                {
                    pre = cur;
                    cur = cur->rchild; // 往右一直找
                } // pre指向最大结点
                tmp->data = pre->data;
                //递归到叶子情况
                // 左子树开始继续删除 // 要反给原二叉树 用root相关的
                Del(root->lchild, pre->data);
            }
        }
    }
    
}
// 层次遍历检查
void LayerOrderTraverse(node *root)
{
    // 根结点入队
    // 队列不空 访问队头结点 队头出队 原队头的左右孩子入队
    // 队列空 结束
    if (!root)
        return;
    std::queue<node *> q;
    node *tmp = nullptr;
    q.push(root);
    while (!q.empty())
    {
        tmp = q.front();
        printf("%d ", tmp->data);
        q.pop();
        if (tmp->lchild)
            q.push(tmp->lchild);
        if (tmp->rchild)
            q.push(tmp->rchild);
    }
    printf("\n");
}

int main()
{
    // 创建1棵排序二叉树
    node *root = nullptr;
    int data[7] = {5, 3, 7, 2, 4, 6};
    root = Create(data, 6);
    LayerOrderTraverse(root);
    // 删除
    Del(root, 5);
    LayerOrderTraverse(root);

    return 0;
}

2、二叉排序树的性质-层次遍历结果有序(从小到大) 

#include <cstdio>
#include <queue>
struct node
{
    int data;
    node * lchild;
    node * rchild;
};
// 二叉排序树的插入
void Insert(node * &root, int x)
{
    if (!root)
    {
        // 插入位置(插入后成叶子结点)
        root = new node({x, nullptr, nullptr});
        return ;
    }  
    else
    {
        if (x > root->data)
            Insert(root->rchild, x); // 往右子树上插入
        else if (x < root->data)
            Insert(root->lchild, x); // 往左子树上插入
        else
            return ; // 已有该结点,不再插入
    }
}
// 创建二叉排序树
node * Create(int data[], int n)
{
    node * root = nullptr;
    for (int i = 0; i < n; ++i)
        Insert(root, data[i]);
    return root;
}
// 层次遍历(基于广度优先搜索BFS)
void LayerOrderTraverse(node *root)
{
    // 根结点入队
    // 队列不空 访问队头结点 队头出队 原队头的左右孩子入队
    // 队列空 结束
    if (!root)
        return;
    std::queue<node *> q;
    node *tmp = nullptr;
    q.push(root);
    while (!q.empty())
    {
        tmp = q.front();
        printf("%d ", tmp->data);
        q.pop();
        if (tmp->lchild)
            q.push(tmp->lchild);
        if (tmp->rchild)
            q.push(tmp->rchild);
    }
    printf("\n");
}
// 中序遍历(基于深度优先搜索DFS)
void InOrderTraverse(node * root)
{
    // 左根右
    if (!root)
        return ;
    else
    {
        InOrderTraverse(root->lchild);
        printf("%d ", root->data);
        InOrderTraverse(root->rchild);
    }
}

int main()
{
    // 二叉排序树的性质 中序遍历得到有序序列(从小到大)
    node * root = nullptr;
    int data[7] = {8, 6, 10, 5, 7, 9, 11};
    root = Create(data, 7);
    printf("层次遍历\n");
    LayerOrderTraverse(root);
    printf("中序遍历\n");
    InOrderTraverse(root);
    printf("\n");

    return 0;
}

3、平衡二叉树 

#include <cstdio>

struct node
{
    int data;
    node * lchild;
    node * rchild;
    int height; // 结点的的高度(子树总共多少层) // 用来计算平衡因子
};
// 新建1个结点
node * New_node(int x)
{
    node * root = new node({x, nullptr, nullptr, 1});
    return root;
} 
// 获取某个结点的高度
int Get_Height(node * root)
{
    if (!root)
        return 0;
    else
        return root->height; 
}
// 计算某个结点的平衡因子 = 左子树高度 - 右子树高度
int Get_Balance(node * root)
{
    return root->lchild->height - root->rchild->height;
}
// 更新某个结点的高度 = MAX(左子树高度, 右子树高度) + 1
int Update_Height(node * root)
{
    root->height = (root->lchild->height > root->rchild->height) ? 
                   root->lchild->height + 1 : root->rchild->height + 1;
    return root->height;
} 

int main()
{


    return 0;
}

3、平衡二叉树的失衡调整--转化成LL RR型失衡

#include <cstdio>
#include <math.h>
struct node
{
    int data;
    node *lchild;
    node *rchild;
    int height; // 增加结点的高度, 用来计算结点的平衡因子(左子树高度-右子树高度)
};
// 新建1个结点
node * New_Node(int x)
{
    node * tmp = new node({x, nullptr, nullptr, 0}); // 新结点没有子树=该结点高度为0
    return tmp;
}
// 获取某个结点的高度
int Get_Height(node * root)
{
    if (!root)
        return -1;
    else
        return root->height;
}
// 计算某个结点的平衡因子
int Get_Balance(node * root)
{
    return root->lchild->height - root->rchild->height; // 该结点的左右子树高度之差
}
// 更新某个结点的高度
int Update_Height(node * root)
{
    root->height = (int)fmax(root->lchild->height, root->rchild->height) + 1;
    return root->height;       
}
// 二叉平衡树的查找
void Search(node *root, int x)
{
    // 本身仍然是二叉排序树
    if (!root)
    {
        printf("没找到查找失败\n");
        return; // 树所有结点均遍历, 查找失败
    }
    else
    {
        if (root->data < x)
            Search(root->rchild, x);
        else if (root->data > x)
            Search(root->lchild, x);
        else
        {
            printf("找到了 %d\n", root->data);
            return;
        }
    }
}
// 左旋 RR型插入新结点 root < B < 新结点
void L(node * &root)
{ 
    // 1、B的原左子树成为A的右子树
    // 2、A(root)成为B的左子树
    // 3、B成为新的根结点
    node * tmp = root->rchild; // 记录B的位置
    root->rchild = tmp->lchild; // B的原左子树成为A的右子树
    tmp->lchild = root; // A成为B的左子树 // 已经完成调整
    // 更新A B结点的高度
    Update_Height(root);
    Update_Height(tmp);
    root = tmp; // B成为新的根结点
}
// 右旋 LL型插入新结点 root > B > 新结点
void R(node * &root)
{
    // 1、B的原右子树成为root的左子树
    // 2、root成为B的右子树
    // 3、B成为新的root结点
    node * tmp = root->lchild;
    root->rchild = tmp->rchild;
    tmp->rchild = root; // 已经完成调整
    // 更新A B结点的高度
    Update_Height(root);
    Update_Height(tmp);
    root = tmp; // 修改根结点的位置
}
// 二叉平衡树的插入
void Insert(node * &root, int x)
{
    // 4种插入的平衡调整策略 左旋、右旋的使用
    // 1、LL型插入新结点 root > B > 新结点 右旋
    // 2、RR型插入新结点 root < B < 新结点 左旋
    // 3、LR型插入新结点 B < 新结点 < root //先右旋 --- RR型
    // 4、RL型插入新结点 新结点 < B > root //先左旋 --- LL型
    if (!root)
    {
        // 插入的位置
        root = new node({x, nullptr, nullptr, 0});
        return ;
    }
    else
    {
        if (root->data > x)
        {
            Insert(root->lchild, x);
            Update_Height(root); // 更新树高
            if (Get_Balance(root) == 2) // 插入新结点后失衡了 LL型和LR型进行调整
            {
                if (Get_Balance(root->lchild) == 1) // LL型
                    R(root); 
                else if (Get_Balance(root->lchild) == -1) // LR型
                {
                    L(root->lchild); // 先左旋成LL型
                    R(root);
                }
            }
        }
        else
        {
            Insert(root->rchild, x);
            Update_Height(root); // 更新树高
            if (Get_Balance(root) == -2) // 插入新结点后失衡了 RR型和RL型进行调整
            {
                if (Get_Balance(root->rchild) == -1) // RR型
                    L(root);
                else if (Get_Balance(root->rchild) == 1) // RL型
                {
                    R(root->rchild); // 先右旋
                    L(root);
                }
            }
        }
    }   
}
// AVL(二叉平衡)树的建立
node * Create(int data[], int n)
{
    node * root = nullptr;
    for (int i = 0; i < n; ++i)
        Insert(root, data[i]);
    return root;
}

int main()
{

    return 0;
}

4、哈夫曼树

#include <cstdio>
// n个结点构成1片森林 F = {T1, T2, ... , Tn};(每个结点作为1棵树(只有根结点))
// 1、从森林中剔除2棵权值最小的树作为新树的左右孩子 // 贪心
// 2、新树加入森林(新树的权值 = 左右孩子权的和)
// 重复1、2、直到森林剩下1棵树(哈夫曼树)
// 选最小的2棵--小根堆--优先队列
#include <queue>
// 计算合并一堆果子消耗的最小体力
int Haffman(int arr[], int len)
{
    // 定义1个小根堆(默认是大根堆)
    std::priority_queue<int, std::vector<int>, std::greater<int> > q; // > >空1个, 避免当成 >> 右移
    // 把这堆果子入队
    int i, left, right, nval, sum = 0;
    for (i = 0; i < len; ++i)
        q.push(arr[i]);
    // 1、从队列内连续2次取出队头(出队)
    // 2、这2次的值求和赋给新数 新数加入队列(入队)
    // 重复1、2、直到队列内只剩1个元素
    while (q.size() >= 2)
    {
        left = q.top();
        q.pop();
        right = q.top();
        q.pop();
        nval = left + right;
        sum += nval;
        q.push(nval);
    }
    nval = q.top();
    q.pop();
    return sum;
}

int main()
{
    int arr[5] = {1, 2, 2, 3, 6};
    printf("%d\n", Haffman(arr, 5));

    return 0;
}

5、并查集

#include <cstdio>
#define MAXSIZE 100
// 并查集-合并查找集合
int Father[MAXSIZE];
// 顺序存树实现 int Father[N]; Father[i] = m; m结点为i结点的父结点(i m均在集合内)
// 1、合并 // 合并2个集合
// 2、查找 // 判断2个元素是否在同一个集合内
// 初始化并查集
void Init(int Father[], int n)
{
    int i;
    for (i = 0; i < n; ++i)
        Father[i] = i;
    return;
}
// 2、查找 判断元素是否在集合内 // 寻找某个结点所在集合的根结点
int FindFather(int val)
{
    // 只知道i结点的父结点--一直找前趋--根结点为止(结束递归)
    if (val == Father[val])
        return val; // val元素在集合内
    else
        return FindFather(Father[val]);
}
// 1、合并 2个不同集合合并(从2个根结点中保留1个)
void Union(int a, int b)
{
    int faA = FindFather(a); // 寻找a结点的根
    int faB = FindFather(b); // 寻找b结点的根
    if (faA != faB)          // 判断2个元素是否在同一个集合
    {
        Father[faB] = faA; // 保留a的根结点
    }
}

int main()
{

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪呈祥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值