在这里插入图片描述

虽然今天不是植树节,但是我今天想种树。

文章目录

    • 树,什么是树?
    • 二叉树
      • 定义
      • 二叉树的创建
      • 二叉树的前中后序遍历
        • 前序遍历:
        • 中序遍历
        • 后序遍历
        • 已知前序、中序遍历结果,还原二叉树
        • 已知后序、中序遍历结果,还原二叉树
      • 二叉树的层序遍历
    • 二叉搜索树
      • 二叉搜索树是什么?
      • 构造二叉搜索树
      • 代码实现:
    • 平衡二叉搜索树(AVL树)
      • 什么是平衡二叉搜索树?
      • AVL树的节点数据结构
      • AVL树构造
        • 左旋
        • 右旋
        • 双旋转
      • 新增节点(背多分)
        • LL(右旋)
        • RR(左旋)
        • LR
        • RL
        • 插入节点
        • 删除节点
    • 红黑树
      • 1、红黑树?长什么果实吗
      • 2、红黑树的节点设计
      • 3、 红黑树的数据结构
      • 4、红黑树插入节点
        • 4.1 元素插入操作(insert_equal())
        • 4.2 元素插入操作(insert_unique())
        • 4.3 插入的幕后黑手(__insert())
      • 5、调整红黑树
        • 旋转与改变颜色
      • 6、红黑树工作流程图
      • 7、红黑树图示
    • 哈夫曼树
      • 什么是哈夫曼树
      • 哈夫曼树构造步骤
      • 代码
    • 浅谈多路查找树(B树)
      • 2-3树
        • 2-3树的插入
        • 2-3树的删除
      • B树
      • B树的典型应用
    • 树与森林
      • 树转换为二叉树
      • 森林转换为二叉树
      • 二叉树转换为树
      • 二叉树转换为森林
    • 写在最后

树,什么是树?

树是我们计算机中非常重要的一种数据结构,同时使用树这种数据结构,可以描述现实生活中的很多事物,例如家
谱、单位的组织架构、等等。
树是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就
是说它是根朝上,而叶朝下的。

喏,看:
在这里插入图片描述
树具有以下特点:

 1.每个结点有零个或多个子结点;2.没有父结点的结点为根结点;3.每一个非根结点只有一个父结点;4.每个结点及其后代结点整体上可以看做是一棵树,称为当前结点的父结点的一个子树;

树的相关术语(图中那些英文):

  1. 结点的度:一个结点含有的子树的个数称为该结点的度;
  2. 叶结点:度为0的结点称为叶结点,也可以叫做终端结点;
  3. 分支结点:度不为0的结点称为分支结点,也可以叫做非终端结点;
  4. 结点的层次:从根结点开始,根结点的层次为1,根的直接后继层次为2,以此类推;
  5. 结点的层序编号:将树中的结点,按照从上层到下层,同层从左到右的次序排成一个线性序列,把他们编成连续的自然数;
  6. 树的度:树中所有结点的度的最大值;
  7. 树的高度(深度):树中结点的最大层次;
  8. 森林:m(m>=0)个互不相交的树的集合,将一颗非空树的根结点删去,树就变成一个森林;给森林增加一个统一的根结点,森林就变成一棵树;
  9. 孩子结点:一个结点的直接后继结点称为该结点的孩子结点;
  10. 双亲结点(父结点):一个结点的直接前驱称为该结点的双亲结点;
  11. 兄弟结点:同一双亲结点的孩子结点间互称兄弟结点。

二叉树

定义

二叉树就是度不超过2的树(每个结点最多有两个子结点)。上面那张图就是二叉树。

满二叉树:
一个二叉树,如果每一个层的结点树都达到最大值,则这个二叉树就是满二叉树。
在这里插入图片描述
完全二叉树:
叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。
在这里插入图片描述

二叉树的创建

根据对图的观察,我们发现二叉树其实就是由一个一个的结点及其之间的关系组成的,按照面向对象的思想,我们
设计一个结点类来描述结点这个事物。

二叉树的创建其实很简单,跟链表差不多:

class TreeNode{
private:int val;TreeNode *left;TreeNode *right;
public:TreeNode(int x) : val(x), left(NULL), right(NULL) {}
}

二叉树的前中后序遍历

以下图为例:
在这里插入图片描述

前序遍历:

先访问根结点,然后再访问左子树,最后访问右子树。

void PreOrderTraverse(TreeNode* root){if(NULL == root)return;cout<<root->val;PreOrderTraverse(root->left);PreOrderTraverse(root->right);
}

打印信息:ABDECFG

中序遍历

先访问左子树,中间访问根节点,最后访问右子树。

void MidOrderTraverse(TreeNode* root){if(NULL == root)return;MidOrderTraverse(root->left);cout<<root->val;MidOrderTraverse(root->right);
}

打印信息:DBEAFCG

后序遍历

先访问左子树,再访问右子树,最后访问根节点。

void LastOrderTraverse(TreeNode* root){if(NULL == root)return;LastOrderTraverse(root->left);LastOrderTraverse(root->right);cout<<root->val;
}

打印顺序:DEBFCA

已知前序、中序遍历结果,还原二叉树

给了中序那就好办了
一:看中序排列中的根节点位置在哪里,根节点前面都属于根的左子树及其后代,后面你懂得。
二:将中序序列分两段:(D、B、E)和(F、C、G)
三:明眼人一看就知道根节点左子树的“根节点”是:B
别问我为啥,问就是看前序序列的第二位
四:重复二三,直到根节点左子树排出来为止
五:同上,排出右子树

具体思路:

对于任意一颗树而言,前序遍历的形式总是
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
即根节点总是前序遍历中的第一个节点。

而中序遍历的形式总是
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。

这样以来,我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置。

细节

在中序遍历中对根节点进行定位时,一种简单的方法是直接扫描整个中序遍历的结果并找出根节点,但这样做的时间复杂度较高。我们可以考虑使用哈希映射(HashMap)来帮助我们快速地定位根节点。对于哈希映射中的每个键值对,键表示一个元素(节点的值),值表示其在中序遍历中的出现位置。在构造二叉树的过程之前,我们可以对中序遍历的列表进行一遍扫描,就可以构造出这个哈希映射。在此后构造二叉树的过程中,我们就只需要 O(1) 的时间对根节点进行定位了。

下面的代码给出了详细的注释。

