数据结构与算法·第6章【树】

news2024/11/28 14:11:04

基本操作

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

树的相关定义

树的深度(高度):树中叶子结点所在的最大层次

森林: m m m棵互不相交的树的集合

二叉树

二叉树或为空树,或是由一个根结点加上两棵分别称为左子树和右子树的、互不交的二叉树组成。

性质

  • 二叉树,第 i i i层上至多有 2 i − 1 2^{i-1} 2i1的结点( i ≥ 1 i≥1 i1这个性质比较好理解,因为第i层是由i-1层引出的,再以此递归到根结点就很好理解
  • 深度为 k k k 的二叉树上至多含 2 k − 1 2^k-1 2k1 个结点( k ≥ 1 k≥1 k1这个也很好理解,先把树想象成一棵满二叉树,因为根结点那层只有一个结点,所以减一即可
  • 对任何一棵二叉树,若它含有 n 0 n_0 n0个叶子结点、 n 2 n_2 n2 个度为 2 2 2 的结点,则必存在关系式: n 0 = n 2 + 1 n_0 = n_2+1 n0=n2+1
  • 具有 n 个结点的完全二叉树的深度为 l o g 2 n + 1 log_2n+1 log2n+1 用第2条性质很好理解
  • 对于含有 n n n 个节点的完全二叉树,我们可以按照从上到下、从左到右的顺序为每个节点进行编号,从 1 1 1 n n n。对于任意一个编号为 i i i 的节点:
    1. 如果 i = 1 i=1 i=1 ,那么该节点就是整棵二叉树的根节点,它没有父节点;否则,它的父节点的编号为 ⌊ i / 2 ⌋ \lfloor i/2 \rfloor i/2
    2. 如果 2 i > n 2i>n 2i>n,那么编号为 i i i 的节点没有左孩子,否则,它的左孩子的编号为 2 i 2i 2i
    3. 如果 2 i + 1 > n 2i+1>n 2i+1>n,那么编号为 i i i 的节点没有右孩子,否则,它的右孩子的编号为 2 i + 1 2i+1 2i+1

这些规则的推论可以帮助我们更好地理解完全二叉树的结构,而且可以方便地进行完全二叉树节点的查找和遍历。

完全二叉树:

完全二叉树是一种特殊的二叉树,它与普通的二叉树区别在于它的层数,及对于第 i i i 层,如果该层的节点没有填满,则其所有节点都必须集中在左侧连续位置上。也就是说,完全二叉树中除最后一层外,其他层的节点数都达到了最大值,且最后一层的节点都集中在该层最左边的若干位置上。

存储结构

顺序存储

#define MAX_TREE_SIZE 100       // 二叉树的最大结点数

typedef int TElemType;          // 定义元素类型为整型

typedef struct BiTNode {
    TElemType data;             // 数据域
    struct BiTNode *lchild, *rchild; // 左右孩子指针
} BiTNode, *BiTree;            // 二叉链表结点定义和二叉树定义

BiTree bt;                     // 定义一个指向二叉树根节点的指针

二叉链表

// 定义二叉树结点和指向二叉树结点的指针类型
typedef struct BiTNode {
    TElemType data;             // 数据域
    struct BiTNode *lchild, *rchild; // 左右孩子指针
} BiTNode, *BiTree;

示意图

lchilddatarchild

三叉链表

// 定义三叉链表结点和指向三叉链表结点的指针类型
typedef struct TriTNode {
    TElemType data;             // 数据域
    struct TriTNode *lchild, *rchild; // 左右孩子指针
    struct TriTNode *parent;    // 双亲指针
} TriTNode, *TriTree;

根节点的 p a r e n t parent parent为NULL

parentlchilddatarchild

二叉树遍历

  • 先序遍历根左右
  • 中序遍历左根右
  • 后序遍历左右根

先序遍历算法比较简单,我就不放了

中序遍历:

//采用非递归的方式,中序遍历以T为根指针的二叉树
Status InOrderTraverse(BiTree T, Status (*Visit)(TElemType e)){
    SqStack S;  //定义一个栈 S 来存储节点。
    InitStack(&S);  //初始化栈
    
    BiTree p = T;  //p为遍历指针,初始时指向根结点
    
    while (p || !StackEmpty(S)){   //p非空或者栈不为空
        if (p){   //如果p非空
            Push(&S, p);   //节点入栈
            p = p->lchild;   //遍历左子树
        }
        else{  //如果p为空
            Pop(&S, &p);   //取出栈顶元素
            if (!Visit(p->data)) return ERROR;  //访问栈顶元素
            p = p->rchild;   //遍历右子树
        }   
    }
    return OK;
}

创建二叉树

//按照前序遍历方式,创建一棵二叉树,并返回 OK
Status CreateBiTree(BiTree &T) {
    char ch;  //定义一个字符类型的变量 ch 用于输入二叉树结点的值
    scanf("%c", &ch);  //从用户输入中读取下一个字符,即当前结点的值
    
    if (ch == ' ') T = NULL;  //如果输入的是空格,则表示该结点为空结点
    else {   //否则,生成一个新的二叉树结点,并赋值为 ch
        if (!(T = (BiTNode *)malloc(sizeof(BiTNode))))
            exit(OVERFLOW);  //若生成结点失败,则退出程序
        T->data = ch;  //为当前结点赋值
        CreateBiTree(T->lchild);  //递归调用 CreateBiTree 函数构造左子树
        CreateBiTree(T->rchild);  //递归调用 CreateBiTree 函数构造右子树
    }
    return OK;  //返回 OK 表示创建成功
}

由先序和中序遍历构建二叉树:

在这里插入图片描述

由后序和中序构建:

在这里插入图片描述

线索二叉树

线索二叉树是在二叉树的基础上增加了线索信息的一种数据结构。线索二叉树的每个结点除了指向左右子树的指针外,还有两个特殊指针,分别称为前驱线索和后继线索指针,用来记录该结点在中序遍历中的前驱和后继结点。

在线索二叉树中,如果一个结点没有左子树,则将其左子树指针指向该结点在中序遍历中的前驱结点;如果一个结点没有右子树,则将其右子树指针指向该结点在中序遍历中的后继结点。当然并非一定中序遍历,只是书上的内容多是中序遍历

线索化过程即为将二叉树转换成线索二叉树的过程。线索化的方法主要有以下两种:

  • 中序遍历线索化:对于给定的二叉树,中序遍历得到的结点序列中,每个结点都有唯一的前驱和后继。通过修改二叉树中的指针,使其变成一个线性的结构,同时保留原有的中序遍历序列,即可得到该二叉树的线索二叉树。
  • 先序遍历线索化:与中序遍历类似,只是遍历顺序不同。

线索二叉树的主要优点是可以加快对二叉树的遍历操作,同时节省存储空间。缺点是线索化过程需要额外的时间开销,并且增加了代码的复杂度。

typedef enum { Link, Thread } PointerThr;  // Link==0:指针,Thread==1:线索

typedef struct BiThrNode {
    TElemType         data;      // 结点数据
    struct BiThrNode *lchild;    // 左子树指针
    struct BiThrNode *rchild;    // 右子树指针
    PointerThr        LTag;      // 左标志
    PointerThr        RTag;      // 右标志
} BiThrNode, *BiThrTree;

遍历算法

void InOrderTraverse_Thr(BiThrTree T, void (*Visit)(TElemType e)) {
    BiThrTree p = T->lchild;   // p指向根结点的左子树
    while (p != T) {           // 空树或遍历结束时,p==T
        while (p->LTag == Link) p = p->lchild;  // 如果左标志为指针,则p进至其左子树的最左下结点
        if (!Visit(p->data)) return;            // 访问该结点,如果访问失败则返回错误
        while (p->RTag == Thread && p->rchild != T) {
            p = p->rchild;  Visit(p->data);      // 循环访问后继结点,直到遇到右标志为指针的结点
        }
        p = p->rchild;          // 如果右标志为指针,则p进至其右子树的根结点
    }
}

仔细看看吧,第一次看的时候还有点不理解

建立线索链表

Status InOrderThreading(BiThrTree &Thrt, BiThrTree T) {
    if (!T) {  // 如果二叉树为空,则创建头结点并将左右指针都指向自身
        if (!(Thrt = (BiThrTree)malloc(sizeof(BiThrNode)))) {
            exit(OVERFLOW);
        }
        Thrt->LTag = Link;
        Thrt->RTag = Thread;
        Thrt->lchild = Thrt;
        Thrt->rchild = Thrt;
        return OK;
    }

    // 否则,创建头结点,并将左指针指向根节点,右指针指向尾节点
    if (!(Thrt = (BiThrTree)malloc(sizeof(BiThrNode)))) {
        exit(OVERFLOW);
    }
    Thrt->LTag = Link;
    Thrt->RTag = Thread;
    Thrt->rchild = Thrt;

    pre = Thrt;  // 初始化前驱节点,方便遍历时标记前驱线索

    Thrt->lchild = T;  // 左指针指向根节点
    InThreading(T);  // 中序遍历二叉树并进行线索化

    pre->rchild = Thrt;  // 将最后一个节点的右子树指针指向头结点
    pre->RTag = Thread;
    Thrt->rchild = pre;  // 将头结点的右指针指向最后一个节点

    return OK;
}

首先判断输入的二叉树是否为空。如果为空,则创建一个头结点,并将左右指针都指向自身。否则,我们创建一个头结点将其左指针指向根节点,将其右指针指向尾节点

接下来,我们初始化前驱节点 pre,并将其指向头结点,以便遍历二叉树时能够方便地标记前驱线索。然后,我们将头结点的左指针指向根节点,调用函数 InThreading 对二叉树进行中序线索化。

在 InThreading 函数返回后,我们需要找到最后一个节点,并将它的右子树指针指向头结点。我们使用变量 pre 来保存当前遍历过的最后一个节点,在循环中不断更新,直到遍历到最后一个节点。最后,我们将最后一个节点的右子树指针指向头结点,并将头结点的右指针指向最后一个节点。

需要注意的是,在整个过程中,我们只需要通过修改线索来实现中序遍历,无需创建新的节点或者修改原有节点的结构。这就是中序线索二叉树的优点,它可以大大提高中序遍历的效率,减少程序的内存占用。

这个算法的具体实现还是有点复杂的

In Threading函数

void InThreading(BiThrTree p, BiThrTree &pre) {
    if (!p) {  // 如果当前节点为空,则直接返回
        return;
    }
    // 对左子树进行线索化
    InThreading(p->lchild, pre);
    // 建立前驱线索
    if (!p->lchild) {
        p->LTag = Thread;
        p->lchild = pre;
    }
    // 建立后继线索
    if (pre && !pre->rchild) {
        pre->RTag = Thread;
        pre->rchild = p;
    }
    pre = p;  // 确定当前节点的前驱节点
    // 对右子树进行线索化
    InThreading(p->rchild, pre);
}

森林与树

表示法

双亲表示法
在这里插入图片描述

孩子链表表示法
在这里插入图片描述
比双亲表示法多了一个存储孩子的域空间

树的二叉链表(孩子-兄弟)表示法
在这里插入图片描述

左孩子-右兄弟

二叉树与森林的转换

在这里插入图片描述

遍历

树的遍历:

在这里插入图片描述

层次遍历——BFS

森林的遍历:
在这里插入图片描述

  1. 若森林不空,则可按下述规则遍历之:(先序遍历)
    (1)访问森林中第一棵树的根结点;
    (2)先序遍历森林中第一棵树的子树森林;
    (3)先序遍历森林中(除第一棵树之外)其余树构成的森林。

  2. 若森林不空,则可按下述规则遍历之:(中序遍历)
    (1)中序遍历森林中第一棵树的子树森林;
    (2)访问森林中第一棵树的根结点;
    (3)中序遍历森林中(除第一棵树之外)其余树构成的森林。

在这里插入图片描述

树的存储结构

typedef struct CSNode {
    Elem data;
    struct CSNode *left, *right;  // 左儿子和右兄弟指针
} CSNode, *CSTree;

左孩子-右兄弟

常见应用算法

求树的深度

int TreeDepth(CSTree T) {
    if (!T) {
        return 0;
    } else {
        int h1 = TreeDepth(T->left);     // 递归求左子树深度
        int h2 = TreeDepth(T->right);    // 递归求右子树深度
        return (h1 > h2 ? h1 : h2) + 1;  // 返回深度较大的子树深度加1
    }
} // TreeDepth

求从根到所有叶子的路径

void AllPath(Bitree T, Stack &S) {
    if (T) {
        Push(S, T->data);
        if (!T->left && !T->right) {
            PrintStack(S);
        } else {
            AllPath(T->left, S);
            AllPath(T->right, S);
        }
        Pop(S);
    }
} // AllPath

Huffman

结点的路径长度:

在这里插入图片描述

树的路径长度:从树根到每个结点的路径长度之和

结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积。

树的带权路径长度:树中所有叶子结点的带权路径长度之和。记作: WPL(T) = ∑wklk (对所有叶子结点)。

构造最优二叉树的一种算法——huffman算法

霍夫曼算法的实现过程如下:

  1. 计算每个字符在文本中出现的频率;
  2. 将所有字符和其频率作为叶子节点构建一棵二叉树,其中节点的权重值为该节点代表的字符的频率;
  3. 对于这棵二叉树,从中选择两个权重值最小的节点作为左右子树,将它们的权重值相加并作为新节点的权重值;
  4. 将这个新节点作为子树的根节点,并将它插入到二叉树中原来两个被选中的节点所在的位置;
  5. 重复步骤 3 和 4 直到整棵树变成一个节点,这个节点就是霍夫曼树的根节点;
  6. 对于每个字符,从根节点开始,如果字符在左子树中,则输出0,否则输出1,直到找到对应的叶子节点;
  7. 将所有字符的编码串连接起来,就是压缩后的数据。

huffman结构体

typedef struct {
    unsigned int weight;
    unsigned int parent;
    unsigned int lchild;
    unsigned int rchild;
} HTNode, *HuffmanTree;

huffman编码

void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n) {
    // w 存放n个字符的权值(均>0),构造哈夫曼树HT,并求出n个字符的哈夫曼编码HC。
    if (n <= 1) return;
    int m = 2 * n - 1; // 计算哈夫曼树节点数
    HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode)); // 动态分配内存
    // 初始化叶子节点
    for (int i = 1; i <= n; ++i) {
        HT[i].weight = w[i-1];
        HT[i].parent = 0;
        HT[i].lchild = 0;
        HT[i].rchild = 0;
    }
    // 初始化内部节点
    for (int i = n+1; i <= m; ++i) {
        HT[i].weight = 0;
        HT[i].parent = 0;
        HT[i].lchild = 0;
        HT[i].rchild = 0;
    }
    // 建立哈夫曼树
    for (int i = n+1; i <= m; ++i) { 
        // 在HT[1..i-1]选择parent为0且weight最小的两个结点,其序号分别为s1和s2。
        Select(HT, i-1, s1, s2);
        HT[s1].parent = i;
        HT[s2].parent = i;
        HT[i].lchild = s1;
        HT[i].rchild = s2;
        HT[i].weight = HT[s1].weight + HT[s2].weight;
    }
    // 分配编码头指针向量
    HC = (HuffmanCode)malloc((n+1) * sizeof(char*)); 
    // 分配求编码的工作空间
    cd = (char*)malloc(n * sizeof(char));    
    cd[n-1] = '\0'; // 编码结束符
    // 逐个字符求编码并保存到HC中
    for (int i = 1; i <= n; ++i) {   
        int start = n-1; // 编码结束符位置
        int c = i, f = HT[i].parent;
        // 从叶子到根逆向求编码
        while (f != 0) {
            if (HT[f].lchild == c) {
                cd[--start] = '0';
            }
            else {
                cd[--start] = '1';
            }
            c = f;
            f = HT[f].parent;
        }
        HC[i] = (char*)malloc((n-start) * sizeof(char));
        // 为第i个字符编码分配空间
        strcpy(HC[i], &cd[start]); // 从 cd 复制编码串到 HC
    }
    free(cd); //释放工作空间
}

