关注、星标公众号,直达精彩内容

来源: https://www.cnblogs.com/NoneID

整理:李肖遥

1. 什么是平衡二叉树

平衡二叉树,我们也称【二叉平衡搜索树/AVL】,树中任何节点的两个子树的高度最大差别为1,巴拉巴拉。。。(https://baike.baidu.com/item/AVL树/10986648?fr=aladdin)

但是有个注意的点: 平衡二叉树的前提是 二叉排序树(https://baike.baidu.com/item/二叉搜索树/7077855?fr=aladdin)

这篇博客主要总结平衡二叉树,所以,二叉排序树知识不会提及,但是会用到。

如果想要看 排序二叉树调整为 平衡二叉树 旋转相关内容的,调整至 第5节。

平衡二叉树

非平衡二叉树

最小不平衡子树节点为 130

左子树深度为 1,右子树深度为3 ,其差值大于1,所以不平衡

2. 如何判断二叉树最小不平衡子树

最小不平衡子树为 130 这颗子树(黄色标注)

判定最小不平衡子树的关键就在于,判断 这棵树的左子树 和 右字数 深度之差是否大于1,若大于1 ,则证明该树不平衡

检查二叉树是否平衡函数代码实现

typedef struct {int data; // 数据节点struct TreeNode *left; // 指向左子树struct TreeNode *right; // 指向右子树
} TreeNode , *PTreeNode;// 记录平衡二叉树
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);
}

程序执行结果

# gcc -w -g -std=c11 BalanceTree.c
#
# ./a.out
当前二叉树遍历
前序遍历: 580    130     80      160     150     158     210     1590    900     2100    1900
中序遍历: 80     130     150     158     160     210     580     900     1590    1900    2100
二叉树不平衡,不平衡子树根节点为: 130
#

3. 二叉树不平衡情况

在一颗平衡二叉树的前提下,插入和删除一个节点,都有可能会引起二叉树不平衡,不平衡的情况主要有以下四种

左左更高

左右更高

右左更高

右右更高

4. 判断不平衡二叉树哪边高

如上图红色所示,可以先根据最小不平衡二叉树左子树或者右子树高,上图所示,为右子树高,则将最小不平衡二叉树的右子树作为树根节点,继续判断子树的左子树或者右子树高。
比如上图的结果是右左较高,若进行调整的话,为 先让不平衡子树右节点的树先向右旋转,然后再向左旋转

判断不平衡二叉树哪边高代码实现

typedef struct {int data; // 数据节点struct TreeNode *left; // 指向左子树struct TreeNode *right; // 指向右子树
} TreeNode , *PTreeNode;// 记录平衡二叉树
bool BalanceTrue = false;
// 最小不平衡子树地址
TreeNode *rjt = NULL;// 返回二叉树树高
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);
}int main() {/*  构建二叉树判断平衡,获取最小不平衡子树, 将数据存入 rjt 中输出二叉树 前序/中序*/if (BalanceTrue) {printf("二叉树不平衡,不平衡子树根节点为: %d\n",rjt->data);} else {return 0;};int ll = treeHeight(rjt->left);int rr = treeHeight(rjt->right);if (1 < ll - rr) {printf("左子树高\n");TreeNode *rjt_ll = rjt->left;int child_ll = treeHeight(rjt_ll->left);int child_rr = treeHeight(rjt_ll->right);if (child_ll > child_rr) {printf("左子树更高\n");} else if (child_rr > child_ll) {printf("右字数更高");}} else if (1 <  rr - ll) {printf("右子数高\n");TreeNode *rjt_rr = rjt->right;int child_ll = treeHeight(rjt_rr->left);int child_rr = treeHeight(rjt_rr->right);if (child_ll > child_rr) {printf("左子树更高\n");} else if (child_rr > child_ll) {printf("右字数更高");}}return 0;
}

输出

# gcc BalanceTree.c -w -g -std=c11
#
# ./a.out
当前二叉树遍历
前序遍历:130    80      160     150     158     210
中序遍历:80     130     150     158     160     210
二叉树不平衡,不平衡子树根节点为: 130
右子数高
左子树更高
#

5. 如何调整平衡二叉树

所谓的旋转,其实是修改指针指向的值,仅此而已。

二叉树不平衡有四种情况

左左更高

原始二叉树,若要调整为平衡二叉树,需要整棵树向右旋转

调整1:整棵树向右旋转

左右更高

原始二叉树,若要调整为平衡二叉树,需要 先让不平衡子树左节点先向左旋转,然后再向右旋转

调整1: 先让不平衡子树左节点的树先向左旋转