class Solution {
private:unordered_map<int, int> index;public:TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {if (preorder_left > preorder_right) {return nullptr;}// 前序遍历中的第一个节点就是根节点int preorder_root = preorder_left;// 在中序遍历中定位根节点int inorder_root = index[preorder[preorder_root]];// 先把根节点建立出来TreeNode* root = new TreeNode(preorder[preorder_root]);// 得到左子树中的节点数目int size_left_subtree = inorder_root - inorder_left;// 递归地构造左子树,并连接到根节点// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);// 递归地构造右子树,并连接到根节点// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int n = preorder.size();// 构造哈希映射,帮助我们快速定位根节点for (int i = 0; i < n; ++i) {index[inorder[i]] = i;}return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);}
};> 作者:LeetCode-Solution
> 链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/cong-qian-xu-yu-zhong-xu-bian-li-xu-lie-gou-zao-9/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

时间复杂度:O(n),其中 n 是树中的节点个数。

空间复杂度:O(n),除去返回的答案需要的 O(n) 空间之外,我们还需要使用 O(n) 的空间存储哈希映射,以及 O(h)(其中 h 是树的高度)的空间表示递归时栈空间。这里 h < nh<n,所以总空间复杂度为 O(n)。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/cong-qian-xu-yu-zhong-xu-bian-li-xu-lie-gou-zao-9/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

已知后序、中序遍历结果,还原二叉树

这个LeetCode上没找到,我模仿着写一个。

//一、二步同上
//其实第三步原理是一样的,不过我们的脑子习惯了从前到后,所以,让我帮你们转个弯。//像对中序分割一样,将后序序列也分割了。
//从中序排列的分割中我们知道根节点的右子树有哪些成员,所以后序序列这样分:
//(H I D J K E B)(L F G C)
//现在就很明显可以看出根节点左子树的“根节点”是谁了吧//重复以上步骤

具体思路:

对于任意一颗树而言,后序遍历的形式总是
[ [左子树的前序遍历结果], [右子树的前序遍历结果],根节点 ]
即根节点总是后序遍历中的最后一个节点。

而中序遍历的形式总是
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的后序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到后序遍历的结果中,对上述形式中的所有左右括号进行定位。

这样以来,我们就知道了左子树的后序遍历和中序遍历结果,以及右子树的后序遍历和中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置。

class Solution {
private:unordered_map<int, int> index;public:TreeNode* myBuildTree(const vector<int>& lastorder, const vector<int>& inorder, int lastorder_left, int lastorder_right, int inorder_left, int inorder_right) {if (lastorder_left > lastorder_right) {return nullptr;}// 后序遍历中的最后一个节点就是根节点int lastorder_root = lastorder_right;// 在中序遍历中定位根节点int inorder_root = index[lastorder[lastorder_root]];// 先把根节点建立出来TreeNode* root = new TreeNode(lastorder[lastorder_root]);// 得到左子树中的节点数目int size_left_subtree = inorder_root - inorder_left;// 递归地构造左子树,并连接到根节点// 后序遍历中「从 左边界开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素root->left = myBuildTree(lastorder, inorder, lastorder_left, lastorder_left + size_left_subtree-1, inorder_left, inorder_root - 1);// 递归地构造右子树,并连接到根节点// 后序遍历中「从 左边界+左子树节点数目 开始到 右边界-1 」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素root->right = myBuildTree(lastorder, inorder, lastorder_left + size_left_subtree, lastorder_right-1, inorder_root + 1, inorder_right);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int n = preorder.size();// 构造哈希映射,帮助我们快速定位根节点for (int i = 0; i < n; ++i) {index[inorder[i]] = i;}return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);}
};

二叉树的层序遍历

所谓的层序遍历,就是从根节点(第一层)开始,依次向下,获取每一层所有结点的值,有二叉树如下:
在这里插入图片描述
实现步骤:

1.创建队列,存储每一层的结点;
2.使用循环从队列中弹出一个结点:2.1获取当前结点的key;2.2如果当前结点的左子结点不为空,则把左子结点放入到队列中2.3如果当前结点的右子结点不为空,则把右子结点放入到队列中

代码实现

#include<queue>
#include<iostream>using namespace std;void LevelOrder(Node *root) {if (root == NULL)return;queue<Node *>	q;// 启动q.push(root);while (!q.empty()) {Node *front = q.front();q.pop();cout<<front->value;if (front->left != NULL) q.push(front->left);if (front->right != NULL) q.push(front->right);}cout<<endl;
}

二叉搜索树

二叉搜索树是什么?

所谓二叉搜索树,可提供对数时间的元素插入和访问。二叉搜索树的节点放置规则是:任何节点的键值一定大于去其左子树中的每一个节点的键值,并小于其右子树的每一个节点的键值。

所以在二叉树中找到最大值和最小值是很简单的,比较麻烦的是元素的插入和移除。
插入新元素时,从根节点开始,遇键值较大者就向左,遇键值较小者就向右,一直到尾端,即为插入点。
移除旧元素时,如果它是叶节点,直接拿走就是了;如果它有一个节点,那就把那个节点补上去;如果它有两个节点,那就把它右节点的最小后代节点补上去。

在这里插入图片描述

构造二叉搜索树

现有序列:A = {61, 87, 59, 47, 35, 73, 51, 98, 37, 93}。根据此序列构造二叉搜索树过程如下:

(1)i = 0,A[0] = 61,节点61作为根节点;
(2)i = 1,A[1] = 87,87 > 61,且节点61右孩子为空,故81为61节点的右孩子;
(3)i = 2,A[2] = 59,59 < 61,且节点61左孩子为空,故59为61节点的左孩子;
(4)i = 3,A[3] = 47,47 < 59,且节点59左孩子为空,故47为59节点的左孩子;
(5)i = 4,A[4] = 35,35 < 47,且节点47左孩子为空,故35为47节点的左孩子;
(6)i = 5,A[5] = 73,73 < 87,且节点87左孩子为空,故73为87节点的左孩子;
(7)i = 6,A[6] = 51,47 < 51,且节点47右孩子为空,故51为47节点的右孩子;
(8)i = 7,A[7] = 98,98 < 87,且节点87右孩子为空,故98为87节点的右孩子;
(9)i = 8,A[8] = 93,93 < 98,且节点98左孩子为空,故93为98节点的左孩子;创建完毕后如图2.4中的二叉搜索树:

在这里插入图片描述

代码实现:

#include<vector>
#include<iostream>using namespace std;class SerchTree{
private:TreeNode* root;
public:SerchTree();//插入节点void Insert_Node(TreeNode* root,int val){if(NULL == root)root = new TreeNode(val);else{if(val<root->val)Insert_Node(root->left,val);else{	//一样大就往左走吧Insert_Node(root->right,val);}}}//从数组中构造二叉搜索树	void Create_SerchTree(vector<int>& vec){int sz = vec.size();for(int i = 0;i<sz;i++){Insert_Node(root,vec[i]);}}//搜索某个节点是否存在bool SerchNode(TreeNode* root,int val){if(NULL == root)return false;if(val<root->val)return SerchNode(root->left,val);else if(val>root->val)return SerchNode(root->right)elsereturn ture;}//删除节点void DelNode(TreeNode* node){TreeNode* temp;if(NULL == node->right){	//如果右子节点为空temp = node;node = node->left;delete temp;}else{	//如果右子节点不空temp = node;while(NULL != temp->left){temp = temp->left;}node->val = temp->val;delete temp;}}	//删除某个节点void DelSerchNode(TreeNode* root,int val){if(NULL == root)return;if(val<root->val)return DelSerchNode(root->left,val);else if(val>root->val)return DelSerchNode(root->right)elseDelNode(root);}//计算二叉树的最大深度int maxDepth(Node x) { 	//1.如果根结点为空,则最大深度为0; if (x == null) return 0; int max = 0; int maxL = 0; int maxR = 0; //2.计算左子树的最大深度; if (x.left != null)  maxL = maxDepth(x.left); //3.计算右子树的最大深度; if (x.right != null)maxR = maxDepth(x.right);//4.当前树的最大深度=左子树的最大深度和右子树的最大深度中的较大者+1 max = maxL > maxR ? maxL + 1 : maxR + 1; return max; }
}

