简介

工作一年多了,天天CRUD,终于以前学习的算法排上用场了。

背景

我们的系统在用户注册时没有校验身份id(身份证)和电话号码的唯一,可能使用相同的身份id或者电话号码创建多个账号,导致有些人开多个账号领多份钱。现在公司想降本增效,让我统计出各个市场存在相同身份id或者电话号码的账号信息。

输入

两张表已过滤本次统计不涉及的字段
ops_tab
(operator 是站点员工表)

staff_tab
(员工表,里面包含的信息更详细。staff包括operator和driver,因此ops_tab的数据是staff_tab的子集)

ops_tab没有PassportId,因此需要拿staff Id去staff_tab获取对应passport id。

另外各个市场判断账号是否来自于同一个人是有差别的,有些市场允许多个账号使用同一个电话号码。

市场 判断条件
印度尼西亚ID 1、PassportId 2、Phone Number
马来西亚MY 1、PassportId 2、Phone Number
菲律宾PH 1、PassportId 2、Phone Number
新加坡SG 1、PassportId 2、Phone Number
泰国TH 1、Phone Number
台湾TW 1、PassportId
越南VN 1、PassportId 2、Phone Number

比如印度尼西亚就不允许Passport Id和电话号码重复,而泰国允许Passport Id重复而电话号码不能重复。

输出

输出excel,相同信息在同一个组,只统计存在重复信息的账号,不重复的不需要展示。

详细设计

流程图

golang实现

算法主体

func Run(opsList []*OpsTab, staffList []*StaffTab) {cid := strings.ToUpper(os.Getenv("CID"))fmt.Println(cid)if len(cid) == 0 {return}NewExcelListWithOpsAndStaff(opsList, staffList).Group(cid).Export(cid)
}type OpsTab struct {Id             uint64 `gorm:"column:id" json:"id"`OpsId          string `gorm:"column:ops_id" json:"ops_id"`OpsName        string `gorm:"column:ops_name" json:"ops_name"`Phone          string `gorm:"column:phone" json:"phone"`OpsStatus      int    `gorm:"column:ops_status" json:"ops_status"`StaffId        string `gorm:"column:staff_id" json:"staff_id"`
}type StaffTab struct {StaffId         string `gorm:"column:staff_id" json:"staff_id"`PassportId      string `gorm:"column:passport_id" json:"passport_id"`
}// 根据staff_id将Ops_tab和staff_tab合并成一个结构体
type ExcelResultItem struct {OpsName    stringOpsId      stringStaffId    stringPhone      stringPassportId stringOpsStatus  int
}type ExcelList []*ExcelResultItemfunc NewExcelListWithOpsAndStaff(opsList []*OpsTab, staffList []*StaffTab) *ExcelList {return (&ExcelList{}).toExcelResultItemList(opsList, staffList)
}func NewExcelList() *ExcelList {return &ExcelList{}
}func (e *ExcelList) Append(item *ExcelResultItem) {*e = append(*e, item)
}func (e *ExcelList) Size() int {return len(*e)
}func (e *ExcelList) toExcelResultItemList(opsList []*OpsTab, staffList []*StaffTab) *ExcelList {staffId2StaffMap := make(map[string]*StaffTab)for _, item := range staffList {staffId2StaffMap[item.StaffId] = item}for _, item := range opsList {passportId := ""if staff, ok := staffId2StaffMap[item.StaffId]; ok {passportId = staff.PassportId}*e = append(*e, &ExcelResultItem{OpsName:    item.OpsName,OpsId:      item.OpsId,StaffId:    item.StaffId,Phone:      item.Phone,PassportId: passportId,OpsStatus:  item.OpsStatus,})}return e
}func (e *ExcelList) Group(cid string) *GroupedExcelList {excelItemList := *euf := NewPathCompressionUnionFind(len(excelItemList))for i := range excelItemList {for j := i + 1; j < len(excelItemList); j++ {if handler.Check(cid, excelItemList[i], excelItemList[j]) {uf.Union(i, j)}}}ufId2ExcelListMap := make(map[int]*ExcelList)for id, item := range excelItemList {root := uf.Find(id)if uf.GetSize(root) < 2 {continue}if _, ok := ufId2ExcelListMap[root]; !ok {ufId2ExcelListMap[root] = NewExcelList()}ufId2ExcelListMap[root].Append(item)}res := NewGroupedExcelList()for _, list := range ufId2ExcelListMap {res.Append(list)}return res
}type GroupedExcelList []*ExcelListfunc NewGroupedExcelList() *GroupedExcelList {return &GroupedExcelList{}
}func (g *GroupedExcelList) Append(list *ExcelList) {*g = append(*g, list)
}

