前言
还是项目的新需求:每天进行一次更新用户等级,具体等级要求如下:
很明显的是,高等级用户依赖于低等级用户的业绩和社区等级进行升级。也就是说需要从低等级用户开始升级,层层递进到高等级用户。这个时候选择一个合适的数据结构在开发中能省很多功夫,比如多叉树。
多叉树实现
其实不管是初始化树还是遍历的思路跟二叉树都是一样的。
树的节点:
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class UserTreeNode {
private String userId;
private String parentId;
private List<UserTreeNode> hierarchy;
}
public class UserTreeUtil {
/**
* 返回层级结构树
*/
public static List<UserTreeNode> getStructure(List<UserTreeNode> allList, List<UserTreeNode> topList) {
//设置子数据
if (!CollectionUtils.isEmpty(topList)) {
for (UserTreeNode entity : topList) {
//存放结构树字段
entity.setHierarchy(getChild(entity.getUserId(), allList));
}
}
return topList;
}
/**
* 调用递归
*/
private static List<UserTreeNode> getChild(String id, List<UserTreeNode> allList) {
// 子数据
List<UserTreeNode> childList = Lists.newArrayList();
for (UserTreeNode entity : allList) {
// 将父id与传过来的id比较
if (StringUtils.isNotBlank(entity.getParentId())) {
if (entity.getParentId().equals(id)) {
childList.add(entity);
}
}
}
// 子级循环
for (UserTreeNode entity : childList) {
if (StringUtils.isNotBlank(entity.getParentId())) {
// 递归
entity.setHierarchy(getChild(entity.getUserId(), allList));
}
}
// 递归退出
if (childList.size() == 0) {
return null;
}
return childList;
}
/**
* 获取根节点
*/
public static List<UserTreeNode> getRootNode(List<UserInfo> userInfoList) {
List<UserTreeNode> userTreeNodes = new ArrayList<>();
for (UserInfo userInfo : userInfoList) {
if (userInfo.getPid() == null) {
userTreeNodes.add(new UserTreeNode()
.setUserId(userInfo.getId().toString())
.setParentId(userInfo.getPid() == null ? null : userInfo.getPid().toString()));
}
}
return userTreeNodes;
}
/**
* 获取所有节点
*/
public static List<UserTreeNode> getAllNode(List<UserInfo> userInfoList) {
List<UserTreeNode> allUserList = new LinkedList<>();
userInfoList.forEach(node -> allUserList.add(new UserTreeNode()
.setUserId(node.getId().toString())
.setParentId(node.getPid() == null ? null : node.getPid().toString())));
return allUserList;
}
/**
* 从底向上遍历用户ID树
*
* @param root 树根节点
* @return 从底向上遍历后的用户ID树
*/
public static List<String> levelOrderBottom(UserTreeNode root) {
LinkedList<List<String>> res = new LinkedList<>();
List<String> userTree = new LinkedList<>();
if (root == null) {
return userTree;
}
UserTreeNode treeNode = root;
Queue<UserTreeNode> queue = new LinkedList<>();
queue.offer(treeNode);
while (!queue.isEmpty()) {
int size = queue.size();
List<String> temp = new LinkedList<>();
while (size > 0) {
treeNode = queue.poll();
if (treeNode != null) {
temp.add(treeNode.getUserId());
if (treeNode.getHierarchy() != null) {
for (UserTreeNode userTreeNode : treeNode.getHierarchy()) {
queue.offer(userTreeNode);
}
}
}
size--;
}
res.addFirst(temp);
}
for (List<String> re : res) {
userTree.addAll(re);
}
return userTree;
}
/**
* 遍历节点添加入childTree中
*
* @param userTreeNodeList 循环遍历的childList
* @param childTree 待添加的list
*/
public static void parseTree(List<UserTreeNode> userTreeNodeList, List<String> childTree) {
for (UserTreeNode userTreeNode : userTreeNodeList) {
childTree.add(userTreeNode.getUserId());
if (userTreeNode.getHierarchy() != null) {
parseTree(userTreeNode.getHierarchy(), childTree);
}
}
}
/**
* 遍历所有该节点的子节点并添加入list内
*
* @param userId 最顶节点
* @param userTreeNodeList user树
* @return 子树
*/
public static List<String> loopTree(Long userId, List<UserTreeNode> userTreeNodeList, List<String> childTree) {
for (UserTreeNode userTreeNode : userTreeNodeList) {
if (userTreeNode.getUserId().equals(userId.toString()) && userTreeNode.getHierarchy() != null) {
parseTree(userTreeNode.getHierarchy(), childTree);
} else {
if (userTreeNode.getHierarchy() != null) {
loopTree(userId, userTreeNode.getHierarchy(), childTree);
}
}
}
return childTree;
}
}
实现
有了从底向上遍历后的多叉树list,就可以很方便的进行需求开发。
只要从头遍历这个list,就可以实现从子到父的升级了。
同时为了减少高层节点不必要的递归次数,可以在遍历低层次节点的同时把该节点的业绩和社区等级结果缓存在一个矩阵里(也就是贪心算法或者动态规划)方便高层节点直接取出或进行累加。