一日一技:在 Golang 中如何快速判断字符串是否在一个数组中
name_list = ['pm', 'kingname', '青南']
if 'kingname' in name_list:
print('kingname 在列表里面')
但是,Golang 是没有in
这个关键词的,所以如果要判断一个字符串数组中是否包含一个特定的字符串,就需要一个一个对比:
package main
import "fmt"
func in(target string, str_array []string) bool {
for _, element := range str_array{
if target == element{
return true
}
}
return false
}
func main(){
name_list := []string{"pm", "kingname", "青南"}
target1 := "kingname"
target2 := "产品经理"
result := in(target1, name_list)
fmt.Println("kingname 是否在 name_list 中:", result)
result = in(target2, name_list)
fmt.Println("产品经理是否在 name_list 中:", result)
}
运行效果如下图所示:
但这种方式有一个弊端,就是要遍历整个字符串数组。如果数组里面有100万条数据,那么平均要遍历50万次才能找到。这是一个非常费时间的操作。
有没有什么办法可以优化这个操作呢?
如果是有序的整型数组,那么我们可以使用二分查找,把时间复杂度O(n)
降到对数时间复杂度。字符串能不能也这样操作呢?实际上是可以的。
在 Golang 中,有一个排序模块sort
,它里面有一个sort.Strings()
函数,可以对字符串数组进行排序。同时,还有一个sort.SearchStrings()[1]函数,会用二分法在一个有序字符串数组中寻找特定字符串的索引。
结合两个函数,我们可以实现一个更高效的算法:
package main
import (
"fmt"
"sort"
)
func in(target string, str_array []string) bool {
sort.Strings(str_array)
index := sort.SearchStrings(str_array, target)
if index < len(str_array) && str_array[index] == target {
return true
}
return false
}
func main(){
name_list := []string{"pm", "kingname", "青南"}
target1 := "kingname"
target2 := "产品经理"
result := in(target1, name_list)
fmt.Println("kingname 是否在 name_list 中:", result)
result = in(target2, name_list)
fmt.Println("产品经理是否在 name_list 中:", result)
}
运行效果如下图所示:
其中,sort.Strings
是一个 in-place 的修改方式,是直接修改的 str_array
。修改以后str_array
变成有序的字符串数组。接下来通过二分查找快速定位。如果找到了,那么返回目标字符串在排序后的列表中第一次出现的索引。如果没有找到,那么返回数组中最后一个元素的索引。所以只要 index 小于最后一个元素的索引,那么目标字符串肯定存在;如果等于最后一个元素的索引,但是值不等于最后一个元素,那么目标字符串就不存在于字符串数组中。
通过先排序再查询的方式,对于100万个元素的字符串数组,只需要查询20次左右就能确认字符串是否存在。速度大大提升。
最后考大家一个思考题。name_list
一开始是乱序的字符串数组,在上图第23行,如果打印一下 name_list
,打印出来的是经过排序的,还是没有经过排序的字符串数字?
参考资料
[1]
sort.SearchStrings()
: https://golang.org/pkg/sort/#SearchStrings
- Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法
- 关于curl网站运维与开发的那些事
- 并查集Union-find及其在最小生成树中的应用
- go 语言的库文件放在哪里?如何通过nginx代理后还能正确获取远程地址
- 离线Tarjan算法-最近公共祖先问题
- Java文件上传下载实训
- 【网络编程系列】二:socket通信原理及实践
- textrank算法原理与提取关键词、自动提取摘要PYTHON
- 【网络编程系列】一:字节顺序的大端与小端表示法
- Linux下的make命令用法
- 增量数据丢失的原因分析(三)(r8笔记第91天)
- JS之浏览器对象BOM
- 超清晰的makefile解释、编写与示例
- 一个简单的sql审核案例 (r8笔记第90天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android实现dialog的3D翻转示例
- Android ImageView 固定宽高比例的实现方法
- Android 实现IOS选择拍照相册底部弹出的实例
- ubuntu 20.04上搭建LNMP环境的方法步骤
- Android实现界面内嵌多种卡片视图(ViewPager、RadioGroup)
- Android设计模式之Builder模式详解
- 详解Android获取系统内核版本的方法与实现代码
- Android 修改viewpage滑动速度的实现代码
- 在Ubuntu20.04中安装ROS Noetic的方法
- Android设计模式之单例模式详解
- Android获取手机联系人的方法
- 学习使用Material Design控件(一)
- Android仿微信Viewpager-Fragment惰性加载(lazy-loading)
- Android嵌套滑动冲突的解决方法
- Android应用图标上的小红点Badge实践代码