golang实现skiplist 跳表

跳表

go 复制代码
package main

import (
	"errors"
	"math"
	"math/rand"
)

func main() {

	// 双向链表
	//
	/**
	先理解查找过程
	Level 3: 1		 6
	Level 2: 1   3   6
	Level 1: 1 2 3 4 6

	比如 查找2 ; 从高层往下找;
	如果查找的值比当前值小 说明没有可查找的值
	2比1大 往当前层的下个节点查找,3层的后面没有了或者比后面的6小 ,往下层找
	2层 查找值比下个节点3还小 往下层找
	最后一层找到

	比如查找 4
	没有找到 3层往下到2层; 2层里 4比3大继续往前,比6小,往下层找
	从第一层的继续往前找

	比如查找 5
	第一层的3开始往前找到6比查找值5大,说明没有待查找值
	*/

	/**
	插入流程
		找到插入的位置
		确定他当前的层数
		在他的层数连接当前节点

	    如何确定层数?
			来一个概率的算法就行
			这样在数量大的时候能基本能达到2分查找的效果(概率是1/2)

	    更新索引数组?
		我们在查找的时候的路径就可以拿来做插入的数据
	    比如查找4
		找的路径是 3层的 1,2层的3 ;
		如果4是第三层的
		更新3层 1->4>6
		更新2层 1->3->4->6
	*/

	/**
	删除流程 基本同上

	*/

	/**

	 */

}

// MAX_LEVEL 最高层数
const MAX_LEVEL = 16

type T comparable

type skipListHandle[T comparable] interface {
	insert(data T, score int32) (err error)
	delete(data T, score int32) int
	findNode(data T, score int32) (error, *skipListNode[T])
}

type skipListNode[T comparable] struct {
	data T
	// 排序分数
	score int32
	//层高
	level int
	// 下个节点 同时也是索引
	forwards []*skipListNode[T]
}

type skipList[T comparable] struct {
	head, tail *skipListNode[T]
	// 跳表高度
	level int
	// 跳表长度
	length int32
}

func createSkipList[T comparable](data T) *skipList[T] {
	return &skipList[T]{
		level:  1,
		length: 0,
		head:   createNode[T](data, math.MinInt32, MAX_LEVEL),
	}
}

func createNode[T comparable](data T, score int32, level int) *skipListNode[T] {
	return &skipListNode[T]{
		data:     data,
		score:    score,
		forwards: make([]*skipListNode[T], MAX_LEVEL, MAX_LEVEL),
		level:    level,
	}
}
func (list *skipList[T]) Insert(data T, score int32) error {
	currenNode := list.head
	// 找到插入的位置
	// 记录插入的路径 记录第一个比待查找的值小的位置
	path := [MAX_LEVEL]*skipListNode[T]{}
	for i := MAX_LEVEL - 1; i >= 0; i-- {
		for currenNode.forwards[i] != nil {
			// 如果插入的位置比当前数据小 直接跳出循环并且高度下降
			if currenNode.forwards[i].score > score {
				path[i] = currenNode
				break
			}
			// 插入位置比当前的大,在当前层继续往前找
			currenNode = currenNode.forwards[i]
		}
		// 如果currenNode.forwards[i] == nil 说明是最后一个值了 所以直接插入
		if currenNode.forwards[i] == nil {
			path[i] = currenNode
		}
	}

	// 随机算法求得最大层数
	level := 1
	for i := 1; i < MAX_LEVEL; i++ {
		if rand.Int31()%7 == 1 {
			level++
		}
	}

	newNode := createNode(data, score, level)

	// 原有节点连接
	for i := 0; i <= level-1; i++ {
		next := path[i].forwards[i]
		// path[i]拿到第一个插入值小的位置 forwards[i] 是指在当前层它指向的下个节点
		newNode.forwards[i] = next
		path[i].forwards[i] = newNode
	}

	// 更新level
	if level > list.level {
		list.level = level
	}

	list.length++

	return errors.New("插入失败")
}

func (list *skipList[T]) Delete(data T, score int32) int {
	currenNode := list.head
	// 找到插入的位置
	// 记录插入的路径 记录第一个比待查找的值小的位置
	path := [MAX_LEVEL]*skipListNode[T]{}
	for i := list.level - 1; i >= 0; i-- {
		path[i] = list.head
		for currenNode.forwards[i] != nil {
			// 記錄刪除的位置
			if currenNode.forwards[i].score == score && currenNode.forwards[i].data == data {
				path[i] = currenNode
				break
			}
			// 插入位置比当前的大,在当前层继续往前找
			currenNode = currenNode.forwards[i]
		}
	}

	currenNode = path[0].forwards[0]
	for i := currenNode.level - 1; i >= 0; i-- {
		if path[i] == list.head && currenNode.forwards[i] == nil {
			list.level = i
		}

		if nil == path[i].forwards[i] {
			path[i].forwards[i] = nil
		} else {
			path[i].forwards[i] = path[i].forwards[i].forwards[i]
		}
	}

	list.length--

	return 0
}

func (list skipList[T]) FindNode(v T, score int32) (err error, node *skipListNode[T]) {

	cur := list.head
	for i := list.level - 1; i >= 0; i-- {
		for nil != cur.forwards[i] {
			if cur.forwards[i].score == score && cur.forwards[i].data == v {
				return nil, cur.forwards[i]
			} else if cur.forwards[i].score > score {
				break
			}
			cur = cur.forwards[i]
		}
	}
	return errors.New("请传入查找的值"), nil
}

测试

go 复制代码
package main

import (
	"testing"
)

func Test_createNode(t *testing.T) {
	sl := createSkipList[int](0)

	sl.Insert(1, 95)
	t.Log(sl.head.forwards[0])
	t.Log(sl.head.forwards[0].forwards[0])
	t.Log(sl)
	t.Log("-----------------------------")

	sl.Insert(2, 88)
	t.Log(sl.head.forwards[0])
	t.Log(sl.head.forwards[0].forwards[0])
	t.Log(sl.head.forwards[0].forwards[0].forwards[0])
	t.Log(sl)
	t.Log("-----------------------------")

	sl.Insert(3, 100)
	t.Log(sl.head.forwards[0])
	t.Log(sl.head.forwards[0].forwards[0])
	t.Log(sl.head.forwards[0].forwards[0].forwards[0])
	t.Log(sl.head.forwards[0].forwards[0].forwards[0].forwards[0])
	t.Log(sl)
	t.Log("-----------------------------")

	t.Log(sl.FindNode(2, 88))
	t.Log("-----------------------------")

	sl.Delete(1, 95)
	t.Log(sl.head.forwards[0])
	t.Log(sl.head.forwards[0].forwards[0])
	t.Log(sl.head.forwards[0].forwards[0].forwards[0])
	t.Log(sl)
	t.Log("-----------------------------")
}
相关推荐
程序员_三木4 分钟前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
是小崔啊14 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
tianmu_sama20 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
黄公子学安全23 分钟前
Java的基础概念(一)
java·开发语言·python
liwulin050624 分钟前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc29 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Oneforlove_twoforjob33 分钟前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
羚羊角uou35 分钟前
【C++】优先级队列以及仿函数
开发语言·c++
FeboReigns41 分钟前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns44 分钟前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++