平衡二叉搜索树(AVL树)

什么是平衡二叉搜索树?

二叉搜索树一定程度上可以提高搜索效率,但是当原序列有序,例如序列A = {1,2,3,4,5,6},构造二叉搜索树如图。依据此序列构造的二叉搜索树为右斜树,同时二叉树退化成单链表,搜索效率降低为O(n)。

如下图:
在这里插入图片描述
在此二叉搜索树中查找元素6需要查找6次。二叉搜索树的查找效率取决于树的高度,因此保持树的高度最小,即可保证树的查找效率。同样的序列A,改为下图方式存储,查找元素6时只需比较3次,查找效率提升一倍。
在这里插入图片描述

可以看出当节点数目一定,保持树的左右两端保持平衡,树的查找效率最高。这种左右子树的高度相差不超过1的树为平衡二叉树。

AVL树的节点数据结构

和上面使用的那个普通结构略有不同。

    class AVLNode{private:int depth; //深度,这里计算每个结点的深度,通过深度的比较可得出是否平衡Tree parent; //该结点的父节点,方便操作int val; //结点值TreeNode lchild;TreeNode rchild;public:AVLNode(int val=0){parent=NULL;depth=0;lchild=rchild=NULL;this->val=val;}};

AVL树构造

初始构造的时候,先把数组排序之后进行构造,这里不多说。

直接看调整树的节点使平衡的操作:旋转

左旋

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

可以得出40节点的左子树高度为1,右子树高度为3,此时平衡因子为-2,树失去平衡。为保证树的平衡,此时需要对节点40做出旋转,因为右子树高度高于左子树,对节点进行左旋操作,流程如下:

1)节点的右孩子替代此节点位置
(2)右孩子的左子树变为该节点的右子树
(3)节点本身变为右孩子的左子树

图解过程:
在这里插入图片描述
在这里插入图片描述

右旋

右旋操作与左旋类似,操作流程为:

1)节点的左孩子代表此节点
(2)节点的左孩子的右子树变为节点的左子树
(3)将此节点作为左孩子节点的右子树。

图解过程:

在这里插入图片描述

在这里插入图片描述

双旋转

在这里插入图片描述
这个图我就要说两句了,有的人可能乍一看会觉得这用上面的单旋转就好了,为什么根节点不是14而是16?为什么这个会要叫双旋转?转着好玩的吗?

其实不然,你可以试着把单旋转做法的图画出来,将会惊奇的发现,还是不平衡,这就很尴尬了。

正确的转法应该是这样的:
在这里插入图片描述

新增节点(背多分)

本部分参考自:详细图文 - AVL树

往平衡二叉树中添加节点很可能会导致二叉树失去平衡,所以我们需要在每次插入节点后进行平衡的维护操作。插入节点破坏平衡性有如下四种情况:

LL(右旋)

LL的意思是向左子树(L)的左孩子(L)中插入新节点后导致不平衡,这种情况下需要右旋操作,而不是说LL的意思是右旋,后面的也是一样。
在这里插入图片描述
我们将这种情况抽象出来,得到下图:
在这里插入图片描述

我们需要对节点y进行平衡的维护。步骤如下图所示:

在这里插入图片描述
(被水印挡住部分:T1,T2,T3,T4)

代码实现:

AVLNode rightRotate(AVLNode y){AVLNode x = y.lchild;	//即将返回的节点是y的左子节点AVLNode t3 = x.rchild;	//先把y的右子节点取出来x.rchild = y;			//把y放进x的右子节点y.lchild = t3;			//把前面预存的放到y的左子节点//更新heighty.height = Math.max(getHeight(y.lchild),getHeight(y.rchild))+1;x.height = Math.max(getHeight(x.lchild),getHeight(x.rchild))+1;return x;
}

RR(左旋)

在这里插入图片描述
我们将这种情况抽象出来,得到下图:
在这里插入图片描述
我们需要对节点y进行平衡的维护。步骤如下图所示:

在这里插入图片描述
(被水印挡住部分:T4,T3,T1,T2)

代码实现:

AVLNode leftRotate(AVLNode y){AVLNode x = y.rchild;NAVLNode ode t2 = x.lchild;x.lchild= y;y.rchild= t2;//更新heighty.height = Math.max(getHeight(y.lchild),getHeight(y.rchild))+1;x.height = Math.max(getHeight(x.lchild),getHeight(x.rchild))+1;return x;
}

LR

在这里插入图片描述
我们将这种情况抽象出来,得到下图:
在这里插入图片描述

我们需要对节点y进行平衡的维护。步骤如下图所示:

在这里插入图片描述
第三个图里面x和z的位置换一下。

RL

在这里插入图片描述
我们将这种情况抽象出来,得到下图:

在这里插入图片描述

我们需要对节点y进行平衡的维护。步骤如下图所示:

在这里插入图片描述第二个图中y的左孩子为T1,第三个图中x和z反了。

(被水印遮住的部分为:T1,T2,T3,T4)

插入节点

AVLNode add(AVLNode node, int val){if(node == null){size ++;return new AVLNode(val);}if(val.compareTo(node.val) < 0)node.lchild = add(node.lchild, val);else if(val.compareTo(node.val) > 0)node.rchild = add(node.rchild, val);//更新heightnode.height = 1+Math.max(getHeight(node.lchild),getHeight(node.rchild));//计算平衡因子int balanceFactor = getBalanceFactor(node);if(balanceFactor > 1 && getBalanceFactor(node.lchild)>0) {//右旋LLreturn rightRotate(node);}if(balanceFactor < -1 && getBalanceFactor(node.rchild)<0) {//左旋RRreturn leftRotate(node);}//LRif(balanceFactor > 1 && getBalanceFactor(node.lchild) < 0){node.lchild = leftRotate(node.lchild);return rightRotate(node);}//RLif(balanceFactor < -1 && getBalanceFactor(node.rchild) > 0){node.rchild = rightRotate(node.rchild);return leftRotate(node);}return node;
}

里面的几个必备函数:

int getBalanceFactor(AVLNode node){if(node==NULL){return 0;}return getHeight(node.lchild)-getHeight(node.rchild);
}
int getHeight(AVLNode node){if(node==NULL){return 0;}return node.height;
}
//compareTo方法:
返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。如果参数字符串等于此字符串,则返回值 0;如果此字符串小于字符串参数,则返回一个小于 0 的值;如果此字符串大于字符串参数,则返回一个大于 0 的值。

删除节点