校验是否重复(策略模式)


类似这个,实际简单一点

var handler = NewCheckWhetherSameStrategyHandler()type CheckWhetherSameStrategyHandler struct {region2StrategyMap map[string]CheckWhetherSameStrategy
}func NewCheckWhetherSameStrategyHandler() *CheckWhetherSameStrategyHandler {return &CheckWhetherSameStrategyHandler{region2StrategyMap: make(map[string]CheckWhetherSameStrategy),}
}func (c *CheckWhetherSameStrategyHandler) Register(cid string, strategy CheckWhetherSameStrategy) {c.region2StrategyMap[cid] = strategy
}func (c *CheckWhetherSameStrategyHandler) Check(cid string, ops1, ops2 *ExcelResultItem) bool {if _, ok := c.region2StrategyMap[cid]; !ok {panic(fmt.Sprintf("cid[%v] not exist", cid))}return c.region2StrategyMap[cid].Check(ops1, ops2)
}type CheckWhetherSameStrategy interface {Check(ops1, ops2 *ExcelResultItem) bool
}type CheckWhetherSameStrategyID struct{}func (c *CheckWhetherSameStrategyID) Check(ops1, ops2 *ExcelResultItem) bool {return len(ops1.PassportId) != 0 && ops1.PassportId == ops2.PassportId || len(ops1.Phone) != 0 && ops1.Phone == ops2.Phone
}type CheckWhetherSameStrategyMY struct{}func (c *CheckWhetherSameStrategyMY) Check(ops1, ops2 *ExcelResultItem) bool {return len(ops1.PassportId) != 0 && ops1.PassportId == ops2.PassportId || len(ops1.Phone) != 0 && ops1.Phone == ops2.Phone
}type CheckWhetherSameStrategyPH struct{}func (c *CheckWhetherSameStrategyPH) Check(ops1, ops2 *ExcelResultItem) bool {return len(ops1.PassportId) != 0 && ops1.PassportId == ops2.PassportId || len(ops1.Phone) != 0 && ops1.Phone == ops2.Phone
}type CheckWhetherSameStrategySG struct{}func (c *CheckWhetherSameStrategySG) Check(ops1, ops2 *ExcelResultItem) bool {return len(ops1.PassportId) != 0 && ops1.PassportId == ops2.PassportId || len(ops1.Phone) != 0 && ops1.Phone == ops2.Phone
}type CheckWhetherSameStrategyTH struct{}func (c *CheckWhetherSameStrategyTH) Check(ops1, ops2 *ExcelResultItem) bool {return len(ops1.Phone) != 0 && ops1.Phone == ops2.Phone
}type CheckWhetherSameStrategyTW struct{}func (c *CheckWhetherSameStrategyTW) Check(ops1, ops2 *ExcelResultItem) bool {return len(ops1.PassportId) != 0 && ops1.PassportId == ops2.PassportId
}type CheckWhetherSameStrategyVN struct{}func (c *CheckWhetherSameStrategyVN) Check(ops1, ops2 *ExcelResultItem) bool {return len(ops1.PassportId) != 0 && ops1.PassportId == ops2.PassportId || len(ops1.Phone) != 0 && ops1.Phone == ops2.Phone
}func init() {handler.Register("ID", &CheckWhetherSameStrategyID{})handler.Register("MY", &CheckWhetherSameStrategyMY{})handler.Register("PH", &CheckWhetherSameStrategyPH{})handler.Register("SG", &CheckWhetherSameStrategySG{})handler.Register("TH", &CheckWhetherSameStrategyTH{})handler.Register("TW", &CheckWhetherSameStrategyTW{})handler.Register("VN", &CheckWhetherSameStrategyVN{})
}

golang Excel导出操作