这个有点复杂,其中编码部分可以用更好理解的递归来解决

void generateHuffmanCodes(HuffmanNode* root, string code) {
	if (!root) return;
	if (root->left == nullptr && root->right == nullptr) codes[root->ch] = code;
	generateHuffmanCodes(root->left, code + "0"); 
	generateHuffmanCodes(root->right, code + "1"); 
}

习题

相似

若已知两棵二叉树 B 1 B1 B1 B 2 B2 B2 皆为空,或者皆不空且 B 1 B1 B1 的左、右子树和 B 2 B2 B2的左、右子树分别相似,则称二叉树 B 1 B1 B1 B 2 B2 B2 相似。试编写算法,判别给定两棵二叉树是否相似。
在这里插入图片描述
比较简单,但不妨看看

栈实现后序遍历

写出后序遍历的非递归算法(提示:为分辨后序遍历时两次进栈的不同返回点,需在指针进栈时同时将一个标志进栈)。

在这里插入图片描述
仔细看看

层次遍历

编写按层次顺序(同一层自左至右)遍历二又树的算法。

在这里插入图片描述

判断完全二叉树

bool full(tree t) {
    init_queue(q);
    int flag = 0;
    enqueue(q, t);
    while (!queue_empty(q)) {
        dequeue(q.p);
        if (!p) {
            flag = 1;
        } else if (flag) {
            return 0;
        } else {
            enqueue(q, p->lchild);
            enqueue(q, p->rchild);
        }
    }
    return 1;
}

