经典算法思想--并查集
前言
(最近在学习Java,所有函数都是用Java语言来书写的)
- 前言部分是一些前提储备知识
在并查集(Union-Find)数据结构中,rank(中文称为“秩”)是用来表示树的高度或深度的一种辅助信息。它的主要作用是优化合并操作,以保持并查集的结构尽可能扁平,从而提高查询效率。
秩的具体定义
-
秩(Rank):
- 秩用来衡量一个节点在树中的相对高度。具体来说,秩通常指的是树的“深度”或“高度”。初始时,每一个节点的秩可以设定为 0。若秩突然增加,说明该节点的子树的深度在增加。
-
合并时使用:
- 在进行
union
操作时,- 若两个集合的根节点的秩不同,我们将根节点秩更小的树连接到秩更大的树上。
- 当两个根节点的秩相同时,将任意一棵树连接到另一棵树上,并将新根节点的秩值加一。这可以避免树过高。
- 在进行
合并示例
- 初始状态:
- 每个元素最初是自己父节点(根节点),并且秩都是 0。
Element: 1 2 3 4
Parent: [1, 2, 3, 4] // 表示每个元素都是自己的父节点
Rank: [0, 0, 0, 0] // 秩初始化为 0
- 调用
union(1, 2)
:- 首先找出元素 1 和 2 的根节点。由于它们各自是自己的根节点,所以
find(1)
返回 1,find(2)
返回 2。 - 由于根节点不同(1 不是 2),可以将它们合并。
- 因为它们的秩相等,都为 0,所以可以任意选择一个作为新的根节点,此处选择把 2 的父节点设为 1,并将 1 的秩增加 1。
- 首先找出元素 1 和 2 的根节点。由于它们各自是自己的根节点,所以
Union(1, 2):
Element: 1 2
Parent: [1, 1] // 2 的父节点指向 1(1成为新的根)
Rank: [1, 0] // 将 1 的秩增加 1
- 状态更新后的图示:
1/2
- 接着进行
union(2, 3)
:- 查找根节点:
find(2)
返回 1(2 的父节点是 1),find(3)
返回 3。 - 1 和 3 是不同的根节点,可以合并。
- 由于 1 的秩(1)大于 3 的秩(0),所以将 3 的父节点指向 1。
- 查找根节点:
Union(2, 3):
Element: 1 2 3
Parent: [1, 1, 1] // 3 的父节点指向 1
Rank: [1, 0, 0] // 秩不变
- 最后进行
union(1, 3)
:- 查找根节点:
find(1)
返回 1,find(3)
返回 1,所以它们已经在同一个集合中,什么也不做。
- 查找根节点:
总结
在进行 union
操作时,我们首先需要找到两个元素的根节点。如果它们的根节点不同,就可以将它们合并。如果相同,则表示它们已经在同一个集合中。
以上是对 union
操作的正确描述和过程演示,谢谢你的耐心!如果你还有其他问题,请随时问我。
并查集简介
并查集(Union-Find)是一种用于处理不重叠集合的数据结构。它特别适合用于解决有关集合连接、合并和查询的问题。并查集通常包括两个主要操作:
- Find: 查找元素所属的集合。
- Union: 合并两个集合。
并查集的主要思想
- 快速查找:利用树形结构可以快速找到集合的根代表。
- 树的优化:
- 路径压缩:在查找过程中,将访问的每个节点直接连接到根节点,从而优化树的结构,使得树变平,查找效率更高。
- 按秩合并:在合并过程中,总是将秩(rank)低的树连接到秩高的树,防止树变得过于高。
路径压缩
路径压缩是在 find
操作中进行的优化。当我们执行查找时,我们将经过的所有节点直接连接到根节点。这减少了树的高度,从而提高随后的查找效率。
路径压缩示例代码
public int find(int[] parent, int index) {if (parent[index] != index) {// 递归地找到根节点,并在回溯阶段将当前节点直接连接到根节点parent[index] = find(parent, parent[index]);}return parent[index];
}
路径压缩示例说明
假设我们有初始集合 [1, 2, 3, 4, 5],其树结构如下:
1
|
2 - 3 - 4|5
- 假设
parent = [0, 1, 1, 2, 3, 3]
,这意味着 2 和 3 的父节点是 1,5 的父节点是 3。 - 调用
find(parent, 5)
时,路径为[5 -> 3 -> 1]
。 - 路径压缩将改变
5
的父节点直接连接到1
,结果是parent = [0, 1, 1, 1, 3, 1]
。 - 树在调用 find 后,将如下一步变得更平展:
1
|
2 - 3 - 4 - 5
按秩合并
按秩合并是在 union
操作中进行的优化,目的是尽可能保持树的扁平。所谓“秩”,在这里可以理解为树的高度。
按秩合并示例代码
public void union(int[] parent, int[] rank, int index1, int index2) {int root1 = find(parent, index1);int root2 = find(parent, index2);if (root1 != root2) {if (rank[root1] > rank[root2]) {parent[root2] = root1; // root2合并到root1上} else if (rank[root1] < rank[root2]) {parent[root1] = root2; // root1合并到root2上} else {parent[root2] = root1; // 随意合并并增加其中一个的rankrank[root1]++;}}
}
按秩合并示例说明
假设我们有两个树:
- 第一棵树的根为 A,秩为 2。
- 第二棵树的根为 B,秩为 3。
调用 union(parent, rank, A, B)
时:
- 由于 B 的秩大于 A,A 被合并到 B 上,这样就避免了增加树的高度。
- 在 rank 相同的情况下,任选一个作为新根,并增加该树的 rank。
结合路径压缩和按秩合并
结合这两个优化策略,在大多数实际应用中,find
和 union
操作可以接近于常数时间复杂度。这种效率使得并查集在处理大量集合合并和查找操作时极为高效。使用上面的两个优化版本的代码,能够保证树的高度不会过于增长,从而优化操作效率。
并查集的应用
并查集被广泛应用于很多算法与实际问题中,比如:
并查集(Union-Find)数据结构在计算机科学中有着广泛的应用,特别是在处理图相关的问题时。下面我将介绍几个具体的实例问题,并展示如何使用并查集来解决这些问题。
1. 网络连接
问题:判断网络中两个节点是否连通。
实例:假设我们有一个网络系统,每一对节点之间有或没有直接连接。如果两个节点是连通的,则它们之间存在一条直接或间接路径。
代码:
public class Network {private int[] parent;private int[] rank;public Network(int size) {parent = new int[size];rank = new int[size];for (int i = 0; i < size; i++) {parent[i] = i;rank[i] = 0;}}public int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]);}return parent[x];}public void union(int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX != rootY) {if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else {parent[rootY] = rootX;rank[rootX]++;}}}public boolean isConnected(int x, int y) {return find(x) == find(y);}public static void main(String[] args) {Network network = new Network(5);network.union(0, 1);network.union(1, 2);System.out.println(network.isConnected(0, 2)); // 输出 trueSystem.out.println(network.isConnected(0, 3)); // 输出 false}
}
2. 图的连通分量
问题:找出图中的连接组件,即连通分量。
实例:给定一个无向图,找出所有的连通分量。
代码:
import java.util.*;public class Graph {private int[] parent;private int[] rank;public Graph(int size) {parent = new int[size];rank = new int[size];for (int i = 0; i < size; i++) {parent[i] = i; // 初始化,每个节点的父节点指向自己 rank[i] = 0; // 秩初始化为0 }}// 查找并进行路径压缩 public int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]);}return parent[x];}// 联合操作 public void union(int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX != rootY) {if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else {parent[rootY] = rootX;rank[rootX]++;}}}// 计算连通分量的数量 public int countComponents() {Set<Integer> uniqueRoots = new HashSet<>();for (int i = 0; i < parent.length; i++) {uniqueRoots.add(find(i));}return uniqueRoots.size();}// 主函数用于测试 public static void main(String[] args) {Graph graph = new Graph(5);graph.union(0, 1);graph.union(1, 2);graph.union(3, 4);System.out.println(graph.countComponents()); // 输出 2,表明有两个连通分量 }
}
3. 最小生成树算法中的环检测
问题:在 Kruskal 算法中,检测添加的边是否会形成环。
实例:找到一个连通无向图的最小生成树。
代码:
import java.util.*;class Edge implements Comparable<Edge> {int src, dest, weight;Edge(int src, int dest, int weight) {this.src = src;this.dest = dest;this.weight = weight;}@Overridepublic int compareTo(Edge compareEdge) {return this.weight - compareEdge.weight;}
}public class KruskalMST {private List<Edge> edges;private int vertices;public KruskalMST(int vertices) {this.vertices = vertices;edges = new ArrayList<>();}public void addEdge(int src, int dest, int weight) {edges.add(new Edge(src, dest, weight));}public List<Edge> findMST() {Collections.sort(edges);int[] parent = new int[vertices];int[] rank = new int[vertices];// Initialize parent and rank arrays for (int i = 0; i < vertices; i++) {parent[i] = i;rank[i] = 0;}List<Edge> mst = new ArrayList<>();// Traverse through all edges for (Edge edge : edges) {int rootSrc = find(parent, edge.src);int rootDest = find(parent, edge.dest);// Check if the selected edge forms a cycle if (rootSrc != rootDest) {mst.add(edge);union(parent, rank, rootSrc, rootDest);}}return mst;}private int find(int[] parent, int x) {if (parent[x] != x) {parent[x] = find(parent, parent[x]);}return parent[x];}private void union(int[] parent, int[] rank, int x, int y) {int rootX = find(parent, x);int rootY = find(parent, y);if (rootX != rootY) {if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else {parent[rootY] = rootX;rank[rootX]++;}}}// 主函数用于测试 public static void main(String[] args) {KruskalMST graph = new KruskalMST(4);graph.addEdge(0, 1, 10);graph.addEdge(1, 3, 15);graph.addEdge(0, 2, 6);graph.addEdge(2, 3, 4);List<Edge> mst = graph.findMST();for (Edge edge : mst) {System.out.println(edge.src + " - " + edge.dest + ": " + edge.weight);}// 输出: // 2 - 3: 4 // 0 - 2: 6 // 0 - 1: 10 // 最小生成树的构建避免了环的形成。 }
}
4. 动态连通性问题
问题:处理动态连通性查询和合并操作。
实例:在一种动态环境中运行,始终保持对连通性的跟踪。
代码:
public class DynamicConnectivity {private int[] parent;private int[] rank;public DynamicConnectivity(int size) {parent = new int[size];rank = new int[size];for (int i = 0; i < size; i++) {parent[i] = i; // 初始化每个节点的父节点为自己 rank[i] = 0; // 初始化秩为0 }}// 查找并进行路径压缩 public int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]);}return parent[x];}// 联合操作 public void union(int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX != rootY) {if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else {parent[rootY] = rootX;rank[rootX]++;}}}// 检查两个节点是否在同一连通分量内 public boolean isConnected(int x, int y) {return find(x) == find(y);}// 主函数用于测试 public static void main(String[] args) {DynamicConnectivity dc = new DynamicConnectivity(5);dc.union(0, 1);dc.union(1, 2);System.out.println(dc.isConnected(0, 2)); // 输出 true System.out.println(dc.isConnected(0, 3)); // 输出 false dc.union(2, 3);System.out.println(dc.isConnected(0, 3)); // 输出 true }
}
这些实例展示了如何应用并查集解决一些常见的动态连通性问题,以及如何通过高效的合并和查找来提高性能。
5.思考应用
有些问题像动态连通性、社交网络中的朋友圈判断等都可以使用并查集来高效解决。特别是在需要动态地合并集合并频繁地查询彼此是否连通时,并查集是理想的选择。例如:
- 社交网络:确定任何两个人是否属于同一个社交圈。
- 电网连通性:判断两座城市是否通过电网连接。
通过这些例子,可以看到并查集以其高效的合并和查询能力,从简单集合操作到复杂图结构都有十分广泛的应用。
相关文章:
经典算法思想--并查集
前言 (最近在学习Java,所有函数都是用Java语言来书写的)前言部分是一些前提储备知识 在并查集(Union-Find)数据结构中,rank(中文称为“秩”)是用来表示树的高度或深度的一种辅助信息…...
挑战Java面试题复习第2天,百折不挠
挑战第 2 天 ArrayList和linkedList的区别HashMap和HashTable的区别Collection 与 Collections 的区别Java的四种引用泛型常用特点 ArrayList和linkedList的区别 底层数据结构: ArrayList:基于动态数组实现,支持快速随机访问。LinkedList&a…...
【vue之道】
vue之道 1. 一生二,二生万物思想2. 变化之律3. 变化之实在哪?4.而后学于形乃已!4.1 展示之形变4.2 动之气谓之指令4.3 血之养分的载体,于vue之绑定载具4.4 vue之道(万法规一篇) 1. 一生二,二生万…...
基于麻雀优化算法SSA的CEEMDAN-BiLSTM-Attention的预测模型
往期精彩内容: 时序预测:LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较 全是干货 | 数据集、学习资料、建模资源分享! EMD、EEMD、FEEMD、CEEMD、CEEMDAN的区别、原理和Python实现(一)EMD-CSDN博客 EMD、EEM…...
Linux:指令再认识
文章目录 前言一、知识点1. Linux下一切皆文件,也就是说显示器也是一种文件2. 指令是什么?3. ll 与 ls -l4. 日志5. 管道6. 时间戳 二、基本指令1. man指令2. cp指令3. mv指令4. 查看文件1)cat/tac指令——看小文件2)more/less指令…...
PHP如何抛出和接收错误
在PHP中,抛出和接收错误通常涉及异常处理机制,以及错误和异常的处理函数。以下是如何在PHP中抛出和接收错误的详细指南: 抛出错误(异常) 在PHP中,你可以使用throw关键字来抛出一个异常。这通常在你检测到…...
计算机网络:网络层 —— IPv4 地址的应用规划
文章目录 IPv4地址的应用规划定长的子网掩码变长的子网掩码 IPv4地址的应用规划 IPv4地址的应用规划是指将给定的 IPv4地址块 (或分类网络)划分成若干个更小的地址块(或子网),并将这些地址块(或子网)分配给互联网中的不同网络,进而可以给各网络中的主机…...
Mongodb命令大全
Mongodb命令大全 一、数据库相关命令二、集合相关命令三、文档(数据)相关命令1、_id 字段说明2、查询2.1、 查询操作符2.2、内嵌文档查询2.3、数组文档查询2.4、去重查询2.5、查询排序 sort2.6、分页查询2.7、指定列投影查询返回2.8、查询统计个数 count 3、聚合查询3.1、查询用…...
宇视设备视频平台EasyCVR视频融合平台果园/鱼塘/养殖场/菜园有电没网视频监控方案
在那些有电无网的偏远地区,如果园、鱼塘、养殖场或菜园,视频监控的实现面临着独特的挑战。宇视设备视频平台EasyCVR提供了一种创新的解决方案,通过结合太阳能供电和4G摄像头技术,有效地解决了这些场景下的监控需求。 在有电没网的…...
面试题:ABCD四个线程,A线程最后执行
我觉得是一个很高频的面试题,ABCD四个线程,A线程要等到BCD线程执行完再执行,怎么做 因为我刚复习完AQS,所以立马想到了CountDownLatch,但是看面试官反应他最想听到的应该是join方法,所以面试后就总结了几种…...
代码随想录算法训练营第46期Day43
leetcode.322零钱兑换 class Solution { public: //无限个硬币->完全背包int coinChange(vector<int>& coins, int amount) {vector<int> dp(10010,INT_MAX);//dp代表的在某个数值下最小的硬币数,要求是最小的硬币数,所以初始值要尽可…...
前端处理API接口故障:多接口自动切换的实现方案
因为在开发APP,一个接口如果不通(被挂了)又不能改了重新打包让用户再下载软件更新,所以避免这种情况,跟后端讨论多备用接口地址自动切换的方案,自动切换到备用的接口地址,并保证后续所有的请求都…...
多租户架构的全景分析(是什么?基本概念、实现策略、资源管理和隔离、数据安全与隔离、性能优化、扩展性与升级、案例研究)
文章目录 1. 多租户的基本概念2. 多租户的实现策略2.1 独立数据库模式2.2 共享数据库-独立Schema模式2.3 共享数据库-共享Schema模式 3. 资源管理和隔离4. 数据安全与隔离5. 性能优化6. 扩展性与升级7. 案例研究总结 多租户架构在云计算和SaaS应用中越来越流行,因为…...
Git使用问题汇总附带解决方法(持续更新)
Git使用问题汇总附带解决方法 一 git pull 代码时报错: Auto packing the repository in background for optimum performance. See “git help gc“ 一 git pull 代码时报错: Auto packing the repository in background for optimum performance. See …...
Spring Boot驱动的植物健康监测革命
1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理植物健康系统的相关信息成为必然。开发合适…...
element 中 el-dialog 在不同的文件中使用
在实际中工作,我们经常需要使用 el-dialog 来做一个弹框的功能。最常见的就是在父组件中点击一个按纽,然后弹出一个框。而这个框就是子组件。同时,父子组件是分布在不同的文件中。 <!--父组件--> <template> <div> <…...
QT中采用QCustomPlot 实现将buffer中的数据绘制成折线图,并且图形随着数据更新而更新
QT中采用QCustomPlot 实现将buffer中的数据绘制成折线图,并且图形随着数据更新而更新 为了在 Qt 中将缓冲区的数据动态绘制成折线图,并随着数据的更新而实时更新,可以使用 QCustomPlot 或 Qt 自带的绘图功能,比如 QGraphicsView,或者在更简单的情况下使用 QPainter 在 QW…...
1688API商品详情接口如何获取
获取 1688API商品详情接口主要有以下步骤: 一、注册开发者账号: 访问 1688 开放平台,进行开发者账号注册。这是获取 API 接口使用权限的第一步,注册信息要确保真实准确。 二、了解接口规范和政策: 在 1688 开放平台…...
pytorch + d2l环境配置
文章目录 前言一、安装软件二、配置具体环境 前言 一直想写一篇 pytorch d2l的深度学习环境配置。但一直都不是很顺利,配置过很多次,都会遇到一些各种依赖项的兼容性问题。但这个是没有办法的,各种开源包都在不断维护过程中,版本…...
Go使用exec.Command() 执行脚本时出现:file or directory not found
使用 Go 提供的 exec.Command() 执行脚本时出现了未找到脚本的 bug,三个排查思路 : exec.Command(execName, args…) 脚本名字不允许相对路径 exec.Command(execName, args…) execName 只能有脚本名,不允许出现参数 如果你是使用 Windows …...
细节性知识(宏定义解析与宏的外部引用)
目录 一、问:#define N 50 中的N可以用来做运算比较吗? 二、宏定义怎么外部引用? 例子 总结 一、问:#define N 50 中的N可以用来做运算比较吗? 解析:在C语言中,#define N 50 是一个预处理指…...
面试中的JVM:结合经典书籍的深度解读
写在前面 🔥我把后端Java面试题做了一个汇总,有兴趣大家可以看看!这里👉 ⭐️在无数次的复习巩固中,我逐渐意识到一个问题:面对同样的面试题目,不同的资料来源往往给出了五花八门的解释&#…...
使用语音模块的开发智能家居产品(使用雷龙LSYT201B 语音模块)
在这篇博客中,我们将探讨如何使用 LSYT201B 语音模块 进行智能设备的语音交互开发。通过这个模块,我们可以实现智能设备的语音识别和控制功能,为用户带来更为便捷和现代的交互体验。 1. 语音模块介绍 LSYT201B 是一个基于“芯片算法”的语音…...
深入理解支持向量机:从基本原理到实际应用
第6章 支持向量机 在本章中,我们将深入探讨支持向量机(SVM)这一强大的分类算法。SVM在模式识别和机器学习领域广泛应用,尤其在处理高维数据时表现出色。我们将依次讨论间隔与支持向量、对偶问题、核函数、间隔与正则化、支持向量…...
每天一题:洛谷P2041分裂游戏
题目描述 有一个无限大的棋盘,棋盘左下角有一个大小为 n 的阶梯形区域,其中最左下角的那个格子里有一枚棋子。你每次可以把一枚棋子“分裂”成两枚棋子,分别放在原位置的上边一格和右边一格。(但如果目标位置已有棋子,…...
简单的 curl HTTP的POSTGET请求以及ip port连通性测试
简单的 curl HTTP的POST&GET请求以及ip port连通性测试 1. 需求 我们公司有一个演示项目,需要到客户那边进行项目部署,项目部署完成后我们需要进行项目后端接口的测试功能,但是由于客户那边么有条件安装类似于postman这种的测试工具&am…...
ubuntu下快捷键启动程序
背景:公司自开发的软件,经常需要启动,每次去找目录启动很麻烦,所以想快捷启动 方法1: 通过编辑.baserc启动 例如启动程序是toolA, 放在/home/user/software/目录下,那么在~/.baserc里面加入一行代码 al…...
Yii2 init 初始化脚本分析
脚本目的: init 脚本主要的作用是:从 environments 目录中复制配置文件,确保应用适配不同环境(例如开发、生产环境等)。 工作流程: 获取 $_SERVER 的 argv 参数 加载 environments/index.php 文件&#…...
深入理解gPTP时间同步过程
泛化精确时间协议(gPTP)是一个用于实现精确时间同步的协议,特别适用于分布式系统中需要高度协调的操作,比如汽车电子、工业自动化等。 gPTP通过同步主节点(Time Master)和从节点(Time Slave)的时钟,实现全局一致的时间参考。 以下是gPTP实现主从时间同步的详细过程:…...
基于阿里云服务的移动应用日志管理方案—日志的上传、下载、存档等
前言 如题,基于阿里云服务(ECS、OSS)实现 APP 的用户日志上传以及日志下载的功能,提高用户反馈问题到研发去分析、定位、解决问题的整个工作流的效率。 术语 ECS: 云服务器ECS(Elastic Compute Service)…...
有哪些做批发的网站/域名查询 站长查询
以织梦官方站为例,我们采集站长学院下的PHP教程栏目,打开列表地址http://www.dedecms.com/web-art/PHP_jiaocheng。 登录后台,进入“采集节点管理”,新建一个节点,选择内容模型为“普通文章”。1.设置节点基本信息先填…...
西藏自治区住房和城乡建设厅网站/windows优化大师官方免费
一、题目 问题描述输入两个矩阵,分别是m*s,s*n大小。输出两个矩阵相乘的结果。 输入格式第一行,空格隔开的三个正整数m,s,n(均不超过200)。接下来m行,每行s个空格隔开的整数,表示矩阵A…...
普通网站做/百度官网进入
vs更新2017最新版本后,项目调试浏览器莫名其妙出现闪退,每次都TMD想打人… 先说下vs更新后出现哪些反人类的操作: –1.每调试一个项目就会单独重启浏览器,而且该调试浏览器关闭后,vs调试也会关闭。 –2.在web中调试&…...
保温管有哪些网站做/google chrome官网下载
Dan Griffin本文讨论: 新的凭据提供程序体系结构 为什么弃用了基于 GINA 的身份验证 多因素 (Multi-factor) 身份验证 开发和调试凭据提供程序 本文使用了以下技术: Windows Vista、C目录 新旧两种体系结构的比较 混合凭据提供程序 要求 设计 混合凭据提供程序 混合方式的实现…...
html5笑话网站源码/谷歌的推广是怎么样的推广
1:关系型数据库。 比如常见的 mysql、oracle、sqlserver 等,这类数据库基本上都支持 jdbc 或者 odbc 链接,所以报表工具基本上都支持。 2:文本文件 常见的 txt、csv、excel 等文本文件,这类文本文件就看各类报表的支…...
网站建设 推广/搜索词分析
深深以为,遇见一个好的文章不容易,希望自己也能用心填坑。 首先来说读取用户信息,之前是用getUserInfo(),但在2018年4月30日之后,该接口不适用于开发版和测试版,正式上线的小程序不受影响。很不幸ÿ…...