调整2: 整棵树向右

右左更高

原始二叉树,若要调整为平衡二叉树,需要 先让不平衡子树右节点的树先向右旋转,然后再向左旋转

调整1: 先让不平衡子树右节点的树先向右旋转

调整2: 整棵树向左

右右更高

原始二叉树,若要调整为平衡二叉树,需要 整棵树向左旋转

调整1: 整棵树向左旋转

全部代码

# include <stdio.h>
# include <stdbool.h>
# include <stdlib.h>
# include <math.h>typedef struct {int data; // 数据节点struct TreeNode *left; // 指向左子树struct TreeNode *right; // 指向右子树
} TreeNode , *PTreeNode;// 记录平衡二叉树
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;
}// 左左旋转
//
// 不平衡二叉树
//       70
//      /   \
//    50    80
//   /  \
//  40  60
//  /
// 30
//
// 旋转后平衡二叉树(向右旋转)
//
//    50
//  /   \
// 40    70
// /     /  \
//30   60    80
//
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;}}// 左右旋转
//不平衡二叉树
//      70
//     /   \
//    50    80
//    / \
//   40 60
//  /
// 55
//
//左子树向左
//      70
//     /   \
//    60    80
//    /
//   50
//  /  \
// 40  55
//
//
// 整棵树向右
//
//     60
//    /   \
//   50    70
//  /  \     \
// 40  55    80
//
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;}
}// 右左旋转
//不平衡二叉树
//   70
//  /  \
// 50   80
//     /  \
//    75  88
//     \
//     77
//
//左子树向右
//   70
//  /  \
// 50   75
//     /  \
//    77  80
//         \
//         88
//
//
//
//整棵树向左
//     75
//    /  \
//   70  80
//  /  \   \
// 50  77  88
//
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;}
}// 右右旋转
//
// 不平衡二叉树
//  70
// /  \
//50   80
//    /  \
//   75  88
//      /
//     85
//
//
//
//向左旋转
//    80
//   /  \
//  70   88
// /  \   /
//50  75 85
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;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));Node->data = data;if (NULL == Node) {printf("申请新节点内存失败\n");return root;}// 链接节点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);sleep(1);}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);sleep(1);}printf("\n当前二叉树遍历\n");printf("前序遍历:");Print1(root);printf("\n");printf("中序遍历:");Print2(root);printf("\n");return 0;
}

程序执行结果

# gcc BalanceTree.c -w -g -std=c11
#
# ./a.out
平衡二叉树插入测试
插入数据: 65
插入数据: 60
插入数据: 70
插入数据: 55
插入数据: 40
二叉树不平衡,最小不平衡子树数据结点: 60
左左旋转,非根节点
二叉树调整平衡后数据结点:
前序遍历:65     55      40      60      70
中序遍历:40     55      60      65      70插入数据: 63
二叉树不平衡,最小不平衡子树数据结点: 65
左右旋转,是根节点
二叉树调整平衡后数据结点:
前序遍历:60     55      40      65      63      70
中序遍历:40     55      60      63      65      70插入数据: 69
插入数据: 66
二叉树不平衡,最小不平衡子树数据结点: 70
左左旋转,非根节点
二叉树调整平衡后数据结点:
前序遍历:60     55      40      65      63      69      66      70
中序遍历:40     55      60      63      65      66      69      70插入数据: 68
二叉树不平衡,最小不平衡子树数据结点: 65
右左旋转,非根节点
二叉树调整平衡后数据结点:
前序遍历:60     55      40      66      65      63      69      68      70
中序遍历:40     55      60      63      65      66      68      69      70插入数据: 77
二叉树不平衡,最小不平衡子树数据结点: 60
右右旋转,是根节点
二叉树调整平衡后数据结点:
前序遍历:66     60      55      40      65      63      69      68      70      77
中序遍历:40     55      60      63      65      66      68      69      70      77当前二叉树遍历
前序遍历:66     60      55      40      65      63      69      68      70      77
中序遍历:40     55      60      63      65      66      68      69      70      77平衡二叉树删除测试
删除节点: 70
删除节点: 55
删除节点: 40
二叉树不平衡,最小不平衡子树数据结点: 60
右左旋转,非根节点
二叉树调整平衡后数据结点:
前序遍历:66     63      60      65      69      68      77
中序遍历:60     63      65      66      68      69      77当前二叉树遍历
前序遍历:66     63      60      65      69      68      77
中序遍历:60     63      65      66      68      69      77
#

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