求孩子-兄弟链的深度

typedef struct TreeNode * Tree;
int depth(Tree root) {
    if (!root) {
        return 0; // 如果节点为空,深度为 0
    }
    int maxDepth = 0;
    for (Tree p = root->firstChild; p; p = p->rightSibling) {
        int d = depth(p); // 递归计算子节点的深度
        if (d > maxDepth) {
            maxDepth = d; // 记录最大深度
        }
    }
    return maxDepth + 1; // 当前节点的深度等于最深子节点的深度加上 1
}

Tree p = root->firstChild; p; p = p->rightSibling
这一条代码可以好好看看

依据前序序列和中序序列构建二叉树(二叉链表)

Tree buildTree(int preorder[], int inorder[], int n) {
    if (n == 0) {
        return NULL; // 当序列为空时,返回空指针
    }
    // 在前序序列中找到根节点
    int root_val = preorder[0];
    int i;
    for (i = 0; i < n; i++) {
        if (inorder[i] == root_val) {
            break;
        }
    }
    // 根据中序序列划分左右子树
    int left_size = i;
    int right_size = n - i - 1;
    // 递归构建左右子树
    Tree root = (Tree) malloc(sizeof(struct TreeNode));
    root->val = root_val;
    root->left = buildTree(preorder + 1, inorder, left_size);
    root->right = buildTree(preorder + 1 + left_size, inorder + 1 + left_size, right_size);
    return root;
}