func (g *GroupedExcelList) Export(cid string) {//创建excel文件xlsx := excelize.NewFile()sheet := fmt.Sprintf("same_field_ops_%v_excel", cid)//创建新表单index := xlsx.NewSheet(sheet)//第一行  各个字段的名称row := 1setFieldValue2Excel(xlsx, sheet, row, FieldNameGroup, FieldNameGroup)setFieldValue2Excel(xlsx, sheet, row, FieldNameOpsName, FieldNameOpsName)setFieldValue2Excel(xlsx, sheet, row, FieldNameOpsId, FieldNameOpsId)setFieldValue2Excel(xlsx, sheet, row, FieldNamePhoneNumber, FieldNamePhoneNumber)setFieldValue2Excel(xlsx, sheet, row, FieldNamePassportId, FieldNamePassportId)setFieldValue2Excel(xlsx, sheet, row, FieldNameStatus, FieldNameStatus)row++for groupId, listPtr := range *g {//Excel 一组list := *listPtrstartRow := rowrow++for _, item := range list {//Excel 一行setFieldValue2Excel(xlsx, sheet, row, FieldNameGroup, fmt.Sprintf("%v", groupId))setFieldValue2Excel(xlsx, sheet, row, FieldNameOpsName, item.OpsName)setFieldValue2Excel(xlsx, sheet, row, FieldNameOpsId, item.OpsId)setFieldValue2Excel(xlsx, sheet, row, FieldNamePhoneNumber, item.Phone)setFieldValue2Excel(xlsx, sheet, row, FieldNamePassportId, item.PassportId)setFieldValue2Excel(xlsx, sheet, row, FieldNameStatus, fmt.Sprintf("%v", item.OpsStatus))row++}//合并A列成一组xlsx.MergeCell(sheet, fmt.Sprintf("A%v", startRow), fmt.Sprintf("A%v", row - 1))}//设置默认打开的表单xlsx.SetActiveSheet(index)//保存文件到指定路径err := xlsx.SaveAs(fmt.Sprintf("./%v.xlsx", sheet))if err != nil {log.Fatal(err)}
}type FieldName = stringconst (FieldNameGroup       FieldName = "Group"FieldNameOpsName     FieldName = "Ops Name"FieldNameOpsId       FieldName = "Ops Id"FieldNamePhoneNumber FieldName = "Phone Number"FieldNamePassportId  FieldName = "Passport Id"FieldNameStatus      FieldName = "Status"
)
//如图,表格上角的ABCDEF
var posMap = map[FieldName]string{FieldNameGroup:       "A",FieldNameOpsName:     "B",FieldNameOpsId:       "C",FieldNamePhoneNumber: "D",FieldNamePassportId:  "E",FieldNameStatus:      "F",
}func setFieldValue2Excel(xlsx *excelize.File, sheet string, row int, key FieldName, val string) {if _, err := strconv.ParseInt(val, 10, 64); len(val) > 1 && err == nil {val = "'" + val}xlsx.SetCellValue(sheet, fmt.Sprintf("%v%v", posMap[key], row), val)
}

路径压缩并查集

type PathCompressionUnionFind struct {parents []intsize    []int
}func NewPathCompressionUnionFind(len int) *PathCompressionUnionFind {parents := make([]int, len)size := make([]int, len)for i := range parents {parents[i] = isize[i] = 1}return &PathCompressionUnionFind{parents: parents,size:    size,}
}func (t *PathCompressionUnionFind) Union(p, q int) {pRoot := t.Find(p)qRoot := t.Find(q)if pRoot == qRoot {return}t.parents[qRoot] = t.parents[pRoot]t.size[pRoot] += t.size[qRoot]
}func (t *PathCompressionUnionFind) Find(p int) int {for t.parents[p] != p {t.parents[p] = t.parents[t.parents[p]]p = t.parents[p]}return p
}func (t *PathCompressionUnionFind) GetSize(p int) int {if p < 0 || p >= len(t.parents) {panic("Union Find Array out of bounds")}return t.size[t.Find(p)]
}

复杂度

时间复杂度
O(N^2)

空间复杂度
O(N)

其他

从k8s pod上连接数据库并下载表数据

我们线上数据数据量约200万,从公司提供的平台查会被限制单词1000条数据,因此下载ops_tab和staff_tab的数据需要写个脚本在k8s上执行。

本地(MAC)
安装交叉编译环境

brew install FiloSottile/musl-cross/musl-cross

切换到main函数所在包,执行编译

CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CGO_LDFLAGS="-static" go build

压缩并拆分为多个文件方便上传

tar cjf - $your_name$ |split -b 5m - $your_name$.tar.bz2.

在k8s pod上
安装上传插件

apt install -y lrzsz

安装解压插件

apt install -y bzip2

上传文件

rz

给文件赋执行权限