平衡二叉树 C语言代码实现相关推荐

  1. Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树、二叉查找树的插入节点、二叉查找树的删除、二叉树的遍历、平衡二叉树)C 语言实现

    Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树.二叉查找树的插入节点.二叉查找树的删除.二叉树的遍历.平衡二叉树)C++语言实现 目录 树的基础知识 1.二叉树的遍-前序 ...

  2. 遗传算法c语言程序,遗传算法c语言代码.doc

    遗传算法c语言代码 遗传算法代码 #include #include #include #include #include struct group //染色体的结构 { int city[citie ...

  3. 071_html语言代码

    1. ISO语言代码 1.1. 国际标准化组织(International Organization for Standardization, ISO)简称ISO, 是一个全球性的非政府组织, 是国际 ...

  4. 嵌入式C语言代码规范

    C语言代码规范 参考安富莱C语言编码规范 1.文件与目录 1.文件及目录的命名规定可用的字符集是[A-Z:a-z:0-9:._-]. 2.源文件名后缀用小写字母 .c 和.h. 3.文件的命名要准确清 ...

  5. java 与c 运行效率_Java语言与C语言代码运行效率的比较

    <Java语言与C语言代码运行效率的比较>由会员分享,可在线阅读,更多相关<Java语言与C语言代码运行效率的比较(2页珍藏版)>请在人人文库网上搜索. 1.Java语言与C语 ...

  6. Python语言学习:python语言代码调试—异常处理之详细攻略

    Python语言学习:python语言代码调试-异常处理之详细攻略 目录 python语言代码调试-异常处理 异常捕捉可以使用 try/except 语句 相关文章 Python3 错误和异常 | 菜 ...

  7. 编程笔试(解析及代码实现):猴子吃桃。猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个…的C++、Java、Python、C#等语言代码实现

    编程笔试(解析及代码实现):猴子吃桃.猴子第一天吃了若干个桃子,当即吃了一半,还不解馋,又多吃了一个. 第二天早上又将剩下的桃子吃了一半,还是不过瘾,又多吃了一个.以后每天都吃前一天剩下的一半再加一个 ...

  8. c语言错误 xef代表什么,单片机C语言代码手册 含100多个经典C程序

    1 单片机单片机 C 语言代码手册语言代码手册 1 LED 灯灯 点亮一个点亮一个 LED include void main while 1 P0 0 x01 P2 0 x7d 流水灯闪烁流水灯闪烁 ...

  9. c 语言怎么编译 .dll,将你的 C 语言代码编译成 .NET

    介绍 通常情况下,对于那些使用C语言编程并开始用C#/ VB或一些其他的用于.NET的编译语言编程,那么他们希望或者甚至是需要调用我们用C语言编写的函数代码. 每当我在互联网上钻研,或说要在编译器中使 ...

最新文章

  1. 3164 质因数分解
  2. java9.0.1教学,零基础Java基础教程【9天入门】
  3. JPA中实现单向多对一的关联关系
  4. React ----- 路由懒加载的几种实现方案
  5. Exchange Server 2007迁移Exchange Server 2010 (16)--- OWA重定向
  6. 滑动窗口在重构数据集的作用
  7. 成功的人和不成功的人最大的区别
  8. 任何在aix下面看你的系统是32位还是64位的
  9. R语言︱集合运算——小而美法则
  10. Unity3D 游戏开发构架篇 ——输入控制
  11. 2. PHP 自动转义函数
  12. python 反弹shell,加了UDP
  13. unity3d面试题
  14. 液压与气动传动(PLC控制)
  15. 皮尔兹777301安全继电器
  16. tftpd32更新内核文件
  17. Win10 64位+Mysql5.7+主从同步配置
  18. 2021-09-29 关于间断点相关题目的总结
  19. Unity3d Platformer Pro 2D游戏开发框架使用教程
  20. 插秧诗 - 退步原来是向前

热门文章

  1. 免费虚拟主机怎么申请?
  2. 《第三部分:熟悉Liunx系统+创建虚拟机》
  3. 计算机网络【谢希仁第七版】第六章【应用层】
  4. 浅析HTTPS与SSL原理
  5. window操作系统下SVN通过sync命令增量同步备份,首次全量备份(单机备份双机备份均可)
  6. SON_EXAM考试php,螃蟹学PHP设计模式之模板模式
  7. python绘制笛卡尔心形曲线_如何在python的极图中显示笛卡尔系统?
  8. 什么是AWG(阵列波导光栅)及AWG的基本结构
  9. 当金融风控遇上人工智能,众安金融的实时特征平台实践
  10. 从用户场景来看,什么是推荐系统