依据递归解决即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/620207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaCV音视频开发宝典:使用JavaCV读取海康平台或海康网络摄像头sdk回调视频TS码流并解析预览图像

《JavaCV音视频开发宝典》专栏目录导航 《JavaCV音视频开发宝典》专栏介绍和目录 ​ 前言 两年前博主写了如何利用JavaCV解析各种h264裸流,《JavaCV音视频开发宝典:使用javacv读取GB28181、海康大华平台和网络摄像头sdk回调视频码流并解析预览图像》,但是随着时间变化,各…

企业邮箱如何设置邮件审核

有的时候对外给客户或合作伙伴等发送邮件需要领导审核后再发&#xff0c;以前都是先发给领导&#xff0c;领导审核以后再重新发给客户&#xff0c; 这样的流程太过繁琐。 新的邮件审核功能既能满足审核需求&#xff0c;又避免了重复发信&#xff0c;可以极大提高工作效率。 设…

使用VitePress静态网站生成器创建组件库文档网站并部署到GitHub

Vue3TSVite开发组件库并发布到npm 网站在线预览&#xff1a; Vue Amazing UI | Amazing UI Components LibraryAmazing UI 组件库https://themusecatcher.github.io/vue-amazing-ui/参考文档&#xff1a; VitePress 目录 一、安装依赖及配置 1、安装 vitepress 2、在 p…

