【学习笔记】数据结构(六 ①)

树和二叉树 (一)

文章目录

  • 树和二叉树 (一)
    • 6.1 树(Tree)的定义和基本术语
    • 6.2 二叉树
      • 6.2.1 二叉树的定义
        • 1、斜树
        • 2、满二叉树
        • 3、完全二叉树
        • 4、二叉排序树
        • 5、平衡二叉树(AVL树)
        • 6、红黑树
      • 6.2.2 二叉树的性质
      • 6.2.3 二叉树的存储结构
    • 6.3 遍历二叉树和线索二叉树
      • 6.3.1 遍历二叉树

6.1 树(Tree)的定义和基本术语

树(Tree)是n(n≥0)个结点的有限集。

在任意一棵非空树中:

​ (1) 有且仅有一个特定的称为**根(Root)**的结点;

​ (2) 当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,…,Tm,其中每一个集合本身又是一棵树,并且

​ 称为根的子树(SubTree)

在这里插入图片描述

树的特点

  1. 树的根结点没有前驱,除根结点外的所有结点有且只有一个前驱。
  2. 树中所有结点可以有零个或多个后继。
  3. 树中的结点数等于所有结点的度数加1.
    度为m的树中第i层上至多有mi-1个结点(i > = 1)
    高度为h 的m叉树至多有( mh − 1 ) / ( m − 1 )个结点。
    具有n个结点的m叉树的最小高度为[ logm ( n ( m − 1 ) + 1 ) ] 。

树的其他表示形式

​ ( a ) 是以嵌套集合(即是一些集合的集体,对于其中任何两个集合,或者不相交,或者一个包含另一个)的形式表示的;

​ ( b ) 是以广义表的形式表示的,根作为由子树森林组成的表的名字写在表的左边;

​ ( c ) 用的是凹人表示法(类似书的编目)。

在这里插入图片描述

👉 基本术语:

  • 结点的祖先是从根到该结点所经分支上的所有结点。根A到结点K的唯一路径上的任意结点,称为结点K的祖先—如结点B是结点K的祖先

  • 以某结点为根的子树中的任一结点都称为该结点的子孙。结点K是结点B的子孙

  • 结点的子树的根称为该 结点的孩子(Child), 相应地,该结点称为孩子的**双亲(Parent) ** 。最接近结点K的结点E称为K的双亲,而K为结点E的孩子。根A是树中唯一没有双亲的结点。

  • 同一个双亲的孩子 之间互称兄弟 (Sibling) 。如结点K和结点L有相同的双亲E,即K和L为兄弟。

  • 其双亲在同一层的结点互为堂兄弟。如结点G与E,F,H,I,J互为堂兄弟。

  • 树的结点包含一个数据元素及若干指向其子树的分支。结点拥有的子树数(树中一个结点的孩子个数)称为结点的度 (Degree) 。如结点B的度为2,结点D的度为3

  • 树的度是树内各结点的度的最大值。树的度为3。

  • 度为0的结点 称为叶子 (Leaf) 或终端结点。

  • 度不为0的结点称为非终端结点或分支结点。除根结点之外,分支结点也称为内部结点

  • 结点的层次 (Level) 从根开始定义起.根为第一层,根的孩子为第二层。

  • 树中结点的最大层次称为树的深度 (Depth) 或高度。图中树的高度为4。

    结点的深度是从根结点开始自顶向下逐层累加的。
    结点的高度是从叶结点开始自底向上逐层累加的。

  • 如果将树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。在有序树中最左边的子树的根称为第一个孩子,最右边的称为最后一个孩子。

  • 森林 (Forest) m(m≥0) 棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。

    任何一棵树是一个二元组 Tree= (root, F), 其中: root 是数据元素、 称做树的根结点; m(m≥0) 棵树的森林, F=(T1, T2, …, Tm), 其中 Ti = (ri, Fi) 称做 root 的第i棵子树;当m≠0时,在树根和其子树森林之间存在下列关系:RF = { < root , 78ri > I i = 1 , 2, …, m ,m>0 }

6.2 二叉树

6.2.1 二叉树的定义

二叉树 (Binary Tree) 的特点是每个结点至多只有两棵子树 (即二叉树中不存在度大于2的结点),并且二叉树的子树有左右之分,其次序不能任意颠倒。[二叉树或为空树;或是由一个根结点加上两棵分别称为左子树和右子树的、互不交的二叉树组成]

在这里插入图片描述

🌟特殊的二叉树

1、斜树
  • 所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。
2、满二叉树
  • 一棵高度为h,且含有2h−1个结点的二叉树称为满二叉树,即树中的每层都含有最多的结点。除叶子结点之外的每个结点度数均为2。
  • 可以对满二叉树按层序编号:约定编号从根结点(根结点编号为1)起,自上而下,自左向右。这样,每个结点对应一个编号,对于编号为i的结点,若有双亲,则其双亲为i / 2, 若有左孩子,则左孩子为2i ; 若有右孩子,则右孩子为2i + 1。
3、完全二叉树
  • 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边。

在这里插入图片描述