int remove(int e){AVLNode node = getNode(root, e);if(node != NULL){root = remove(root, e);return node.e;}return NULL;
}AVLNode remove(AVLNode node, int e){if( node == NULL )return NULL;AVLNode retNode;if( e.compareTo(node.e) < 0 ){node.left = remove(node.left , e);retNode = node;}else if(e.compareTo(node.e) > 0 ){node.right = remove(node.right, e);retNode = node;}else{   // e.compareTo(node.e) == 0// 待删除节点左子树为空的情况if(node.left == NULL){Node rightNode = node.right;node.right = NULL;size --;retNode = rightNode;}// 待删除节点右子树为空的情况else if(node.right == NULL){Node leftNode = node.left;node.left = NULL;size --;retNode = leftNode;}else {// 待删除节点左右子树均不为空的情况// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点// 用这个节点顶替待删除节点的位置Node successor = minimum(node.right);successor.right = remove(node.right, successor.e);successor.left = node.left;node.left = node.right = NULL;retNode = successor;}}if(retNode==NULL)return NULL;//维护平衡//更新heightretNode.height = 1+Math.max(getHeight(retNode.left),getHeight(retNode.right));//计算平衡因子int balanceFactor = getBalanceFactor(retNode);if(balanceFactor > 1 && getBalanceFactor(retNode.left)>=0) {//右旋LLreturn rightRotate(retNode);}if(balanceFactor < -1 && getBalanceFactor(retNode.right)<=0) {//左旋RRreturn leftRotate(retNode);}//LRif(balanceFactor > 1 && getBalanceFactor(retNode.left) < 0){node.left = leftRotate(retNode.left);return rightRotate(retNode);}//RLif(balanceFactor < -1 && getBalanceFactor(retNode.right) > 0){node.right = rightRotate(retNode.right);return leftRotate(retNode);}return retNode;
}

Java代码我是不排斥的,我都看得懂一些,主要是思想。


红黑树

1、红黑树?长什么果实吗

红黑树,又称RB-tree,是一种平衡二叉搜索树。不过它这个平衡没有AVL-tree要求那么严格罢了。(最长路径不超过最短路径的两倍)

红黑树的规矩:

  1. 每个节点,非黑即红。
  2. 根节点为黑。
  3. 不能存在连续的两个红节点。
  4. 任何节点,至其下属的、不同的叶节点的每条路径上,黑节点数必须相等。

好,看了上面这些规矩,你是不是在想:这什么SJ病的规矩,这还是人能弄出来的吗?
没错,我就是这样想的,这样想很正常。确实绕。

稍微来看一下这几个规则。
首先规则四便锁死了新增节点,妥妥得是红节点。
然而规则三又锁定了叶节点的父节点为黑节点。

那就涉及到一个问题:如果某叶节点为红,要再它后面新增节点,怎么办?
那就只有调整颜色并旋转树形。

这个调整的时候,可不能忘了它是红黑树,还是平衡二叉树,更是搜索树。所以还得守平衡二叉搜索树的规矩。

2、红黑树的节点设计

typedef bool __rb_tree_color_type;
const __rb_tree_color_type __rbtree_red = false;	//红0
const __rb_tree_color_type __rbtree_black = true;	//黑1struct __rb_tree_node_base
{typedef __rb_tree_color_type color_type;typedef __rb_tree_node_base* base_ptr;color_type color;base_ptr parent;base_ptr left;base_ptr right;static base_ptr minimum(base_ptr x){ ··· }static base_ptr maximum(base_ptr x){ ··· }
}
template <class Value>
struct __rb_tree_node :public __rb_tree_node_base
{typedef ____rb_tree_node<Value>* link_type;Value value_field;	//节点值
} 

3、 红黑树的数据结构

红黑树每次配置一个节点的空间。

template<class Key,class Value,class KeyOfValue,class Compare,class Alloc = alloc>
class rb_tree
{
protected:typedef void* void_pointer;typedef __rb_tree_node<Value> rb_tree_node;typedef	__rb_tree_node* base_ptr;typedef simple_alloc<rb_tree_node,Alloc> rb_tree_node_allocator;typedef __rb_tree_color_type color_type;public:typedef Key key_type;typedef Value value_type;typedef value_type* pointer;typedef const value_type* const_pointer;typedef value_type& reference;typedef rb_tree_node* link_node;typedef size_t size_type;typedef	ptrdiff_t difference_type;protected:···		link_type header;	//这是实现上的一个技巧Compare key_compare;	//节点间的键值大小比较准则//以下三个函数用于取得header的成员link_type& root() const { return (link_type&)header->parent; }link_type& leftmost() const { return (link_type&)header->left; }link_type& rightmost() const { return (link_type&)header->right; }···};

红黑树的构造有两种,一种是显式定义的复制构造函数,另一种是一颗空树。

4、红黑树插入节点

4.1 元素插入操作(insert_equal())

允许节点键值相同

//返回的是一个RB-tree迭代器,指向新增节点template<class Key,class Value,class KeyOfValue,class Compare,class Alloc>
typename rb_tree<Key,Value,KeyOfValue,Compare,Alloc>::iterator		//	这一行是返回值类型
rb_tree<Key,Value,KeyOfValue,Compare,Alloc>::insert_equal(const Value& v)	//这句看不懂的话我还有另一篇博客专门讲解这种句法,只是代码块里面放不了链接
{link_type y = header;link_type x = root();while(x!=0){y = x;x = key_compare(KeyOfValue()(v),key(x)) ? left(x) : right(x);	//左右转}return __insert(x,y,v);	//x:新值插入点  y:插入点之父节点  v:新值
}

4.2 元素插入操作(insert_unique())

插入新值,节点键值不允许重复,若重复则插入无效

template<class Key,class Value,class KeyOfValue,class Compare,class Alloc>
pair<typename rb_tree<Key,Value,KeyOfValue,Compare,Alloc>::iterator>		//	这一行是返回值类型
rb_tree<Key,Value,KeyOfValue,Compare,Alloc>::insert_unique(const Value& v)
{link_type y = header;link_type x = root();bool comp = true;while( x != 0 ){y = x;comp = key_compare(KeyOfValue()(v),key(x));x = comp ? left(x) : right(x);	//左右转}//循环转出来之后,y所指的便是插入节点的父节点,此时y必为叶节点iterator j = iterator(y);	//令迭代器j指向插入yif(comp)	//y节点值大于新值if(j == begin())	//如果插入点的父节点为最左	return pair<iterator,bool>(__insert(x,y,v),true);else--j;	//调整j,回头准备测试(我也不知道要测试啥)if(key_compare(key(j.node)KeyOfValue(key())(v))	//y节点小于新值return pair<iterator,bool>(__insert(x,y,v),true);return pair<iterator,bool>(j,false);//要是走到这一步,那说明键值重复了
}

4.3 插入的幕后黑手(__insert())

template<class Key,class Value,class KeyOfValue,class Compare,class Alloc>
typename rb_tree<Key,Value,KeyOfValue,Compare,Alloc>::iterator		//	这一行是返回值类型
rb_tree<Key,Value,KeyOfValue,Compare,Alloc>::__insert(base_ptr x_,base_ptr y_,const Value& v)	
{link_type y = link_type y_;link_type x = link_type x_;link_type z;if(y == header || x!=0 || Key_compare(KeyOfValue()(v),key(y))){z = create_node(v);	//产生一个新节点left(y) = z;if(y == header){root() = z;rightmost() = z;}else if(y == leftmost())leftmost() = z;}else{z = create_node(v);right(y) = z;if(y == rightmost())rightmost() = z;}parent(z) = y;	//设定新节点的父节点left(z) = 0;right(z) = 0;__rb_tree_rebalalance(z,header->parent);++node_count;return iterator(z);	//返回一个迭代器,指向新节点}

5、调整红黑树

旋转与改变颜色

任何插入操作,在插入完成之后,都需要做一次调整性操作,将树的状态调整到符合RB-tree的要求。

//函数功能:重新令树型平衡inline void __rb_tree_rebalance(__rb_tree_node_base* x/* 新增节点*/,__rb_tree_node_base*& root)
{x->color = __rb_tree_red;while( x != root && x->parent->color == __rb_tree_red)	//父节点为红{if( x->parent == x->parent->parent->left )	//父节点为祖父节点的左子节点{__rb_tree_node_base* y = x->parent->parent->right;	//将y作为它的伯父节点if( y && y->color == __rb_tree_red)	//如果真的有伯父节点,且为红{x->parent->color = __rb_tree_black;	//将父节点改黑y->color = __rb_tree_black;	//将伯父也抹黑y->parent->color = __rb_tree_red;	//把爷爷放红x = x->parent->parent;	//自己当爷爷							}else	//没有伯父,或者伯父是非洲回来的{if( x == x->parent->right)	//如果新节点为父节点右孩子{x = x->parent;	//自己当爸爸__rb_tree_rotate_left(x,root);	//第一参数为左旋点}x->parent->color = __rb_tree_black;	//将父节点抹黑x->parent->parent->color = __rb_tree_red;	//把爷爷放红__rb_tree_rotate_right(x->parent->parent,root);	//第一参数为右旋点	}}	else	//父节点为祖父节点右子节点{__rb_tree_node_base* y = x->parent->parent->left;	//令y为伯父节点if( y && y->color == __rb_tree_red)	//如果真的有伯父节点,且为红{x->parent->color = __rb_tree_black;	//将父节点改黑y->color = __rb_tree_black;	//将伯父也抹黑y->parent->color = __rb_tree_red;	//把爷爷放红x = x->parent->parent;	//自己当爷爷							}else	//没有伯父,或者伯父是非洲回来的{if( x == x->parent->left)	//如果新节点为父节点左孩子{x = x->parent;	//自己当爸爸__rb_tree_rotate_right(x,root);	//第一参数为左旋点}x->parent->color = __rb_tree_black;	//将父节点抹黑x->parent->parent->color = __rb_tree_red;	//把爷爷放红__rb_tree_rotate_right(x->parent->parent,root);	//第一参数为右旋点	}}}	//while结束root->color = __rb_tree_black;	//根节点抹黑
}

//源码之前,了无秘密。
操作流程图稍后会画。

//左旋inline void __rb_tree_rotate_left(__rb_tree_node_base* x,__rb_tree_node_base*& root)
{__rb_tree_node_base* y = x->right;	//令y为旋转点的右子节点x->right = y->left;	//令旋转点的右子节点的左子节点为旋转点的右节点if(y->left != 0)y->left->parent = x;	//并不知道这个有什么意义y->parent = x->parent;//令y完全顶替x的位置if(x == root)root = y;else if(x == x->parent->left)x->parent->left = y;elsex->parent->right = y;y->left = x;x->parent = y;
}
//右旋inline void __rb_tree_rotate_right(__rb_tree_node_base* x,__rb_tree_node_base*& root)
{__rb_tree_node_base* y = x->left;	//令y为旋转点的左子节点x->left = y->right;	//令旋转点的右子节点的左子节点为旋转点的右节点if(y->right != 0)y->right->parent = x;	//并不知道这个有什么意义y->parent = x->parent;//令y完全顶替x的位置if(x == root)root = y;else if(x == x->parent->right)x->parent->right = y;elsex->parent->left = y;y->right = x;x->parent = y;
}

6、红黑树工作流程图

前面都是不说两句就贴代码,接下来就比较友好了,贴图。
话说,源码之前,了无秘密。但是就算备注给你了,看着还费劲呢。

所以,我便想重源码之中整理处一份流程图,留待有缘人。CSDn上搜红黑树,一搜一大把,但是既然打开了我的,看了这么多,那我就得留点彩蛋才是》

先来张红黑树的流程图
红黑树流程图

7、红黑树图示

由于时间关系这里没配上操作图了,
其实是我发现另一篇的图实在太精美了,让我不好意思画了。

这里给出链接:漫画图解 - 红黑树

哈夫曼树

什么是哈夫曼树

哈夫曼大叔说,从树中一个结点到另一个结点之间的分支构成2个结点之间的路径,路劲上的分支数目称作路径长度。树的路径长度就是从树根到每一结点的路径长度之和。如果考虑到带权的结点,结点的带权的路径长度就为从该结点到树根之间的路径长度与结点上权的乘积。树的带权路径长度为树中所有叶子结点的带权路径长度之和。

假设有n个权值{W1,W2…,Wn},构造一棵n个叶子结点的二叉树,每个叶子结点带权Wk,每个叶子的路径长度为1k,我们通常记作,其中带权路径长度WPL最小的二叉树称为哈夫曼树(又称最优二叉树)。

哈夫曼树构造步骤

根据给定的n个权值{W1,W2,...,Wn}构成n棵二叉的集合F={T1,T2,...Tn},其中每棵二叉树Ti只有一个带权为Wi的根结点,其左右子树均为空。
在F中选取2棵根结点最小的树 作为左右子树 构造一棵新的二叉树,且新的二叉树的根结点左右子树根结点权值之和。
在F中删除这2棵子树,同时将新得到的二叉树加入F中。
重复23步骤,直到F只含一棵树为止,这棵树便是哈夫曼树。

代码

//.h#pragma once 
#ifndef __HUFFMANTREE_H__
#define __HUFFMANTREE_H__//创建哈夫曼树提供的字符的最大长度
#define MAX_SZ 256
//哈夫曼编码字符串最大长度
#define MAX_ENCODE 1024
//哈夫曼树结点数据结构
typedef struct HuffmanNode
{char data;struct HuffmanNode *lchild, *rchild;
}HuffmanNode,*HuffmanTree;//哈夫曼编码表结点数据结构
typedef struct HuffmanTableNode
{char data;		//字符char *encode;	//字符对应的哈夫曼编码struct HuffmanTableNode *next;
}HuffmanTableNode;
//哈夫曼编码表数据结构
typedef struct HuffmanTable
{HuffmanTableNode* front;//指向队头元素HuffmanTableNode* tail;	//指向队尾元素
}HuffmanTable;//根据用户提供原始数据,生成对应的哈夫曼树
HuffmanTree BuildHuffmanTree(char* inputString);//根据哈夫曼树 生成对应的哈夫曼编码表
HuffmanTable* BuildHuffmanTable(HuffmanTree tree);//对用户提供的源字符进行哈夫曼编码
char* encode(HuffmanTable* table, char* src);//根据用户提供的哈夫曼编码进行解码
char* decode(HuffmanTree root, char* encode);//遍历哈夫曼编码表
void TraverseHuffmanTable(HuffmanTable* table);
#endif // !__HUFFMANTREE_H__
//.cpp#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "HuffmanTree.h"
#include "HuffQueue.h"//根据用户提供字符集,生成对应的哈夫曼树
HuffmanTree BuildHuffmanTree(char* inputString)
{//统计每个字符出现的权值int charWeight[MAX_SZ] = { 0 };for (int i = 0; i < inputString[i]!='\0'; i++){charWeight[(unsigned char)inputString[i]]++;}HuffQueue* queue = NULL;InitHuffQueue(&queue);for (int i = 0; i <MAX_SZ; i++){//对应的字符有权值,创建树结点,添加到树节点队列中if (charWeight[i]){HuffmanNode* treeNode = (HuffmanNode*)malloc(sizeof(HuffmanNode));treeNode->data = i;treeNode->lchild = treeNode->rchild = NULL;AddHuffQueueNode(queue, treeNode, charWeight[i]);}}//根据哈夫曼树创建原理构建哈夫曼树//核心就是将权值最小的2个结点,取出作为新创建树结点的孩子结点,新创建树结点的权值为它们之和,然后放回树结点队列//一直这样循环进行操作,直到队列中最后剩一个结点,它就是树的根结点。while (queue->size != 1){HuffQueueNode* node1 = GetHuffQueueNode(queue);HuffQueueNode* node2 = GetHuffQueueNode(queue);HuffmanNode* treeNode = (HuffmanNode*)malloc(sizeof(HuffmanNode));treeNode->data = '\0';treeNode->lchild = node1->treeNode;treeNode->rchild = node2->treeNode;int weightNum = node1->weightNum + node2->weightNum;AddHuffQueueNode(queue, treeNode, weightNum);}return queue->first->treeNode;
}
/*
递归遍历哈夫曼树
depth 树的深度
tree 哈夫曼树
hTable 哈夫曼编码表
encode 字符对应的哈夫曼编码
*/
void traverseHuffTree(HuffmanTable* hTable,HuffmanTree tree,char* encode,int depth)
{if (NULL == tree->lchild && NULL == tree->rchild){HuffmanTableNode* tableNode = (HuffmanTableNode*)malloc(sizeof(HuffmanTableNode));tableNode->data = tree->data;tableNode->next = NULL;encode[depth] = '\0';tableNode->encode = (char*)malloc(depth+1);strcpy(tableNode->encode, encode);if (hTable->front == NULL){hTable->front = hTable->tail = tableNode;}else{hTable->tail->next = tableNode;hTable->tail = tableNode;}}if (NULL != tree->lchild){encode[depth] = '0';//左分支代表0traverseHuffTree(hTable, tree->lchild, encode, depth+1);}if (NULL != tree->rchild){encode[depth] = '1';//右分支代表1traverseHuffTree(hTable, tree->rchild, encode, depth+1);}return;
}//根据哈夫曼树生成哈夫曼编码表
HuffmanTable * BuildHuffmanTable(HuffmanTree tree)
{HuffmanTable* hTable = (HuffmanTable*)malloc(sizeof(HuffmanTable));hTable->front = hTable->tail = NULL;char encode[MAX_SZ] = { 0 };traverseHuffTree(hTable, tree, encode, 0);return hTable;
}//对用户提供的源字符进行哈夫曼编码
char* encode(HuffmanTable* table,char* src)
{char* encode = (char*)calloc(sizeof(char)*MAX_ENCODE,1);for (int i = 0; i < src[i]!='\0'; i++){char ch = src[i];HuffmanTableNode* iterator = table->front;while (iterator != NULL){if (iterator->data == ch){strcat(encode, iterator->encode);break;}iterator = iterator->next;}if (iterator == NULL){printf("哈夫曼编码表中没有字符%c对应的哈夫曼编码!\n",ch);return NULL;}}return encode;
}//根据用户提供的哈夫曼编码进行解码
char* decode(HuffmanTree root,char* encode)
{char* decode = (char*)calloc(MAX_SZ, 1);HuffmanTree tree = root;for (int i = 0; i < encode[i]!='\0'; i++){char ch = encode[i];if ('0' == ch)//0 往左走{tree = tree->lchild;}else//1 往右走{tree = tree->rchild;}//走到头,也就是找到相应的 源字符了if (tree->lchild == NULL && tree->rchild == NULL){strncat(decode, &tree->data, 1);//找到字符后,树节点回到根结点,继续解码tree = root;}}return decode;
}//打印哈夫曼编码表
void TraverseHuffmanTable(HuffmanTable* table)
{HuffmanTableNode* node = table->front;while (node != NULL){printf("源字符:%c ->哈夫曼编码:%s\n", node->data, node->encode);node = node->next;}
}int main(int argc, char *argv[])
{HuffmanTree tree = BuildHuffmanTree("aabbccc");HuffmanTable* table = BuildHuffmanTable(tree);TraverseHuffmanTable(table);char *src = "abc";char* dest = "10110101101";char* encode_str = encode(table, src);char* decode_str = decode(tree, dest);printf("原始字符串:%s ->哈夫曼编码:%s\n", src, encode_str);printf("哈夫曼编码:%s ->解码为:%s\n", dest, decode_str);return 0;
}

浅谈多路查找树(B树)

曾今我不知道多叉树有上面用,所以对于多叉树并没有过多的关注,或者说,基本没关注。
直到我了解到了多路查找树(B树),我知道,是我浅薄了。

先不说那些高深莫测的内容,我们就通俗的聊聊。

我们现在常说大数据大数据,就算没说过也听过不少了。但是我们的系统的内存就那么大,而外部硬盘却可以达到成千上百个G。

我们之前聊的数据结构,都是基于内存的,因此考虑的是内存中的运算时间复杂度。但是如果我们操作的数据集非常大,大到内存已经无法处理了,这也是很正常的现象,比方说某个程序要从文件系统中取一个文件出来,这个时间复杂度就会发生变化了。

试想一下,要在一个拥有几十万个文件的文件系统中查找一个文件,你所设计的算法需要读取磁盘上千次还是几十次,这是有本质上的差异的,后者可能是几秒,前者可能就要几分钟了,你能忍?找个文件都要等几分钟!!!

2-3树

这是一个简单的多路查找树,学新东西嘛,自然从最简单的开始。什么是多路查找树?和二叉树做个比较可能会比较直观:二叉树,你可以叫它二路查找树。明白了吧。

那么2-3树是一颗怎样的树?长这样:
1、其中每一个节点都有两个孩子(称为2节点)或三个孩子(三节点)或者没有。
2、子节点排序参考二叉树
3、一个二节点包含一个元素和两个子节点(或没有子节点),一个三节点包含两个元素和三个子节点(或没有子节点)
4、2-3树中所有的叶子节点都在同一层次上。

在这里插入图片描述

2-3树的插入

这个比较开始复杂了,不过咱就随便聊聊,聊到哪儿算哪儿。

首先,要明白每个新插入的节点都是二节点。

插入情景1:
插入空树,直接插入,完事儿。

插入情景2:
插入到一个二节点的叶子上,这也没什么,就像上面最左叶子节点,在“1"旁边给新节点“3”留个位置就好了。

插入情景3:
插入到一个三节点的叶子上,这就很尴尬了,2-3树的节点极限就是3,比方说上面要插个5进去。
那怎么弄?
先看一下,要插入节点的父节点是个二节点,那就好办了,把那个二节点变成三节点,自然就有地方插入了。怎么变?把“6”提上去啊,图自己画。
如果要插入节点的父节点是个三节点,那就比较尴尬一点。比方说我现在要插个“11”进去。
那怎么弄?
那就一直往上找,找到二节点为止,然后该怎么做上面已经讲了。

还是刚刚最后一种场景,如果往上找,找到根节点都是三节点,那怎么办?
那就意味着当前=层次已经无法满足我们的需要了,从根开始,全拆成二节点吧。
在这里插入图片描述

也不要去钻牛角尖了,咱就随便聊聊,要钻牛角尖咱往后去把代码实现写一下。现在知道是这么回事儿就好。


2-3树的删除

删除其实就是增加的逆过程,如果增加看懂了,删除就很简单。

以下场景针对删除节点为叶子节点:
删除场景1:要删除的节点位于一个三节点上,直接删了。

删除场景2:删除根节点,也是直接删了吧。

删除场景3:删除的节点位于一个二节点上。就像插入节点在三节点上一样的尴尬。不,更尴尬。

删除场景3.1:该节点的父节点为三节点:将父节点拆开下放一个节点。
删除场景3.2:上面场景不存在,但是 该节点的兄弟节点是三节点,将它的兄弟节点拆开,然后做单旋转。

其他的删除场景嘛,日后深入研究的时候再行探讨,过于复杂。


B树

B树是一种平衡的多路查找树。
节点最大的孩子的数量的树叫做m阶B数。
所以2-3树就是3阶B树,二叉树就是2阶B树。

B树有如下性质:

  1. 如果根节点不是叶节点,那么B树至少有两叉。
  2. 所有叶子节点都位于同一层次。
  3. 每一个非根的分支结点都有k-1个元素和k个孩子,其中[m/2]<k<m. 21每一个叶子结点n都有k-1个元素,其中[m/2]<k< m.

在B树上的查找过程是一个顺指针查找节点和在节点中查找关键字的交叉过程。

关于B树的插入删除,和2-3树一样,只不过阶数可能会大了些。

B树的典型应用

我们的外存,比如硬盘,是将所有的信息分割成相等大小的页面,每次硬盘读写的都是一个或多个完整的页面,对于一个硬盘来说,-页的长度可能是 211到214个字节。

在一个典型的B树应用中,要处理的硬盘数据量很大,因此无法一次全部装入内存。因此我们会对 B树进行调整, 使得B树的阶数 (或结点的元素)与硬盘存储的页面大小相匹配。比如说一棵B树的阶为1001 (即1个结点包含1000个关键字),高度为2,它可以储存超过10亿个关键字,我们只要让根结点持久地保留在内存中,那么在这棵树上,寻找某一个关键字至多需要两次硬盘的读取即可。

B树查找的时间复杂度:O(log n).


树与森林

树转换为二叉树

由于二叉树是有序的,为了避免混淆,对于无序树,我们约定树中的每个结点的孩子结点按从左到右的顺序进行编号。

将树转换成二叉树的步骤是:
(1)加线。就是在所有兄弟结点之间加一条连线;
(2)抹线。就是对树中的每个结点,只保留他与第一个孩子结点之间的连线,删除它与其它孩子结点之间的连线;
(3)旋转。就是以树的根结点为轴心,将整棵树顺时针旋转一定角度,使之结构层次分明。

在这里插入图片描述

森林转换为二叉树

森林是由若干棵树组成,可以将森林中的每棵树的根结点看作是兄弟,由于每棵树都可以转换为二叉树,所以森林也可以转换为二叉树。

将森林转换为二叉树的步骤是:
(1)先把每棵树转换为二叉树;
(2)第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子结点,用线连接起来。当所有的二叉树连接起来后得到的二叉树就是由森林转换得到的二叉树。

在这里插入图片描述

二叉树转换为树

二叉树转换为树是树转换为二叉树的逆过程,其步骤是:
(1)若某结点的左孩子结点存在,将左孩子结点的右孩子结点、右孩子结点的右孩子结点……都作为该结点的孩子结点,将该结点与这些右孩子结点用线连接起来;
(2)删除原二叉树中所有结点与其右孩子结点的连线;
(3)整理(1)和(2)两步得到的树,使之结构层次分明。

在这里插入图片描述

二叉树转换为森林

二叉树转换为森林比较简单,其步骤如下:
(1)先把每个结点与右孩子结点的连线删除,得到分离的二叉树;
(2)把分离后的每棵二叉树转换为树;
(3)整理第(2)步得到的树,使之规范,这样得到森林。


写在最后

哇,累。

如果喜欢,欢迎收藏、评论、点赞、关注哦!!!

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 交换机应用寻找10个完美的因素

    交换机应用寻找10个完美的因素 10 factors for finding the perfect switch for your application 选择开关的过程并不总是值得注意的。考虑到大多数交换机的成本相对较低且简单,通常在选择交换机时没有充分考虑它们所提供的特性和功能。根据应用的不同,有一些参数可以指导设…...

    2024/4/28 0:35:28
  2. redis学习笔记(三)

    使用redis保存对象方法一为该对象的类实现序列化接口,之后便能正常使用 class DemoApplicationTests {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid contextLoads() {User u = new User();u.setAge(18);u.setName("hehe");redisTemplate.opsForValue()…...

    2024/4/27 22:00:36
  3. 移动开发笔记(0)了解安卓

    Android介绍 1.Android系统架构 1.Linux内核层:提供底层驱动 2.系统运行库:通过一些c/c++库为Android系统提供主要的特性支持,还有Androidd运行时库,Android运行时库中还包含了Dalvik虚拟机(5.0系统之后改为了ART运行环境) 3.应用框架层:提供了构建应用程序时可能用到的各种…...

    2024/4/16 0:53:07
  4. Android 开发(七)

    音乐播放器开发 MediaPlayer 是Android 控制音频和视频文件播放类 1.创建MediaPlayer 对象 的Create方法 2.无can构造方法 -> setDataSorce -> prepare()加载创造文件 注意访问SDK需要授予权限 当Mediaplay.stop() 资源后需要重新加载资源,使用Mediaplay.setDataSource…...

    2024/4/28 5:05:27
  5. 时间转换,可能真的要注意这个坑(来自SimpleDateFormat的阴暗面)

    时间转换,可能真的要注意这个坑(来自SimpleDateFormat的阴暗面)andChen关注0.0662017.11.05 22:47:03字数 983阅读 8,928一个坑 我们经常在客户端做一些与时间相关的需求,比如 在6点~12点要展示一个新的广告位。 为了解决上述问题,我们通常把 开始、结束时间格式和服务端做好…...

    2024/4/28 2:52:46
  6. Python学习笔记24-mini_web框架添加路由以及MySQL功能

    1.引入路由的概念解决程序判断执行哪个页面 (1)使用字典来导入路由(2)使用代参数的装饰器来导入路由 1)方案1: import reURL_FUNC_DICT = dict()def route(url):def set_func(func):URL_FUNC_DICT[url] = func # 相当于 URL_FUNC_DICT["/index.py"] = indexde…...

    2024/4/1 3:37:57
  7. 大学生如何用微信公众号免费查题

    微信搜题的公众号有哪些,对于大多数人来说,使用微信是越来越方便,它不仅可以聊天,还可以解决我们生活中的一些难题,比如我们可以用它来查题,是不是觉得特别方便了,下面我给大家分享一下微信查题的方法,仅供大家参考。 第一步:打开我们的微信, 第二步:点击发现----搜…...

    2024/4/19 4:15:23
  8. xctf_如来十三掌

    如来十三掌 [目标] 破解密文 [环境] Windows [工具] 与佛论坛 Rot13解码 Base64解码 [分析] 1.打开该文件,发现处于word中的一堆经文一开始只是对黑体字产生疑惑,但最终发现什么用也没有 2.经过其他人的wp,才了解到设计经文的是与与佛论坛有关,也是属于编码解码网站,因此通…...

    2024/4/27 1:25:27
  9. redis简单总结

    redis简述 redis是一个高性能的key-value内存数据库,一般用来缓存,还可以用作消息中间件。读写速度快,支持10W QPS redis单进程单线程,线程安全。 redis为什么这么快直接操作内存 数据结构简单 单线程,避免了上下文切换五种数据类型string: 存字符串或数字,最大512m hash…...

    2024/4/16 0:53:33
  10. FRM证书一年薪资曝光!!!

    融跃在网上也看到过一些FRM持证人的薪资报告,有不少网站也统计过这些,确切的说这并不能够代表每个人,因此融跃在今天不能准确的回答这个问题! 我们需要明确:FRM证书不是一个可以挂靠的证书!有的人想要考证书挂靠,FRM证书是不可以的。 FRM是全球性的考试,由全球风险管理…...

    2024/3/28 18:05:34
  11. 机器学习分类算法(四)-贝叶斯算法

    朴素贝叶斯算法是有监督的学习算法,解决的是分类问题,如客户是否流失、是否值得投资、信用等级评定等多分类问题。该算法的优点在于简单易懂、学习效率高、在某些领域的分类问题中能够与决策树、神经网络相媲美。但由于该算法以自变量之间的独立(条件特征独立)性和连续变量…...

    2024/4/16 0:55:20
  12. if与switch的对比

    1. if与switch里括号内的类型 从类型转换来看if和switch,if(bool)和switch(整数),所以转换是根据括号里面的类型来看的。那么就分为以下两种情况:分析if判断的时候,如果括号里面是数字的时候就会转换为bool类型; 分析是switch分支判断的时候,如果括号里面是bool类型就会转…...

    2024/3/30 2:09:54
  13. 基于ESP8266与Blinker(点灯科技平台)的智能遥控器设计(三)

    3. 遥控控制 我们根据已经获取到的按键与其红外信号,开始进行控制设计 首先,我们需要手机下载软件 点灯 blinker ,注册登陆 点击右上方“+”号 -> Arduino -> wifi接入 -> 复制key 后,返回主界面,就会出现一个新的设备 。 点击 Arduino -> 开始使用 -> …...

    2024/4/19 21:55:39
  14. Android Q版本Launcher相关布局

    在处理Android Q版本需求时,发现GMS版本所需求的Launcher默认修改地方变了,在此记录下。path:vendor\partner_gms\apps\GmsSampleIntegration\res_dhs_min\xml\partner_default_layout.xml<?xml version="1.0" encoding="utf-8"?> <!-- Copy…...

    2024/4/18 9:28:23
  15. IDM+百度云链下载网盘资源

    IDM+百度云链下载网盘资源所需工具列表下载地址使用方法 所需工具列表IDM下载器 浏览器油猴插件Tampermonkey 百度网盘直链提取脚本下载地址IDM 浏览器插件Tampermonkey 百度网盘直链提取脚本,提取码:aud3使用方法在开始下载之前,需要先对IDM进行一些设置:在选项–>下载…...

    2024/4/16 0:54:24
  16. 图解|什么是缓存系统三座大山

    1.无处不在的缓存缓存在计算机系统是无处不在,在CPU层面有L1-L3的Cache,在Linux中有TLB加速虚拟地址和物理地址的转换,在浏览器有本地缓存、手机有本地缓存等。可见,缓存在计算机系统中有非常重要的地位,其主要作用是提高响应速度、减少磁盘访问等,本文主要讨论在高并发系…...

    2024/4/16 0:54:34
  17. 【新手向】C++ 求解二元一次方程

    针对入门级新手的C++代码分享 编程新人,前来报到! 最近老师布置的C++作业对于我这种纯萌新来讲实在是让人头大 心潮澎湃,但是我在网上找前辈们的代码学习时发现了一个问题——大佬们的代码都是简洁明了,甚至有所减省的,这对于我们这种小菜鸡而言有点不太友好高深莫测,所以…...

    2024/4/16 0:54:59
  18. 解决VUE项目重复点击菜单报错:Avoided redundant navigation to current location: “/xxxxx“. 问题

    描述: 报错见下图:解决方法: 在router文件夹下添加下面一段代码 // 解决ElementUI导航栏中的vue-router在3.0版本以上重复点菜单报错问题 const originalPush = VueRouter.prototype.push VueRouter.prototype.push = function push(location) {return originalPush.call(th…...

    2024/4/18 11:35:07
  19. 无法打开包括文件:“d3dx9math.h“或者“d3dx9.h”或者“D3DX9.LIB“:No such file of directory

    无法打开包括文件:“d3dx9math.h”:No such file of directory或者无法打开包括文件:“d3dx9.h”: No such file or directory,之后出现的 无法打开包括文件:“D3DX9.LIB" 首先确保安装dx9 sdk 看下C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)有没有这个…...

    2024/4/16 0:54:44
  20. touch bar的4个使用小技巧

    大家都知道MacBook Pro的实体功能键改成了Touchbar,目前全系的MacBook Pro都带了Touchbar。在刚推出的时候,一些人吐槽Touchbar,但是如果用了,习惯了之后,就真的回不去了。 想要玩转touch bar其实并不难,下面给大家介绍几个Touchbar使用的小技巧。 1、截图 Touchbar也是可…...

    2024/4/16 0:54:34

最新文章

  1. Spring AOP 面向切面编程通用化实现方案

    Spring AOP(面向切面编程)是一种编程范例,它使开发人员可以将横切关注点(跨越整个应用程序的重复代码)从实现他们的业务逻辑代码中分离开来。这样,开发人员可以使他们的代码更容易理解、更易于维护,以及更易于重用。 下面是 Spring AOP 的主要功能: 提供声明式企业服务…...

    2024/4/28 19:21:45
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 代码随想录算法训练营day32

    1005_K次取反后最大化的数组和&#xff08;看了题解&#xff09; 题目&#xff1a; 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i…...

    2024/4/26 15:52:19
  4. Topaz Video AI for Mac v5.0.0激活版 视频画质增强软件

    Topaz Video AI for Mac是一款功能强大的视频处理软件&#xff0c;专为Mac用户设计&#xff0c;旨在通过人工智能技术为视频编辑和增强提供卓越的功能。这款软件利用先进的算法和深度学习技术&#xff0c;能够自动识别和分析视频中的各个元素&#xff0c;并进行智能修复和增强&…...

    2024/4/27 12:49:51
  5. 设计模式:组合模式

    定义 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得客户端可以统一对待单个对象和组合对象。 应用场景 组合模式适用于以下场景: 表达对象的部分-整体层次结构:当你想要表示对象的部分-整…...

    2024/4/26 14:14:17
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/28 13:52:11
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/28 3:28:32
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/28 13:51:37
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/28 15:57:13
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/28 1:22:35
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57