想要精通算法和SQL的成长之路 - 反转链表

想要精通算法和SQL的成长之路 - 反转链表 前言一. 反转链表 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 反转链表 原题链接 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 思路如下&#xff1a; 我们可以通过一次遍历&#xff…

该怎么用设计测试用例测网上银行转账?

目录 前言 1、网上银行转账是怎么测的&#xff0c;设计一下测试用例。 回答思路&#xff1a; 2、测试工作的流程?缺陷状态有什么?设计测试用例有几种方法&#xff1f; 修改完以后&#xff0c;有两种处理情况&#xff1a; 3、在项目中找到的经典BUG是什么&#xff1f; 4、定期…

kali系统渗透

用kali远程ssh时&#xff0c;如果不能的话 按照以下方法 ┌──(root㉿kali)-[~]└─# mkdir ~/.ssh┌──(root㉿kali)-[~]└─# vim ~/.ssh/configHost *HostkeyAlgorithms ssh-rsaPubkeyAcceptedKeyTypes ssh-rsa msfconlole -q //进入马上发不显示内容&#xff0c;所以加…

编程实现人脸识别

1.更改url 用翔云平台下的人脸识别的API文档 把他贴到url中&#xff1a; 2.定义参数 &#xff08;根据平台给定的这些串口接收参数来定义&#xff09; key和secret在这里找&#xff1a; 3.指定post内容 把这部分定义成一个postString 4.字符串的拼接 sprintf函数调用的主…