4、二叉排序树
  • 二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。
    • 若它的左子树不空,则左子树上所有结点的值均小于它根结点的值。
    • 若它的右子树不空,则右子树上所有结点的值均大于它根结点的值。
    • 它的左、右子树都满足为⼆叉排序树

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>#include<stdio.h>
#include<stdlib.h>//二叉排序树节点存储方式
typedef int DataType;
typedef struct BTnode {DataType data;	//数据域struct BTnode* left;	//左指针 struct BTnode* right;	//右指针
}BTnode, * BTree;//插入结点
void Insert_node(BTree* root, DataType data) {if (*root == NULL) {*root = (BTnode*)malloc(sizeof(BTnode));if (!*root) {printf("ERROR\n");exit(-1);}(*root)->data = data;(*root)->left = NULL;(*root)->right = NULL;}else if ((*root)->data <= data)Insert_node(&(*root)->right, data);else if ((*root)->data > data)Insert_node(&(*root)->left, data);
}//创建排序二叉树
void Create_sortBtree(BTree* T, DataType* arr, int size) {if (!arr)return NULL;else {for (int i = 0; i < size; i++) {Insert_node(T, arr[i]);}}
}//中序遍历排序二叉树
void mid_travel(BTree T)
{if (!T)return;mid_travel(T->left);printf("%d ", T->data);mid_travel(T->right);
}//递归查找
BTnode* Btree_search(BTree root, DataType target) {if (!root)return NULL;if (target == root->data) {return root;}return target > root->data ? Btree_search(root->right, target) : Btree_search(root->left, target);
}//非递归查找
BTnode* Btree_search_fa(BTree root, DataType target) {BTnode* p = root;while (p) {if (p->data == target){return p;}p = target > p->data ? p->right : p->left;}return NULL;
}//获取最大值
int Btree_max(BTree T) {BTnode* cur = T;while (cur->right) {cur = cur->right;}return cur->data;
}//获取最小值
int Btree_min(BTree T) {BTnode* cur = T;while (cur->left) {cur = cur->left;}return cur->data;
}//删除结点
void Del_Node(BTree* T, int val)
{if (T == NULL) return ;if ((*T)->data < val){Del_Node(&((*T)->right), val);}else if ((*T)->data > val){Del_Node(&((*T)->left), val);}else {//叶子结点if ((*T)->left == NULL && (*T)->right == NULL){*T = NULL;return;}//没有左孩子,只有右孩子if ((*T)->left == NULL && (*T)->right != NULL){*T = (*T)->right;return ;}//没有右孩子,只有左孩子if ((*T)->left != NULL && (*T)->right == NULL){*T = (*T)->left;return ;}//左右子树都有if ((*T)->left != NULL && (*T)->right != NULL){BTree cur = (*T)->left;while (cur->right != NULL){cur = cur->right;}cur->right = (*T)->right;*T = (*T)->left;return ;}}
}int main()
{BTree* root = NULL;DataType w[9] = { 8, 3, 10, 1, 6, 14, 4, 7, 13 };Create_sortBtree(&root, w, 9);mid_travel(root);printf("\n");printf("递归查找结点:");BTnode* node1 = Btree_search(root, 6);printf("%d, 左子结点 %d, 右子节点 %d\n", node1->data, node1->left->data, node1->right->data);printf("非递归查找结点:");BTnode* node2 = Btree_search_fa(root, 6);printf("%d, 左子结点 %d, 右子节点 %d\n", node2->data, node2->left->data, node2->right->data);printf("该树最大值:%d\n", Btree_max(root));printf("该树最小值:%d\n", Btree_min(root));/*删除叶子结点 4 */Del_Node(&root, 4);mid_travel(root);printf("\n");/*删除只有右子树,没有左子树结点 10 */Del_Node(&root, 10);mid_travel(root);printf("\n");/*删除只有左子树,没有右子树结点 14 */Del_Node(&root, 14);mid_travel(root);printf("\n");/*删除有左子树和右子树结点 8 */Del_Node(&root, 8);mid_travel(root);printf("\n");return 0;
}
5、平衡二叉树(AVL树)

平衡二叉树也叫自平衡二叉搜索树(Self-Balancing Binary Search Tree),所以其本质也是一颗二叉搜索树,树上任一结点的左子树和右子树的深度/高度差不超过1。左右子树的高度差称之为平衡因子。

自平衡 - 旋转操作

触发时机:当添加一个节点之后,该树不再是一颗平衡二叉树

  • 左旋

    在这里插入图片描述

  • 右旋

    在这里插入图片描述

  • 单旋转

    • LL - 左左 一次右旋

      当不平衡节点的左子树的左子树有节点插入,导致二叉树不平衡

      在这里插入图片描述

    • RR - 右右 一次左旋

      当不平衡节点的右子树的右子树有节点插入,导致二叉树不平衡

      在这里插入图片描述

  • 双旋转

    • LR - 左右 先局部左旋,再整体右旋

      当不平衡节点的左子树的右子树有节点插入,导致二叉树不平衡
      在这里插入图片描述

    • RL - 右左 先局部右旋,再整体左旋

      当不平衡节点的右子树的左子树有节点插入,导致二叉树不平衡

      在这里插入图片描述

# include <stdio.h>
# include <stdbool.h>
# include <stdlib.h>
# include <math.h>typedef struct TreeNode { // 直接定义结构体类型int data; // 数据节点struct TreeNode* left; // 指向左子树,现在可以使用 TreeNodestruct TreeNode* right; // 指向右子树,现在可以使用 TreeNode
} TreeNode, * PTreeNode; // 注意这里去掉了*号,因为TreeNode现在已经是结构体类型// 记录平衡二叉树
bool BalanceTrue = false;
// 最小不平衡子树地址
TreeNode* rjt = NULL;// 检查二叉树是否平衡,若不平衡 BalanceTrue 为 true
int checkTreeBalance(TreeNode* root) {if (NULL == root) { return 0; }int x = checkTreeBalance(root->left);int y = checkTreeBalance(root->right);// 若检测到最小二叉树后,不进行后面的检查if (BalanceTrue) return 0;int xx = abs(x - y);if (xx > 1) {// 左子树 和 右子树 相差大于1 , 二叉树不平衡BalanceTrue = true;rjt = root;}return (x > y ? x + 1 : y + 1);
}// 返回二叉树树高
int treeHeight(TreeNode* root) {if (NULL == root) return 0;int ll = treeHeight(root->left);int rr = treeHeight(root->right);return (ll > rr ? ll + 1 : rr + 1);
}// 父节点查询
TreeNode* queryTopData(TreeNode* root, int data) {// 空地址异常抛出if (NULL == root) return NULL;// top: 父节点 ,如果为Null, 该节点为父节点// tmp: 遍历查询节点 TreeNode* top = NULL;TreeNode* tmp = root;while (tmp != NULL) {if (data == tmp->data) {// 节点为 返回Nullif (NULL == top) return NULL;return top;}top = tmp;if (data > tmp->data) {tmp = tmp->right;}else if (data < tmp->data) {tmp = tmp->left;}}return NULL;
}// 左左旋转 - 右旋
bool turnLL(TreeNode** root, TreeNode* notBalanceRoot) {if ((*root) != notBalanceRoot) {printf("左左旋转,非根节点\n");// 非根节点TreeNode* lleft = notBalanceRoot->left;TreeNode* lright = lleft->right;// 查找父节点TreeNode* fdata = queryTopData((*root), notBalanceRoot->data);if (NULL == fdata) return false;lleft->right = notBalanceRoot;notBalanceRoot->left = lright;if (notBalanceRoot == fdata->left) {fdata->left = lleft;}else if (notBalanceRoot == fdata->right) {fdata->right = lleft;}return true;}else {// 根节点printf("左左旋转,是根节点\n");TreeNode* lleft = notBalanceRoot->left;TreeNode* absroot = lleft;TreeNode* lright = lleft->right;lleft->right = notBalanceRoot;notBalanceRoot->left = lright;(*root) = absroot;return true;}}// 左右旋转
bool turnLR(TreeNode** root, TreeNode* notBalanceRoot) {if ((*root) != notBalanceRoot) {printf("左右旋转,非根节点");TreeNode* lleft = notBalanceRoot->left;TreeNode* leftRight = lleft->right;TreeNode* leftRightLeft = leftRight->left;// 第一次调整leftRight->left = lleft;lleft->right = leftRightLeft;notBalanceRoot->left = leftRight;// 查找父节点TreeNode* fdata = queryTopData((*root), notBalanceRoot->data);//if (NULL != fdata) printf("fdata: %d\n",fdata->data);// 第二次调整lleft = notBalanceRoot->left;leftRight = lleft->right;lleft->right = notBalanceRoot;notBalanceRoot->left = leftRight;if (notBalanceRoot == fdata->left) {fdata->left = lleft;}else if (notBalanceRoot == fdata->right) {fdata->right = lleft;}}else {printf("左右旋转,是根节点\n");TreeNode* lleft = notBalanceRoot->left;TreeNode* leftRight = lleft->right;TreeNode* leftRightLeft = leftRight->left;// 第一次调整leftRight->left = lleft;lleft->right = leftRightLeft;notBalanceRoot->left = leftRight;// 第二次调整lleft = notBalanceRoot->left;leftRight = lleft->right;lleft->right = notBalanceRoot;notBalanceRoot->left = leftRight;(*root) = lleft;}
}// 右左旋转
bool turnRL(TreeNode** root, TreeNode* notBalanceRoot) {TreeNode* rright = notBalanceRoot->right;TreeNode* rightLeft = rright->left;TreeNode* rightLeftRight = rightLeft->right;// 第一次调整rightLeft->right = rright;rright->left = rightLeftRight;notBalanceRoot->right = rightLeft;// 查找父节点TreeNode* fdata = queryTopData((*root), notBalanceRoot->data);//if (NULL != fdata) printf("fdata: %d\n",fdata->data);// 第二次调整rright = notBalanceRoot->right;rightLeft = rright->left;rright->left = notBalanceRoot;notBalanceRoot->right = rightLeft;if ((*root) != notBalanceRoot) {printf("右左旋转,非根节点\n");if (notBalanceRoot == fdata->left) {fdata->left = rright;}else if (notBalanceRoot == fdata->right) {fdata->right = rright;}}else {printf("右左旋转,是根节点\n");(*root) = rright;}
}// 右右旋转
bool turnRR(TreeNode** root, TreeNode* notBalanceRoot) {if ((*root) != notBalanceRoot) {printf("右右旋转,非根节点");TreeNode* rright = notBalanceRoot->right;TreeNode* rleft = rright->left;// 查找父节点TreeNode* fdata = queryTopData((*root), notBalanceRoot->data);if (NULL != fdata) printf("fdata: %d\n", fdata->data);rright->left = notBalanceRoot;notBalanceRoot->right = rleft;if (notBalanceRoot == fdata->left) {fdata->left = rright;}else if (notBalanceRoot == fdata->right) {fdata->right = rright;}}else {// 右右旋转,是根节点printf("右右旋转,是根节点\n");TreeNode* rright = notBalanceRoot->right;TreeNode* absroot = rright;TreeNode* rleft = rright->left;rright->left = notBalanceRoot;notBalanceRoot->right = rleft;(*root) = absroot;}
}// 二叉树前序遍历
void Print1(TreeNode* root) {if (NULL == root) return;printf("%d\t", root->data);Print1(root->left);Print1(root->right);
}// 二叉树中序遍历
void Print2(TreeNode* root) {if (NULL == root) return;Print2(root->left);printf("%d\t", root->data);Print2(root->right);
}// 二叉树后续遍历
void Print3(TreeNode* root) {if (NULL == root) return;Print3(root->left);Print3(root->right);printf("%d\t", root->data);
}// 插入二叉树节点
TreeNode* addNode(TreeNode* root, int data) {if (NULL == root) {// 头节点插入TreeNode* Node = (TreeNode*)malloc(sizeof(TreeNode));if (NULL == Node) {printf("新节点申请内存失败\n");return NULL;}Node->data = data;Node->left = NULL;Node->right = NULL;return Node;}TreeNode* tmp = root;TreeNode* top = NULL;// 找到合适的最尾巴节点while (NULL != tmp) {top = tmp;if (tmp->data == data) {printf("已经存在该节点,节点地址: %p\n", tmp);return root;}if (tmp->data < data) {tmp = tmp->right;}else if (tmp->data > data) {tmp = tmp->left;}}TreeNode* Node = (TreeNode*)malloc(sizeof(TreeNode));if (NULL == Node) {printf("申请新节点内存失败\n");return root;}Node->data = data;Node->left = NULL;Node->right = NULL;// 链接节点if (data > top->data) {top->right = Node;}else if (data < top->data) {top->left = Node;}return root;
}// 删除二叉排序树节点
bool DeleteTreeNode(TreeNode** TreeRoot, int data) {if (NULL == (*TreeRoot)) return false;printf("删除节点: %d\n", data);TreeNode* tmp = (*TreeRoot);TreeNode* top = NULL;while (tmp != NULL) {if (tmp->data == data) {// 叶子节点if ((NULL == tmp->left) && (NULL == tmp->right)) {// 叶子节点if (NULL == top) {// 仅有根节点的叶子节点free(tmp);return true;}else {// 其他的叶子节点TreeNode* lastNode = top;if (tmp == lastNode->left) {lastNode->left = NULL;}else if (tmp == lastNode->right) {lastNode->right = NULL;}free(tmp);return true;}}else {// 非叶子节点// 算法为: // 默认算法为: 1.  当删除该节点时,获取该树右子树最左子节点//             2.  当右子树为空时,此时应该获取左子树最右端子节点if (NULL != tmp->right) {// 方案 1TreeNode* tmp2 = tmp->right;TreeNode* top2 = NULL;// 找到最后一个节点while (tmp2->left != NULL) {top2 = tmp2;tmp2 = tmp2->left;}// 删除老的节点tmp->data = tmp2->data;// 只有右子树节点 没有左子树节点if (NULL == top2) {tmp->right = NULL;}else {top2->left = NULL;}free(tmp2);}else {// 方案 2TreeNode* tmp2 = tmp->left;TreeNode* top2 = NULL;// 找到最后一个节点while (tmp2->right != NULL) {tmp2 = tmp2->right;}// 删除老的节点tmp->data = tmp2->data;if (NULL == top2) {tmp->left = NULL;}else {top2->right = NULL;}free(tmp2);}}}else {top = tmp;if (data > tmp->data) {tmp = tmp->right;}else {tmp = tmp->left;}}}return false;
}// 二叉树平衡调整
bool treeBalance(TreeNode** root) {checkTreeBalance((*root));while (BalanceTrue) {printf("二叉树不平衡,最小不平衡子树数据结点: %d\n", rjt->data);TreeNode* tmp;if (1 < treeHeight(rjt->left) - treeHeight(rjt->right)) {// 对于不平衡二叉树而言,左子树比右子树高////printf("左\n");if (rjt->left != NULL) {tmp = rjt->left;int ll = treeHeight(tmp->left);int rr = treeHeight(tmp->right);if (ll > rr) {// 对于不平衡子树 左子树 而言, 左子树比右子树高// 左左旋转turnLL(root, rjt);}else {// 对于不平衡子树 左子树 而言, 右子树比左子树高// 左右旋转//turnLR(root, rjt);}}}else if (1 < treeHeight(rjt->right) - treeHeight(rjt->left)) {// 对于不平衡二叉树而言,右子树比左子树高////printf("右\n");if (rjt->right != NULL) {tmp = rjt->right;int ll = treeHeight(tmp->left);int rr = treeHeight(tmp->right);if (ll > rr) {//右左旋转turnRL(root, rjt);}else {//右右旋转turnRR(root, rjt);}}}BalanceTrue = false;checkTreeBalance((*root));printf("二叉树调整平衡后数据结点:\n");printf("前序遍历:");Print1(*root);printf("\n");printf("中序遍历:");Print2(*root);printf("\n");printf("\n");}}int main() {TreeNode* root = NULL;printf("平衡二叉树插入测试\n");int nums[] = { 65,60,70,55,40,63,69,66,68,77 };int i;for (i = 0; i < sizeof(nums) / sizeof(int); i++) {printf("插入数据: %d\n", nums[i]);root = addNode(root, nums[i]);if (NULL == root) {printf("首节点申请失败");return -1;}treeBalance(&root);}printf("\n当前二叉树遍历\n");printf("前序遍历:");Print1(root);printf("\n");printf("中序遍历:");Print2(root);printf("\n");//return 0;printf("\n\n平衡二叉树删除测试\n");for (i = 2; i < 5; i++) {DeleteTreeNode(&root, nums[i]);treeBalance(&root);}printf("\n当前二叉树遍历\n");printf("前序遍历:");Print1(root);printf("\n");printf("中序遍历:");Print2(root);printf("\n");return 0;
}

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

删除节点

  • 如果节点是叶子节点
    • 如果叶子节点是根节点,则直接释放该节点的内存。
    • 如果叶子节点不是根节点,则需要在父节点中删除该节点,并释放该节点的内存。
  • 如果节点不是叶子节点
    • 如果节点有右子树,则找到右子树中最左边的节点(即右子树的最小值节点),用这个节点的值替换当前节点的值,然后删除那个最小值节点。通过替代节点的父节点将替代节点设置为NULL,并释放替代节点的内存。
    • 如果节点没有右子树(即只有左子树),则找到左子树中最右边的节点(即左子树的最大值节点),用这个节点的值替换当前节点的值,然后删除那个最大值节点。通过替代节点的父节点将替代节点设置为NULL,并释放替代节点的内存。
6、红黑树

红黑树也是一种自平衡的二叉查找树,以前也叫做平衡二叉B树(Symmetric Binary B-tree)。

  • 它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色每一个节点可以是红或者黑;

  • 红黑树不是高度平衡的,它的平衡是通过"红黑规则"进行实现的

  • 红黑树增删改查的性能都很好

平衡二叉树红黑树
区别高度平衡
当左右子树高度差超过1时,通过旋转保持平衡
不是高度平衡的
条件: 特有的红黑规则

红黑规则

  • 每一个节点或是红色的,或者是黑色的 【节点是**RED** 或者**BLACK**】
  • 根节点必须是黑色 【根节点必须是**BLACK**】
  • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶子节点,每个叶子节点(Nil)是黑色的【叶子节点(外部节点,空节点)都是**BLACK**】
  • 如果某一个节点是红色,那么它的父节点和子节点必须是黑色(不能出现两个红色节点相连的情况)【RED 节点的父节点和子节点都是**BLACK**】
  • 对每一个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点

在这里插入图片描述

红黑树添加节点的规则

  • 默认颜色: 添加节点默认是红色的(效率高)

在这里插入图片描述

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>#define RED 0
#define BLACK 1typedef struct RBNode {int color; // 节点颜色int data;  // 数据struct RBNode* left;struct RBNode* right;struct RBNode* parent;
} RBNode, * RBTree;// 函数声明
RBNode* createNode(int data);
void insertFixup(RBTree* tree, RBNode* node);
RBNode* insertNode(RBTree* tree, int data);
void leftRotate(RBNode** root, RBNode* x);
void rightRotate(RBNode** root, RBNode* y);
void Print(RBNode* root);// 创建新节点
RBNode* createNode(int data) {RBNode* node = (RBNode*)malloc(sizeof(RBNode));if (node){node->data = data;node->color = RED;node->left = NULL;node->right = NULL;node->parent = NULL;return node;}return;
}// 插入新节点并修复红黑树
RBNode* insertNode(RBTree* tree, int data) {RBNode* node = createNode(data);if (*tree == NULL) {*tree = node;node->color = BLACK;}else {RBNode* current = *tree;RBNode* parent = NULL;while (current != NULL) {parent = current;if (node->data < current->data) {current = current->left;}else {current = current->right;}}node->parent = parent;if (node->data < parent->data) {parent->left = node;}else {parent->right = node;}insertFixup(tree, node);}return node;
}// 插入修复
void insertFixup(RBTree* tree, RBNode* node) {//父亲节点RBNode* parentOfNode = NULL;//祖父节点RBNode* grandparentOfNode = NULL;//只有父亲是红色的才需要操作,父亲是黑色的无需任何操作while (node != *tree && node->parent->color == RED) {//父亲节点parentOfNode = node->parent;//祖父节点grandparentOfNode = parentOfNode->parent;//父亲节点是祖父的左子节点if (parentOfNode == grandparentOfNode->left) {//叔叔节点RBNode* uncleOfNode = grandparentOfNode->right;//叔叔节点存在并是红色if (uncleOfNode && uncleOfNode->color == RED) {//父亲黑色,叔叔黑色,祖父红色grandparentOfNode->color = RED;parentOfNode->color = BLACK;uncleOfNode->color = BLACK;//祖父作为当前节点node = grandparentOfNode;}else {//叔叔是黑色,并且当前节点是父的右孩子 - 左右LR旋转if (node == parentOfNode->right) {//把父为当前节点node = parentOfNode;//左旋leftRotate(tree, node);}//叔叔是黑色,并且当前节点是父的左孩子 - 左左LL旋转 //即使叔叔是黑色,并且当前节点是父的右孩子,把父为当前节点进行左旋之后,也会变成叔叔是黑色,并且当前节点是父的左孩子的情况//父亲黑色,祖父红色parentOfNode->color = BLACK;grandparentOfNode->color = RED;//以祖父为支点右旋rightRotate(tree, grandparentOfNode);                }}else {//父亲节点是祖父的右子节点RBNode* uncleOfNode = grandparentOfNode->left;if (uncleOfNode && uncleOfNode->color == RED) {grandparentOfNode->color = RED;parentOfNode->color = BLACK;uncleOfNode->color = BLACK;node = grandparentOfNode;}else {//叔叔是黑色,并且当前节点是父的左孩子 - 右左RL旋转if (node == parentOfNode->left) {node = parentOfNode;rightRotate(tree, node);}//叔叔是黑色,并且当前节点是父的右孩子 - 右右LL旋转//即使叔叔是黑色,并且当前节点是父的左孩子,把父为当前节点进行右旋之后,也会变成叔叔是黑色,并且当前节点是父的右孩子的情况parentOfNode->color = BLACK;grandparentOfNode->color = RED;leftRotate(tree, grandparentOfNode);}}}//根只能是黑色的(*tree)->color = BLACK;
}// 左旋
void leftRotate(RBNode** root, RBNode* x) {RBNode* y = x->right;//建立x、y->left的关系x->right = y->left;if (y->left != NULL) {y->left->parent = x;}//建立x->parent和y的关系y->parent = x->parent;if (x->parent == NULL) {*root = y;}else if (x == x->parent->left) {x->parent->left = y;}else {x->parent->right = y;}//建立x、y的关系y->left = x;x->parent = y;
}// 右旋
void rightRotate(RBNode** root, RBNode* y) {RBNode* x = y->left;//建立y、x->right的关系y->left = x->right;if (x->right != NULL) {x->right->parent = y;}//建立y->parent和x的关系x->parent = y->parent;if (y->parent == NULL) {*root = x;}else if (y == y->parent->left) {y->parent->left = x;}else {y->parent->right = x;}//建立y,x关系x->right = y;y->parent = x;
}// 二叉树中序遍历
void Print(RBNode* root) {if (NULL == root) return;Print(root->left);printf("%d - %d\t", root->data, root->color);Print(root->right);
}// 主函数或其他函数可以调用这些函数来操作红黑树
int main() {RBTree tree = NULL;int nums[9] = { 20,18,23,22,17,24,19,15,14 };int i;for (i = 0; i < 9; i++){insertNode(&tree, nums[i]);}Print(tree);return 0;
}

6.2.2 二叉树的性质

  1. 任意一棵树,若结点数量为n, 则边的数量为n − 1。
  2. 非空二叉树上第i层上至多有2i-1个结点
  3. 高度为h的二叉树至多有2h-1个结点
  4. 非空二叉树上的叶子结点数等于度为2的结点数加1,即n0= n2+ 1
  5. 具有 n个结点的完全二叉树的深度为 ⌊ log ⁡ 2 n ⌋ + 1 \left \lfloor \log_2 n \right \rfloor + 1 log2n+1
  6. 对完全二叉树按从上到下、从左到右的顺序依次编号1 , 2… ∗ , n,则有以下关系:
    • 当 i = 1 时, 则结点 i 是二叉树的根-无双亲; 当 i > 1 时, 结点 i 的双亲的编号为 i / 2 ,即当 i 为偶数时,它是双亲的左孩子;当 i 为奇数时,它是双亲的右孩子。
    • 当 2i ≤ n 时,结点 i 的左孩子编号为 2i , 否则无左孩子。
    • 当 2i+1 ≤ n 时,结点 i 的右孩子编号为2i + 1,否则无右孩子。

在这里插入图片描述

6.2.3 二叉树的存储结构

  • 顺序存储结构: 一种极端的情况,一棵深度为k的右斜树,只有k个结点,却需要分配2k-1个存储单元,造成明显的浪费,所以顺序存储结构一般只用于完全二叉树

    #define MAX_TREE_SIZE 100 //二义树的最大结点数
    typedef char TElementType;
    typedef TElementType SqBiTree[MAX_TREE_SIZE];
    SqBiTree bt;
    

    在这里插入图片描述

    #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
    #include<math.h>
    #define MAX_TREE_SIZE 100
    #define CHAR 1#define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */#if CHAR
    typedef char TElemType;
    TElemType Nil = ' '; /* 设字符型以空格符为空 */
    #else
    typedef int TElemType;
    TElemType Nil = 0; /* 设整型以0为空 */
    #endif#define MAX_TREE_SIZE 100 /* 二叉树的最大结点数 */
    typedef TElemType SqBiTree[MAX_TREE_SIZE]; /* 0号单元存储根结点 */Status InitBiTree(SqBiTree T); /* 构造空二叉树T */
    Status CreateBiTree(SqBiTree T); /* 按层序次序输入二叉树中结点的值(字符型或整型), 构造顺序存储的二叉树T */
    Status BiTreeEmpty(SqBiTree T); /* 判空 */
    int BiTreeDepth(SqBiTree T); /*求深度*/
    Status Root(SqBiTree T, TElemType* e); /* 求根节点 */
    void Print_BiTree(SqBiTree T); /* 打印 */int main()
    {Status i;TElemType e;SqBiTree T;InitBiTree(T);CreateBiTree(T);printf("打印二叉树的数据\n");Print_BiTree(T);printf("建立二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n", BiTreeEmpty(T), BiTreeDepth(T));i = Root(T, &e);if (i)
    #if CHARprintf("二叉树的根为:%c\n", e);
    #elseprintf("二叉树的根为:%d\n", e);
    #endifelseprintf("树空,无根\n");return 0;
    }Status InitBiTree(SqBiTree T)
    { /* 构造空二叉树T。因为T是固定数组,不会改变,故不需要& */int i;for (i = 0; i < MAX_TREE_SIZE; i++)T[i] = Nil; /* 初值为空 */return OK;
    }Status CreateBiTree(SqBiTree T)
    { /* 按层序次序输入二叉树中结点的值(字符型或整型), 构造顺序存储的二叉树T */int i = 0;
    #if CHARint l;char s[MAX_TREE_SIZE];printf("请按层序输入结点的值(字符),空格表示空结点,结点数≤%d:\n", MAX_TREE_SIZE);gets(s); /* 输入字符串 */l = strlen(s); /* 求字符串的长度 */for (; i < l; i++) /* 将字符串赋值给T */{T[i] = s[i];if (i != 0 && T[(i + 1) / 2 - 1] == Nil && T[i] != Nil) /* 此结点(不空)无双亲且不是根 */{printf("出现无双亲的非根结点%c\n", T[i]);exit(ERROR);}}for (i = l; i < MAX_TREE_SIZE; i++) /* 将空赋值给T的后面的结点 */T[i] = Nil;
    #elseprintf("请按层序输入结点的值(整型),0表示空结点,输999结束。结点数≤%d:\n", MAX_TREE_SIZE);while (1){scanf("%d", &T[i]);if (T[i] == 999)break;if (i != 0 && T[(i + 1) / 2 - 1] == Nil && T[i] != Nil) /* 此结点(不空)无双亲且不是根 */{printf("出现无双亲的非根结点%d\n", T[i]);exit(ERROR);}i++;}while (i < MAX_TREE_SIZE){T[i] = Nil; /* 将空赋值给T的后面的结点 */i++;}
    #endifreturn OK;
    }Status BiTreeEmpty(SqBiTree T)
    { /* 初始条件: 二叉树T存在 *//* 操作结果: 若T为空二叉树,则返回TRUE,否则FALSE */if (T[0] == Nil) /* 根结点为空,则树空 */return TRUE;elsereturn FALSE;
    }int BiTreeDepth(SqBiTree T)
    //高度为h的二叉树至多有2^h-1个结点 - n<=pow(2,h)-1
    { /* 初始条件: 二叉树T存在。操作结果: 返回T的深度 */int i, j = -1;for (i = MAX_TREE_SIZE - 1; i >= 0; i--) /* 找到最后一个结点 */if (T[i] != Nil)break;i++; /* 为了便于计算 */doj++;while (i >= pow(2, j));//计算2的J次幂,返回double值。因为i<=pow(2,j)-1,因此i<pow(2,j).return j;//返回二叉树的深度
    }Status Root(SqBiTree T, TElemType* e)
    { /* 初始条件: 二叉树T存在 *//* 操作结果:  当T不空,用e返回T的根,返回OK;否则返回ERROR,e无定义 */if (BiTreeEmpty(T)) /* T空 */return ERROR;else{*e = T[0];//返回T的根,也就是第一个节点return OK;}
    }void Print_BiTree(SqBiTree T)
    {int i;for (i = 0; i < 100; ++i){if (T[i] != '\0')
    #if CHARprintf("%c", T[i]);
    #elseprintf("%d", T[i]);
    #endif}printf("\n");
    }
    
  • 链式存储结构

    二叉树的结点 由一个数据元素和分别指向其 左、右子树的两个分支构成,则表示二叉树的链表中的结点至少包含3个域:数据域和左、右指针域。有时,为了便于找到结点的双亲,则还可在结点结构中增加一个指向其双亲结点的指针域。利用这两种结点结构所得二叉树的存储结构分别称之为二叉链表三叉链表

    在这里插入图片描述

    在这里插入图片描述

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <stdio.h>
    #include <stdlib.h>#define MAX_TREE_SIZE 100typedef char TElementType;
    typedef struct BiTNode {TElementType data;struct BiTNode* lchild, * rchild; //左孩子、右孩子指针
    }BiTNode, * BiTree;//先序
    BiTree CreateBiTree() {char ch;if (scanf(" %c", &ch) != 1) { // 检查输入是否成功// 输入失败,可能是文件结束符或错误输入exit(EXIT_FAILURE);}if (ch != '#') {BiTNode* node = (BiTNode*)malloc(sizeof(BiTNode)); // 分配内存if (node == NULL) {// 内存分配失败perror("内存分配失败");exit(EXIT_FAILURE);}node->data = ch;node->lchild = CreateBiTree(); // 递归创建左子树node->rchild = CreateBiTree(); // 递归创建右子树return node;}else {return NULL;}
    }//中序
    void Print_Tree(BiTree T)
    {if (T){		Print_Tree(T->lchild);printf("%c  ", T->data);Print_Tree(T->rchild);}
    }int main()
    {BiTree T = NULL;T = CreateBiTree(); // abc##de#g##f###Print_Tree(T); // c  b  e  g  d  f  areturn 0;
    }
    

6.3 遍历二叉树和线索二叉树

6.3.1 遍历二叉树

  • 先(根)序遍历

    (1) 访问根结点; (2) 先序遍历左子树; (3) 先序遍历右子树

  • 中(根)序遍历

    (1) 中序遍历左子树; (2) 访问根结点; (3) 中序遍历右子树

  • 后(根)序遍历

    (1) 后序遍历左子树; (2) 后序遍历右子树; (3) 访问根结点

  • 层序遍历

    层序遍历以树的根节点开始,按照从上到下、从左到右的顺序逐层遍历树中的节点。

    过程:

    1. 创建一个队列,并将根节点入队。
    2. 当队列不为空时,执行以下步骤:
      1. 从队列中取出一个节点,访问该节点。
      2. 将该节点的所有子节点(如果存在)依次入队。
    3. 如果队列为空,则表示遍历结束。
/*  递归  */
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>#define MAX_TREE_SIZE 100
#define MAXQSIZE 10#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define ERROR -2
#define TRUE 1
#define FALSE 0
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */typedef char TElementType;
typedef struct BiTNode {TElementType data;struct BiTNode* lchild, * rchild; //左孩子、右孩子指针
}BiTNode, * BiTree;typedef struct {BiTNode** base; // 队列存储的是TreeNode*的指针int front;int rear;
} SqQueue;Status InitQueue(SqQueue* Q) {Q->base = (BiTNode**)malloc(MAXQSIZE * sizeof(BiTNode*));if (!Q->base) exit(1); // 使用exit(1)表示错误退出Q->front = Q->rear = 0;return OK;
}// 判断队列是否为空
int IsEmpty(SqQueue* q) {return q->rear <= q->front;
}Status EnQueue(SqQueue* Q, BiTNode* node) {if ((Q->rear + 1) % MAXQSIZE == Q->front) {return OVERFLOW; // 队列已满,返回OVERFLOW}Q->base[Q->rear] = node;Q->rear = (Q->rear + 1) % MAXQSIZE;return OK;
}BiTNode* DeQueue(SqQueue* Q) {if (Q->front == Q->rear) {return ERROR; // 队列为空,返回ERROR}BiTNode* dequeuedNode = Q->base[Q->front]; // 使用引用来修改e的值Q->front = (Q->front + 1) % MAXQSIZE;return dequeuedNode;
}void DestroyQueue(SqQueue* Q) {free(Q->base);Q->base = NULL;Q->front = Q->rear = 0;
}//先序
BiTree CreateBiTree() {char ch;if (scanf(" %c", &ch) != 1) { // 检查输入是否成功// 输入失败,可能是文件结束符或错误输入exit(EXIT_FAILURE);}if (ch != '#') {BiTNode* node = (BiTNode*)malloc(sizeof(BiTNode)); // 分配内存if (node == NULL) {// 内存分配失败perror("内存分配失败");exit(EXIT_FAILURE);}node->data = ch;node->lchild = CreateBiTree(); // 递归创建左子树node->rchild = CreateBiTree(); // 递归创建右子树return node;}else {return NULL;}
}Status PrintElement(TElementType e)
{printf("%c ", e);return OK;
}//先序遍历
Status PreOrderTraverse(BiTree T, Status(* PrintElement)(TElementType e))
{if (T){		if (PrintElement(T->data))if (PreOrderTraverse(T->lchild,PrintElement))if (PreOrderTraverse(T->rchild,PrintElement)) return OK;return OK;}else{return OK;}
}//中序遍历
Status InOrderTraverse(BiTree T, Status(*PrintElement)(TElementType e))
{if (T){if (InOrderTraverse(T->lchild, PrintElement))if (PrintElement(T->data))if (InOrderTraverse(T->rchild, PrintElement)) return OK;return OK;}else{return OK;}
}//后序遍历
Status PostOrderTraverse(BiTree T, Status(*PrintElement)(TElementType e))
{if (T){if (PostOrderTraverse(T->lchild, PrintElement))if (PostOrderTraverse(T->rchild, PrintElement)) if (PrintElement(T->data)) return OK;return OK;}else{return OK;}
}//层序遍历
void LevelOrderTraversal(BiTree T, Status(*PrintElement)(TElementType e)) {if (T == NULL) return;SqQueue Q;InitQueue(&Q);	//初始化辅助队列EnQueue(&Q, T);	//将根节点入队while (!IsEmpty(&Q)) {	//队列不空则循环BiTNode* frontNode = DeQueue(&Q);	//队头结点出队PrintElement(frontNode->data);	//访问出队结点if (frontNode->lchild != NULL) {EnQueue(&Q, frontNode->lchild);	//左子树不空,则左子树根节点入队}if (frontNode->rchild != NULL) {EnQueue(&Q, frontNode->rchild);	//右子树不空,则右子树根节点入队}}
}int main()
{BiTree T = NULL;T = CreateBiTree(); // abc##de#g##f###printf("----------先序---------------\n");PreOrderTraverse(T, PrintElement); // a  b  c  d  e  g  fprintf("\n----------中序---------------\n");InOrderTraverse(T, PrintElement); // c  b  e  g  d  f  aprintf("\n----------后序---------------\n");PostOrderTraverse(T, PrintElement); // c  g  e  f  d  b  aprintf("\n----------层序---------------\n");LevelOrderTraversal(T, PrintElement); // a  b  c  d  e  f  greturn 0;
}

非递归的思路:

先序:

在这里插入图片描述

中序:

在这里插入图片描述

后序:

在这里插入图片描述

/*  非递归  */
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>#define MAX_TREE_SIZE 100
#define MAXQSIZE 10#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define ERROR -2
#define TRUE 1
#define FALSE 0typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */typedef char TElementType;
typedef struct BiTNode {TElementType data;struct BiTNode* lchild, * rchild; //左孩子、右孩子指针
}BiTNode, * BiTree;// 模拟栈结构
typedef struct Stack {BiTNode* nodes[100]; // 假设最多100个节点int top;
} Stack;// 初始化栈
void StackInit(Stack* s) {s->top = -1;
}// 判断栈是否为空
int IsEmpty(Stack* s) {return s->top == -1;
}// 压栈
void Push(Stack* s, BiTNode* node) {if (s->top < 99) { // 防止栈溢出s->nodes[++(s->top)] = node;}
}// 出栈
BiTNode* Pop(Stack* s) {if (!IsEmpty(s)) {return s->nodes[(s->top)--];}return NULL;
}//先序
BiTree CreateBiTree() {char ch;if (scanf(" %c", &ch) != 1) { // 检查输入是否成功// 输入失败,可能是文件结束符或错误输入exit(EXIT_FAILURE);}if (ch != '#') {BiTNode* node = (BiTNode*)malloc(sizeof(BiTNode)); // 分配内存if (node == NULL) {// 内存分配失败perror("内存分配失败");exit(EXIT_FAILURE);}node->data = ch;node->lchild = CreateBiTree(); // 递归创建左子树node->rchild = CreateBiTree(); // 递归创建右子树return node;}else {return NULL;}
}Status PrintElement(TElementType e)
{printf("%c ", e);return OK;
}// 非递归先序遍历:	
void PreOrderTraverse(BiTNode* root) {if (root == NULL) {return;}Stack stack;StackInit(&stack);Push(&stack, root);while (!IsEmpty(&stack)) {root = Pop(&stack);PrintElement(root->data);if (root->rchild != NULL) {Push(&stack, root->rchild);}if (root->lchild != NULL) {Push(&stack, root->lchild);}}
}//中序遍历
Status InOrderTraverse(BiTNode* head) {if (head == NULL) {return;}Stack stack;StackInit(&stack);BiTNode* current = head;while (current != NULL || !IsEmpty(&stack)) {// 将当前节点压入栈中,并遍历其左子树while (current != NULL) {Push(&stack, current);current = current->lchild;}// 访问栈顶节点,并遍历其右子树current = Pop(&stack);PrintElement(current);current = current->rchild;}
}//后序遍历
Status PostOrderTraverse(BiTNode* root) {if (root == NULL) {return;}Stack s1, s2;StackInit(&s1);StackInit(&s2);Push(&s1, root);while (!IsEmpty(&s1)) {root = Pop(&s1);Push(&s2, root);if (root->lchild != NULL) {Push(&s1, root->lchild);}if (root->rchild != NULL) {Push(&s1, root->rchild);}}while (!IsEmpty(&s2)) {PrintElement(Pop(&s2)->data);}
}int main()
{BiTree T = NULL;T = CreateBiTree(); // abc##de#g##f###printf("----------先序非递归---------------\n");PreOrderTraverse(T, PrintElement); // a  b  c  d  e  g  fprintf("\n----------中序非递归---------------\n");InOrderTraverse(T, PrintElement); // c  b  e  g  d  f  aprintf("\n----------后序非递归---------------\n");PostOrderTraverse(T, PrintElement); // c  g  e  f  d  b  areturn 0;
}

参考:

教材:

严蔚敏《数据结构》(C语言版).pdf

离散数学及其应用(原书第8版) (Kenneth H.Rosen) .pdf

《代码随想录》回溯算法(V3.0).pdf

视频:

https://www.bilibili.com/video/BV1Zf4y1a77g/?spm_id_from=333.788&vd_source=a89593e8d33b31a56b894ca9cad33d33

https://www.bilibili.com/video/BV1b7411N798/?p=51&spm_id_from=333.880.my_history.page.click&vd_source=a89593e8d33b31a56b894ca9cad33d33

https://www.bilibili.com/video/BV1pu411c7pf/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=a89593e8d33b31a56b894ca9cad33d33

https://www.bilibili.com/video/BV17F411T7Ao?p=195&vd_source=a89593e8d33b31a56b894ca9cad33d33

博客:

https://blog.csdn.net/Real_Fool_/article/details/113930623

https://blog.csdn.net/m0_73633088/article/details/133443742

https://blog.csdn.net/wangrunmin/article/details/7831318

https://blog.csdn.net/Colorful___/article/details/133603913

https://blog.51cto.com/u_13360/6732847

https://zhuanlan.zhihu.com/p/600665635

https://blog.csdn.net/crr411422/article/details/129932889

https://blog.51cto.com/u_15773967/6148952

https://blog.csdn.net/2401_82584055/article/details/138349195

https://mp.weixin.qq.com/s?__biz=Mzg5MjkxNTA5MA==&mid=2247484467&idx=2&sn=3ea1749b1c7c301289a40dac4497e87e&chksm=c03798cef74011d8ead16e27ebea8e3a1df2a9a0242385d328f0ec05973e9b12ef1fa7254e14&scene=27

工具:

https://www.cs.usfca.edu/~galles/visualization/DisjointSets.html

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

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

相关文章

程序员常用开发软件集合

文本编辑器 Sublime Text 编程工具 Visual Studio Code IntelliJ IDEA 数据连接客户端 Navicat DBeaver 远程连接客户端 WinSCP xshell WindTerm 流程图工具 draw.io 远程连接电脑工具 ToDesk 向日葵 teamviewer

JVM java主流的追踪式垃圾收集器

目录 前言 分代垃圾收集理论 标记清除算法 标记复制算法 标记整理法 前言 从对象消亡的角度出发, 垃圾回收器可以分为引用计数式垃圾收集和追踪式垃圾收集两大类, 但是java主流的一般是追踪式的垃圾收集器, 因此我们重点讲解. 分代垃圾收集理论 分代收集这种理…

《黑龙江水产》是什么级别的期刊?是正规期刊吗?能评职称吗?

问题解答 问&#xff1a;《黑龙江水产》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的第一批认定 学术期刊。 问&#xff1a;《黑龙江水产》级别&#xff1f; 答&#xff1a;省级。主管单位&#xff1a;黑龙江省农业农村厅 …

[深度学习]神经网络

1 人工神经网络 全连接神经网络 2 激活函数 隐藏层激活函数由人决定输出层激活函数由解决的任务决定&#xff1a; 二分类&#xff1a;sigmoid多分类&#xff1a;softmax回归&#xff1a;不加激活&#xff08;恒等激活identify&#xff09; 2.1 sigmoid激活函数 x为加权和小于…

9月18日国家网络安全通报中心发布的100个高危漏洞(下)

9月18日国家网络安全通报中心发布&#xff0c;公安机关网安部门从危害程度、广泛性、漏洞利用形式、利用难度、检测难度等维度&#xff0c;梳理出了100个突出的高危漏洞&#xff0c;目前这些漏洞是各个网络安全公司检测的重点&#xff0c;广大网络运营者应尽快对照排查自己的网…

【MYSQL表的增删改查(进阶)】

MYSQL表的增删改查&#xff08;进阶&#xff09; 一、新增二、查询2.1 聚合查询2.1.1 聚合函数count&#xff08;&#xff09;sum&#xff08;&#xff09;AVG&#xff08;&#xff09;MAX&#xff08;&#xff09;&#xff0c;MIN&#xff08;&#xff09;GROUP_CONCAT() 2.1.…

Elasticsearch集群的运维与管理

【1】安装启动ES 集群 &#xff08;1.1&#xff09;集群架构规划 OS  ES versionIpnode.nameRolecluster.namees basedirCentOS Linux release 7.8.2003 (Core)elasticsearch-7.14.1 192.168.175.132:9200 cluster&#xff1a;192.168.175.132:9301 node_1 node.mastertrue …

C++—string类接口与用法大总结(其中涉及STL基础)

目录 1.string类的本质 2.string类的构造 1.普通构造 2.功能型构造 1.拷贝构造功能型 2.带参构造功能型 3.其余构造 3.operator[] 4.迭代器&#xff08;iterator&#xff09; 1.概念 2.改变string对象本身 3.正向迭代器&#xff08;iterator&#xff09; 4.反向迭代…

BLE 协议之链路层

目录 一、前言二、状态和角色三、Air Interface Packets1、Preamble 字段2、Access Address 字段2.1 静态地址2.2 私有地址 3、PDU 字段3.1 Advertising Channel PDU3.1.1 Header 字段3.1.2 Payload 字段 3.2 Data Channel PDU3.2.1 Header 字段3.2.2 Payload 字段 4、CRC 字段…

YOLO交通目标识别数据集(红绿灯-汽车-自行车-卡车等)

YOLO交通目标识别 数据集 模型 ui界面 ✓图片数量15000&#xff0c;xml和txt标签都有&#xff1b; ✓class&#xff1a;biker&#xff0c;car&#xff0c;pedestrian&#xff0c;trafficLight&#xff0c;trafficLight-Green&#xff0c;trafficLight-GreenLeft&#xff0c; t…

OpenCV特征检测(3)计算图像中每个像素处的特征值和特征向量函数cornerEigenValsAndVecs()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算图像块的特征值和特征向量用于角点检测。 对于每一个像素 p &#xff0c;函数 cornerEigenValsAndVecs 考虑一个 blockSize blockSize 的邻…

Ferret-UI——于移动用户界面的多模态大规模语言模型

概述 论文地址&#xff1a;https://arxiv.org/abs/2404.05719 移动应用程序已成为我们日常生活中不可或缺的工具&#xff0c;涉及信息搜索、预订和娱乐等多个领域。我们通常会目测屏幕&#xff0c;然后根据自己的目的执行必要的操作。将这一过程自动化可以让用户更轻松地实现…

【秋招笔试-支持在线评测-试读版】9.19小米秋招(已改编)-三语言题解

&#x1f36d; 大家好这里是 春秋招笔试突围&#xff0c;一起备战大厂笔试 &#x1f4bb; ACM金牌团队&#x1f3c5;️ | 多次AK大厂笔试 &#xff5c; 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; 和 手里的小花花…

阿贝云评测:免费虚拟主机和免费云服务器体验分享

最近我有幸体验了阿贝云提供的免费虚拟主机和免费云服务器&#xff0c;在这里分享一下我的使用体验。首先我想说的是&#xff0c;阿贝云的服务真的很不错。他们提供的免费虚拟主机性能稳定&#xff0c;速度快&#xff0c;对于刚开始建站的小伙伴来说是一个很好的选择。免费云服…

阿里发布史上最大规模开源全家桶!千问2.5系列发布

实话说&#xff0c;我一直没想明白阿里为什么会在大模型这个赛道&#xff0c;成为中国版的Meta。 扎克伯格被问及为什么要做开源大模型时说&#xff0c;“我们的商业模式并不是靠卖模型赚钱“。显然&#xff0c;Meta没有云平台产品&#xff0c;就算要卖模型赚钱&#xff0c;效…

前端vue-单选按钮的实现

要把name“sex”和value"男" 和 要把name“sex”和value"女"写上&#xff0c;然后在各自的标签内部写上v-model绑定属性。data中定义v-model的绑定值&#xff0c;后面的值是默认选中的男或者女性。

B站前端错误监控实践

前言 从23年开始&#xff0c;我们团队开始前端错误监控方向的开发。经历了一些列的迭代和发展&#xff0c;从监控SDK、上报、数据治理、看板集成、APM自研可视化初步完成了一条完整且适合B站前端监控。 截止目前(2024.08.01)&#xff0c;前端监控在B站85%以上的业务线&#xf…

Percona发布开源DBaaS平台;阿里云RDS发布全球多活数据库(GAD);Redshift支持自然语言生成SQL

重要更新 1. 云栖大会于本周四/五在杭州举行&#xff0c;周五上午云栖主论坛阿里云数据库负责人李飞飞将发表《从数据到智能&#xff1a;DataAI驱动的云原生数据库》演讲&#xff0c;另外&#xff0c;还有多场次的数据库专场&#xff0c;感兴趣的可以现场或在线观看&#xff1a…

前端vue-自己封装组件并使用三步走

在components下&#xff0c;创建.vue文件&#xff0c;里面正常写样式什么的&#xff0c;在需要引用的文件内先在script标签内引入在components下创建的组件&#xff0c;再导出处使用&#xff0c;再在templete标签内直接使用自己封装的组件。

SQL - 基础语法

SQL作为一种操作命令集, 以其丰富的功能受到业内人士的广泛欢迎, 成为提升数据库操作效率的保障。SQL Server数据库的应用&#xff0c;能够有效提升数据请求与返回的速度&#xff0c;有效应对复杂任务的处理&#xff0c;是提升工作效率的关键。 由于SQL Servers数据库管理系统…