跳表查找和数组二分查找 对比
Posted Math World
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跳表查找和数组二分查找 对比相关的知识,希望对你有一定的参考价值。
动机:
对比效率的想法源于公司的一次压测, 经过一轮排除下来 发现是由于排行榜引起 占了大部分CPU
对比代码
平台windows 10 x64
IDE IntelliJ IDEA Community Edition 2020.3
Java jdk 1.8.0_201
Kotlin 1.3.21
首先贴一份kotlin实现的链表
interface SkipList<E : RankElement> {
val SKIPLIST_MAXLEVEL: Int
get() = 32
val SKIPLIST_P: Double
get() = 0.25
fun insert(element: E)
fun delete(element: E)
fun getRank(element: E): Long
fun between(from: E, to: E): List<E>
fun range(element: E, leftCount: Int, rightCount: Int): List<E>
fun getElementByRank(rank: Long): E?
fun createNode(level: Int, element: E?): SkipListNode<E> = SkipListNode(Array(level) { SkipListLevel<E>() }, element)
fun firstRange(size: Int): List<E>
/**根据排位来查找*/
fun findWithRange(from: Long, to: Long, filter: (element: E) -> Boolean = { true }): List<E>
fun toList(): ArrayList<E>;
}
interface RankElement : Comparable<RankElement> {
fun ownerId(): Long
}
class SkipListImpl<E : RankElement> : SkipList<E> {
var level: Int = 1
private set
var length: Int = 0
private set
var header: SkipListNode<E>? = null
private set
var tail: SkipListNode<E>? = null
private set
init {
this.header = createNode(SKIPLIST_MAXLEVEL, null)
}
//随机一个level
fun randomLevel(): Int {
var level = 1
while (random() and 0xFFFF < SKIPLIST_P * 0xFFFF) level += 1
return if (level < SKIPLIST_MAXLEVEL) level else SKIPLIST_MAXLEVEL
}
private fun random(): Int {
return ThreadLocalRandom.current().nextInt()
}
override fun insert(element: E) {
val update = arrayOfNulls<SkipListNode<E>>(SKIPLIST_MAXLEVEL)
val rank = IntArray(SKIPLIST_MAXLEVEL)
var x = this.header
for (i in this.level - 1 downTo 0) {
rank[i] = if (i == this.level - 1) 0 else rank[i + 1]
var tempNode = x?.let { it.level[i].forward }
while (tempNode != null) {
val cmp = cmp(tempNode.element, element)
if (cmp == 0) {//同一个元素
throw IllegalStateException("insert same element ${tempNode.element} $element")
}
if (cmp < 0) {
rank[i] += requireNotNull(x?.let { it.level[i].span })
x = tempNode
tempNode = x.let { it.level[i].forward }
}else{
break
}
}
update[i] = x
}
val level = randomLevel()
if (level > this.level) {
for (i in this.level until level) {
rank[i] = 0;
update[i] = this.header
update[i]?.let { it.level[i].span = this.length }
}
this.level = level
}
val newNode = createNode(level, element)
for (i in 0 until level) {
update[i]?.let {
newNode.level[i].forward = it.level[i].forward
it.level[i].forward = newNode
newNode.level[i].span = it.level[i].span - (rank[0] - rank[i])
it.level[i].span = (rank[0] - rank[i]) + 1
}
}
for (i in level until this.level) {
update[i]?.let { it.level[i].span++ }
}
newNode.backward = if (update[0] == this.header) null else update[0]
if (newNode.level[0].forward != null) {
newNode.level[0].forward?.backward = newNode
} else {
this.tail = newNode
}
this.length++
}
private fun cmp(element1: E?, element2: E): Int {
if (element1 == null) {
throw IllegalAccessException("元素不该为空")
}
return element1.compareTo(element2)
}
override fun delete(element: E) {
val update = arrayOfNulls<SkipListNode<E>>(SKIPLIST_MAXLEVEL)
var x: SkipListNode<E>? = this.header
for (i in level - 1 downTo 0) {
var forward = x?.let { it.level[i].forward }
while (forward != null && cmp(forward.element, element) < 0) {
x = forward
forward = x.level[i].forward
}
update[i] = x
}
x = x?.let { it.level[0].forward }
//找到哦了
x?.let {
if (cmp(x.element, element) == 0) {
for (i in 0 until this.level) {
update[i]?.let {
if (it.level[i].forward == x) {
it.level[i].span += x.level[i].span - 1
it.level[i].forward = x.level[i].forward
} else {
it.level[i].span -= 1
}
}
}
if (x.level[0].forward != null) {
x.level[0].forward?.backward = x.backward
} else {
this.tail = x.backward
}
while (this.level > 1 && this.header?.let { it.level[this.level - 1].forward } == null) {
this.level--
}
this.length--
}
}
}
override fun getRank(element: E): Long {
var x = this.header
var rank: Long = 0
for (i in (this.level - 1) downTo 0) {
var forward = x?.let { it.level[i].forward }
while (forward != null && cmp(forward.element, element) <= 0) {
rank += (x?.let { it.level[i].span } ?: 0)
x = forward
forward = x.level[i].forward
}
}
if (x?.element?.let { cmp(it, element) == 0 } == true) {
return rank
}
return 0
}
override fun between(from: E, to: E): List<E> {
var x = this.header
var rank: Long = 0
for (i in this.level - 1 downTo 0) {
var forward = x?.let { it.level[i].forward }
while (forward != null && cmp(forward.element, from) <= 0) {
rank += (x?.let { it.level[i].span } ?: 0)
x = forward
forward = x.level[i].forward
}
}
val elements = arrayListOf<E>()
x?.let {
var temp = it.level[0].forward
while (temp != null) {
val cmp = cmp(temp.element, to)
if (cmp >= 0) {
return@let
}
elements.add(temp.element!!)
temp = temp.level[0].forward
}
}
return elements
}
override fun range(element: E, leftCount: Int, rightCount: Int): List<E> {
var x = this.header
var rank: Long = 0
for (i in this.level - 1 downTo 0) {
var forward = x?.let { it.level[i].forward }
while (forward != null && cmp(forward.element, element) <= 0) {
rank += (x?.let { it.level[i].span } ?: 0)
x = forward
forward = x.level[i].forward
}
}
//找到自己的位置,朝前朝后找
val list = LinkedList<E>()
x?.let { target ->
var temp = target.backward
repeat(leftCount) {
temp?.let {
list.addFirst(it.element)
temp = it.backward
} ?: return@repeat
}
temp = target.level[0].forward
repeat(rightCount) {
temp?.let {
list.addLast(it.element)
temp = it.level[0].forward
} ?: return@repeat
}
}
return list
}
override fun getElementByRank(rank: Long): E? {
if (this.header == null || this.length < rank) {
throw IllegalAccessException("排名不存在 $rank")
}
var x: SkipListNode<E>? = this.header
var traversed: Long = 0
for (i in this.level - 1 downTo 0) {
while (x?.let { it.level[i].forward != null && traversed + it.level[i].span <= rank } == true) {
traversed += x.level[i].span
x = x.level[i].forward
}
if (traversed == rank) {
return x?.element
}
}
return null
}
override fun firstRange(size: Int): List<E> {
if (this.length == 0) {
throw java.lang.IllegalStateException("元素为空")
}
val element = header?.level?.get(0)?.forward?.element
return element?.let { range(it, 0, size) }!!
}
override fun findWithRange(from: Long, to: Long, filter: (element: E) -> Boolean): List<E> {
if (this.header == null || this.length < from) {
throw IllegalAccessException("排名不存在 $from")
}
val list = arrayListOf<E>()
var x: SkipListNode<E>? = this.header
var traversed: Long = 0
for (i in this.level - 1 downTo 0) {
while (x?.let { it.level[i].forward != null && traversed + it.level[i].span <= from } == true) {
traversed += x.level[i].span
x = x.level[i].forward
}
if (traversed == from.toLong()) {
repeat((to - from + 1).toInt()) {
x?.let {
list.add(requireNotNull(it.element))
x = it.level[0].forward
}
}
}
}
return list
}
override fun toList(): ArrayList<E> {
val arrayList = arrayListOf<E>()
var x: SkipListNode<E>? = this.header
repeat(this.length) {
x?.let {
arrayList.add(requireNotNull(it.level[0].forward?.element))
x = it.level[0].forward
}
}
return arrayList
}
}
class SkipListLevel<E> {
//同level下一个元素
var forward: SkipListNode<E>? = null
//同level跨越节点的数量
var span = 0
}
class SkipListNode<E>(
//不同level指向的下一个元素和span
var level: Array<SkipListLevel<E>>,
var element: E? = null
) {
//元素 或集成 compareble
//上一个元素
var backward: SkipListNode<E>? = null
}
class Rank<E : RankElement>(
val skipList: SkipList<E> = SkipListImpl(),
val elementMap: HashMap<Long, E> = hashMapOf()
) {
fun insert(element: E) {
val remove = elementMap.remove(element.ownerId())
remove?.let { skipList.delete(it) }
elementMap[element.ownerId()] = element
skipList.insert(element)
}
fun remove(element: E) {
elementMap.remove(element.ownerId())?.let {
skipList.delete(it)
}
}
fun toList(): ArrayList<E> {
return skipList.toList()
}
}
对比测试的代码一
fun testSkipList(){
//初始化跳表排行榜
val skipList: SkipList<testElement> = SkipListImpl()
val elementMap: HashMap<Long, testElement> = hashMapOf()
val rank = Rank(skipList, elementMap)
//准备跳表数据
repeat(10000){
rank.insert(testElement(it.toLong()))
}
//准备二维数组数据
val arrayList = arrayListOf<Int>()
repeat(10000){
arrayList.add(it)
}
//测试跳表耗时
val t1 = System.nanoTime()
repeat(1000) {
//测试找单个
rank.skipList.getElementByRank((300+it).toLong())
//测试找范围
//rank.skipList.range(testElement(5000),10, 10)
}
val t2 = System.nanoTime()
//测试二维数组耗时
val t3 = System.nanoTime()
repeat(1000){
//测试找单个
arrayList.binarySearch(300+it)
//测试找范围
// repeat(20){
// arrayList.binarySearch(4990+it)
// }
}
val t4 = System.nanoTime()
println("skiplistSearch time ${(t2-t1)/1000000.0} ms")
println("binarySearch time ${(t4-t3)/1000000.0} ms")
}
结果:
skiplistSearch time 1.2985 ms
binarySearch time 15.4235 ms
明显 跳表查找比二分查找还是有很大优势
对比测试的代码二
fun testSkipList(){
//初始化跳表排行榜
val skipList: SkipList<testElement> = SkipListImpl()
val elementMap: HashMap<Long, testElement> = hashMapOf()
val rank = Rank(skipList, elementMap)
//准备跳表数据
repeat(10000){
rank.insert(testElement(it.toLong()))
}
//准备二维数组数据
val arrayList = arrayListOf<Int>()
repeat(10000){
arrayList.add(it)
}
//测试跳表耗时
val t1 = System.nanoTime()
repeat(1000) {
//测试找单个
//rank.skipList.getElementByRank((300+it).toLong())
//测试找范围
rank.skipList.range(testElement(300),10, 10)
}
val t2 = System.nanoTime()
//测试二维数组耗时
val t3 = System.nanoTime()
repeat(1000){
//测试找单个
//arrayList.binarySearch(300+it)
//测试找范围
repeat(20){
arrayList.binarySearch(300+it)
}
}
val t4 = System.nanoTime()
println("skiplistSearch time ${(t2-t1)/1000000.0} ms")
println("binarySearch time ${(t4-t3)/1000000.0} ms")
}
结果:
skiplistSearch time 3.2909 ms
binarySearch time 16.2339 ms
跳表依然比 二叉查找快
截图得时间 都是多次运行之后 取得平均出现次数比较高的
结论: 不管在单次查询 或者连续多次查询中 跳表查询 都比 二维数组的二分查询 有优势
写到这里肯定有有细心的读者 注意到为什么之前
arrayList.binarySearch(300+it)
和
repeat(20){
arrayList.binarySearch(300+it)
}
几乎没有时间差距 一个是 15.4235 ms 一个是16.2339 ms
这是因为CPU发生了 缓存命中
什么是缓存命中 下一篇文章讲
以上是关于跳表查找和数组二分查找 对比的主要内容,如果未能解决你的问题,请参考以下文章