使用粒子群优化算法(PSO)辨识锂电池二阶RC模型参数(附MATLAB代码)

目录 一、原理部分 二、代码详解部分 三、结果及分析 一、原理部分 PSO算法由美国学者于 1995 年提出&#xff0c;因其算法简单、效果良好&#xff0c;而在很多领域得到了广泛应用。该算法的起源是模拟鸟群的觅食过程&#xff0c;形成一种群体智能搜索算法。 其核心是&#…

使用Flask高效构建Web应用

1、聊聊Flask框架 Flask官方文档 Flask是Armin ronacher基于Python开发的微型Web框架&#xff0c;诞生于2010年&#xff0c;它依赖于jinja2模板和Werkzeug WSGI服务。Flask的核心简单易于扩展&#xff0c;它不会替你做出太多决策比如使用何种数据库或模板引擎&#xff0c;这些都…

抖音seo矩阵系统源码解析

抖音SEO矩阵系统源码是一种用于优化抖音视频内容的工具&#xff0c;可以帮助用户提高抖音视频的搜索排名和流量&#xff0c;从而增加视频曝光和转化率。该系统包括两部分&#xff0c;即数据收集和分析模块以及SEO策略和实施模块。 数据收集和分析模块主要负责从抖音平台上收集…

认识功能安全