chmod +x $your_name$

运行

./$your_name$

并查集解决重复员工问题相关推荐

  1. 数据结构之并查集:并查集解决案例, Python——21

    并查集解决案例畅通工程 案例问题介绍: 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都可以实现交通 ...

  2. 堆的应用--并查集解决“擒贼先擒王”问题(JAVA)

    现在有10个强盗. 1号强盗与2号强盗是同伙. 3号强盗与4号强盗是同伙. 5号强盗与2号强盗是同伙. 4号强盗与6号强盗是同伙. 2号强盗与6号强盗是同伙. 8号强盗与7号强盗是同伙. 9号强盗与7 ...

  3. 算到怀疑人生!如何用并查集解决朋友圈个数问题?

    作者 |  channingbreeze 责编 | 郭芮 小史是一个应届生,虽然学的是电子专业,但是自己业余时间看了很多互联网与编程方面的书,一心想进BAT互联网公司. 今天小史去了一家社交小巨头公司 ...

  4. 并查集解决朋友圈问题

    引入 最近在网上看到了这样一道面试题: 假如已知有n个人和m对好友关系(存于数组r)如果两个人是直接或间接的好友(好友的好友的好友-),则认为他们属于同一个朋友圈,请写程序求出这n个人里一共有多少个朋 ...

  5. 关于 并查集(union find) 算法基本原理 以及 其 在分布式图场景的应用

    二月的最后一篇水文-想写一些有意思的东西. 文章目录 环检测在图数据结构中的应用 深度/广度优先 检测环 并查集数据结构 (Union-Find) 基本概念 初始化 合并 union 查找祖先 优化1 ...

  6. 【高级数据结构】并查集

    目录 A.AcWing 1250. 格子游戏 B.AcWing 1252. 搭配购买 C.AcWing 237. 程序自动分析 D.AcWing 239. 奇偶游戏 E.AcWing 238. 银河英 ...

  7. SDUTOJ [2801] 并查集模板

     英语四六级系列(一) Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 众所周知,英语四六级考试有几套不同的试卷,而且,为了防 ...

  8. 并查集路径压缩_并查集(UnionFind)技巧总结

    什么是并查集 在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题.有一个联合-查找算法(Union-find Algorithm)定义了两个用 ...

  9. How Many Answers Are Wrong HDU - 3038(带权并查集)

    TT and FF are - friends. Uh- very very good friends -________-b FF is a bad boy, he is always wooing ...

最新文章

  1. Android自动化测试框架
  2. 服务器邮箱备份文件在哪里,如何轻松将数据文件备份到电子邮箱?
  3. 声音匹配_如何调节人声音色方法如下
  4. RabbitMQ消息应答------ack机制
  5. Simulink仿真 离散系统仿真
  6. 利用redis保存验证码并设置过期时间
  7. Halcon 基本算子释义
  8. DBI接口和DPI接口的区别
  9. mysql 锁表 for update,MySQL中select * for update锁表的问题(转)
  10. mysql generator 命令_Mybatis使用命令生成逆向工程的方法
  11. Java程序性能优化- 让你的Java程序更快、更稳定pdf
  12. Vista home版连Win7旗舰版的远程桌面,提示“您的凭据不工作”的解决方法
  13. 点金软件测试自学,徐文明短线点金相关公式
  14. Android 一篇文章轻松搞懂什么是Callback回调
  15. 内部收益率计算公式用计算机,内部收益率计算器
  16. OneNote for win10 无法加载笔记本
  17. 企业网站开发需要注意什么事项?
  18. Docker三剑客详解
  19. linux自动备份系统快照,我的 Linux 时光机——Snapper系统级自动备份工具
  20. 2021年度上海公务员考试公告(11月05日开启)

热门文章

  1. WebView 拦截广告 简单实现
  2. 网易滑块识别-通用滑块识别
  3. java createfile,Java Filer.createSourceFile方法代碼示例
  4. Java 12位uuid_java如何生成12位永远不重复的数字
  5. 与世界对话丨预康可瘦品牌发布暨全国招商会隆重举行
  6. 树莓派3b+,4b新手入门到手开箱第一次使用之十大步骤
  7. 01 什么是数据结构
  8. fs/binfmt_aout.c:270: error: 'SEGMENT_SIZE' undeclared
  9. 系统分析师-资料总结-中
  10. 键盘布局及各按键功能介绍(超超超详细的哟~)