01、什么是功能安全 1-背景简介 由于汽车的复杂性&#xff0c;整个行业正在致力于提供符合安全要求的零部件系统。比如&#xff0c;线控油门系统&#xff0c;当驾驶员踩下油门踏板&#xff0c;踏板上的传感器向控制器发送信号时&#xff0c;控制器会综合分析如发动机转速、车…

ERP系统实施,8个需要了解的关键绩效指标

实施ERP系统是一项复杂的、耗时的工作&#xff0c;但它也代表了企业重新思考其战略、目标和流程的绝佳机会。成功的ERP实施可以激发创新&#xff0c;改善企业的所有领域。例如&#xff0c;通过集中企业各部门不断增长的财务和业务数据&#xff0c;所有的决策者都在同一个页面上…

非煤矿山电子封条系统算法方案 opencv

非煤矿山电子封条系统算法部署方案是基于pythonopencv网络模型Ai视频图像识别技术&#xff0c;非煤矿山电子封条系统算法部署方案对出入井人员、人员变化及非煤矿山生产作业状态等状况&#xff0c;及时发现处理异常动态将自动发出警报。OpenCV的全称是Open Source Computer Vis…

亲身经历告诉你,0基础转行学Python,毕业就能15000

对生活现状的不满&#xff0c;对自己浑浑噩噩的厌恶&#xff0c;以及对互联网行业的憧憬&#xff0c;让我下定决心选择去黑马程序员开启转行之旅。 学科 | Python大数据开发 薪资 | 15000 时间总是在你回忆往事时才会觉得短暂&#xff0c;来黑马之前觉得六个月将是一段很漫长…

操作系统原理 —— 内存动态分区分配算法(二十一)

在上一个章节我们讲了内存连续分配的几种方式&#xff0c;有单一、固定、动态这三种&#xff0c;在固定、动态这种里面&#xff0c;操作系统会记录空闲分区表&#xff0c;这个表是用来记录当前空闲的内存。 那么在之后有新的进程装入内存&#xff0c;需要从空闲分区表中找到一…

从零手写操作系统之RVOS内存管理模块简单实现-02

从零手写操作系统之RVOS内存管理模块简单实现-02 内存管理分类内存映射表&#xff08;Memory Map)Linker Script 链接脚本语法基于符号定义获取程序运行时内存分布 基于 Page 实现动态内存分配代码讲解调试 扩展 本系列参考: 学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 整…

qrcode.min.js下载

目录 qrcode.min.js下载步骤&#xff1a; 去官网 下载后解压&#xff1a; 如下&#xff1a;就可以得到 qrcode.min.js文件了 qrcode.min.js下载步骤&#xff1a; 去官网 可以前往qrcode官网&#xff08;https://davidshimjs.github.io/qrcodejs/&#xff09;下载qrcode.m…

培训pytorch(未完善) bp算法原理

生物的神经元 接受外部的刺激 神经网络工作流程演示

MySQL高可用集群解决方案之:MySql Cluster搭建

MySQL是当前使用最广泛的关系型数据库管理系统之一&#xff0c;但MySQL在高并发访问和大量数据处理方面存在较为明显的性能瓶颈。为了解决MySQL单点故障带来的不便和可扩展性问题&#xff0c;我们需要构建稳定性极高的MySQL集群方案。本文将介绍一种快速搭建MySQL集群的解决方案…

机器学习聚类算法——BIRCH算法、DBSCAN算法、OPTICS算法

系列文章目录 前言 本文主要介绍BIRCH算法、DBSCAN算法、OPTICS算法&#xff0c;以及相关案例举例&#xff0c;以下案例仅供参考 一、BIRCH算法 1.1、BIRCH算法简介 BIRCH&#xff08;Balanced Iterative Reducing and Clustering using Hierarchies&#xff0c;平衡迭代规约…