AK F.*ing leetcode 流浪计划之跳表

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

本期话题:go实现有序的map

最近在做题时遇到需要用有序map来处理的题,但是golang中没有这个功能。
实现方法有很多,比如可以模仿c++用平衡树来实现,在redis中也可以使用跳表实现。
感觉跳表代码更简洁,之前也没有了解过,就用这个来实现一下。

跳表实现参考 :https://blog.csdn.net/weixin_45827856/article/details/103254965
具体原理已经写得很清楚,我就没必要再写。

跳表功能

有序map的功能他都有

  1. 自动排序
  2. 快速取最大、最小值
  3. 快速按下标取数
  4. 二分查找

代码模板

import "math/rand"

type KV interface {
	GetKey() interface{}
	GetValue() interface{}
	Less(b KV) bool
}

const SKIPLIST_MAXLEVEL = 32

/*
以1/4的概率递增,最多32层
*/
func slRandomLevel() int {
	level := 1
	for (rand.Int() & 0xFFFF) < (0xFFFF >> 2) {
		level++
	}

	if level < SKIPLIST_MAXLEVEL {
		return level
	}
	return SKIPLIST_MAXLEVEL
}

// 利用跳表实现可排序的map
/*跳表节点定义*/
type leveInfo struct {
	// 前进指针
	forward *skiplistNode
	// 跨度
	span int64
}

type skiplistNode struct {
	// value
	elem KV
	// 后退指针
	backward *skiplistNode
	// 层
	level []leveInfo
}

type Skiplist struct {
	// 跳跃表头指针
	header *skiplistNode
	tail   *skiplistNode
	// 表中节点的数量
	length int64
	// 表中层数最大的节点的层数
	level int
}

func slCreateNode(level int, e KV) *skiplistNode {
	node := &skiplistNode{
		elem:     e,
		backward: nil,
		level:    make([]leveInfo, level),
	}
	return node
}

func NewSkipList() *Skiplist {
	list := &Skiplist{
		length: 0,
		level:  1,
		header: slCreateNode(SKIPLIST_MAXLEVEL, nil),
		tail:   nil,
	}

	return list
}

/*
添加元素
*/
func (list *Skiplist) Add(e KV) {
	var (
		// 存储搜索路径
		update [SKIPLIST_MAXLEVEL]*skiplistNode
		x      *skiplistNode
		// 存储经过的节点跨度
		rank [SKIPLIST_MAXLEVEL]int64
	)

	var i int

	// 1. 逐步降级寻找目标节点,得到 "搜索路径"
	for x, i = list.header, list.level-1; i >= 0; i-- {
		// 前缀和rank[i]代表 update[i]之前经过的结点个数
		if i == list.level-1 {
			rank[i] = 0
		} else {
			rank[i] = rank[i+1]
		}

		// 比较elem与e 找到当前层比e小的最后一个结点
		for ; x.level[i].forward != nil && x.level[i].forward.elem.Less(e); x = x.level[i].forward {
			rank[i] += x.level[i].span
		}

		update[i] = x
	}

	// 2. 生成新节点高度,超过了当前最大高度需要对高出部分初始化
	level := slRandomLevel()
	for i = list.level; i < level; i++ {
		rank[i] = 0
		update[i] = list.header
		update[i].level[i].span = list.length
	}
	if level > list.level {
		list.level = level
	}

	// 3. 创建新节点
	x = slCreateNode(level, e)

	// 4. 重排向前指针
	for i = 0; i < level; i++ {
		x.level[i].forward = update[i].level[i].forward
		update[i].level[i].forward = x

		// 更新跨度
		x.level[i].span = update[i].level[i].span - (rank[0] - rank[i])
		update[i].level[i].span = rank[0] - rank[i] + 1
	}

	for i = level; i < list.level; i++ {
		update[i].level[i].span++
	}

	// 向后结点更新
	if update[0] != list.header {
		x.backward = update[0]
	}

	if x.level[0].forward != nil {
		x.level[0].forward.backward = x
	} else {
		list.tail = x
	}
	list.length++
}

// 删除一个结点
func (list *Skiplist) slDeleteNode(x *skiplistNode, update [SKIPLIST_MAXLEVEL]*skiplistNode) {
	for i := 0; i < list.level; i++ {
		// 被删除节点在第i层有节点,则update[i]为被删除节点的前一个节点
		if update[i].level[i].forward == x {
			// 步长 = 原步长 + 被删除节点步长 - 1(被删除节点)
			update[i].level[i].span += x.level[i].span - 1
			// 指针越过被删除节点
			update[i].level[i].forward = x.level[i].forward
		} else {
			// 被删除节点在第i层无节点,则 步长 = 原步长 - 1(被删除节点)
			update[i].level[i].span -= 1
		}
	}
	if x.level[0].forward != nil {
		// 更新被删除节点下一节点的后退指针
		x.level[0].forward.backward = x.backward
	} else {
		list.tail = x.backward
	}
	for list.level > 1 && list.header.level[list.level-1].forward == nil {
		list.level--
	}
	list.length--
}

// 删除结点
func (list *Skiplist) Deletee(e KV) bool {
	var (
		// 存储搜索路径
		update [SKIPLIST_MAXLEVEL]*skiplistNode
		x      *skiplistNode
	)

	x = list.header;
	for i := list.level-1; i >= 0; i-- {
		for ; x.level[i].forward != nil && x.level[i].forward.elem.Less(e); x = x.level[i].forward {}
		update[i] = x;
	}

	x = x.level[0].forward;
	if x!=nil && !x.elem.Less(e) && !e.Less(x.elem) {
		list.slDeleteNode(x, update);
		return true;
	}
	return false; /* not found */
}

// 遍历结点
func (list *Skiplist) Range(f func(e KV)) {
	for x := list.header.level[0].forward; x != nil; x = x.level[0].forward {
		f(x.elem)
	}
}

具体用时需要自己实现KV


type A struct {
	k, v int
}

func (a A) GetKey() interface{} {
	return a.k
}

func (a A) GetValue() interface{} {
	return a.v
}

func (a A) Less(b KV) bool {
	return a.k<b.GetKey().(int)
}


func TestSkipList(t *testing.T) {

	sl := NewSkipList()
	sl.Add(A{1,2})
	sl.Add(A{5,2})
	sl.Add(A{4,2})
	sl.Add(A{3,2})
	sl.Add(A{1,1})
	sl.Add(A{1,2})
	sl.Add(A{1,1})
	sl.Add(A{1,3})

	sum :=0
	sl.Range(func(e KV) {
		fmt.Printf("%+v ",e)
		sum += e.GetKey().(int)
	})
	fmt.Println("\n",sum)

	fmt.Println(sl.Deletee(A{1,2}))
	fmt.Println(sl.Deletee(A{1,2}))
	fmt.Println(sl.Deletee(A{1,2}))
	fmt.Println(sl.Deletee(A{5,2}))
	sl.Add(A{5,9})

	sl.Range(func(e KV) {
		fmt.Printf("%+v ",e)
	})
	fmt.Println()
}

上面由于有类型转换,比较耗时,可以根据需要直接使用struct 代替interface。
实际做题时由类型转换超时了,使用struct直接访问。

题目大意

链接:https://leetcode-cn.com/problems/minimize-deviation-in-array/

给你一个由 n 个正整数组成的数组 nums 。

你可以对数组的任意元素执行任意次数的两类操作:

如果元素是 偶数 ,除以 2
例如,如果数组是 [1,2,3,4] ,那么你可以对最后一个元素执行此操作,使其变成 [1,2,3,2]
如果元素是 奇数 ,乘上 2
例如,如果数组是 [1,2,3,4] ,那么你可以对第一个元素执行此操作,使其变成 [2,2,3,4]
数组的 偏移量 是数组中任意两个元素之间的 最大差值 。

返回数组在执行某些操作之后可以拥有的 最小偏移量 。

示例 1:

输入:nums = [1,2,3,4]
输出:1
解释:你可以将数组转换为 [1,2,3,2],然后转换成 [2,2,3,2],偏移量是 3 - 2 = 1
示例 2:

输入:nums = [4,1,5,20,3]
输出:3
解释:两次操作后,你可以将数组转换为 [4,2,5,5,3],偏移量是 5 - 2 = 3
示例 3:

输入:nums = [2,10,8]
输出:3

提示:

n == nums.length
2 <= n <= 10^5
1 <= nums[i] <= 10^9

题目解析

试想一下如果所有数字都偶数,那我们唯一可操作的就是把最大数字除以2,这样结果才有可能变优。
再来考虑有奇数的情况。我们可以先把所有奇数都乘以2。这样就变成都是偶数的情况。如果有些数字结果需要变成奇数,通过除以2,最终肯定会到达奇数状态。

具体过程,先把所有奇数乘以2变偶数。
每次取最大值,是否为偶数,如果不是就停止算法返回结果。
如果是偶数则除以2,更新最优结果。

这里就需要1个数据结果,他可以让我们快速取得最大值,并且快速更新1个数。
我首先想到的是有序map, 更新值可能通过删除原来的值,再插入新值来实现。
c++中有map, go中的map是无序的。我使用跳表来实现。

AC 代码

C++

typedef long long lld;

class Solution {
public:
    int minimumDeviation(vector<int>& nums) {
        set<lld> s;
        vector<lld> odd; // 记录所有奇数
        for(lld n: nums) {
            s.insert(n);
            if(n&1) odd.push_back(n);
        }
        sort(odd.begin(), odd.end());
        if(s.size()==1)return 0;

        lld ans = *s.rbegin() - *s.begin();

        // 从小到大奇数变偶数,并更新结果
        for(lld n: odd) {
            s.erase(n);
            s.insert(2*n);
            lld t = *s.rbegin() - *s.begin();
            ans = min(ans, t);
        }

        // 最大偶数除以2
        for(;(*s.rbegin() &1)==0;) {
            lld t = *s.rbegin();
            s.erase(*s.rbegin());
            s.insert(t>>1);
            t = *s.rbegin() - *s.begin();
            ans = min(ans, t);
        }

        return ans;
    }
};

GO

import "math/rand"
/*
type KV interface {
	GetKey() interface{}
	GetValue() interface{}
	Less(b KV) bool
}*/

type KV struct {
	k int
}

func (a KV) GetValue() int {
	return a.k
}

func (a KV) GetKey() int {
	return a.k
}

func (a KV) Less(b KV) bool {
	return a.k<b.GetKey()
}

const SKIPLIST_MAXLEVEL = 16

/*
以1/4的概率递增,最多32层
*/
func slRandomLevel() int {
	level := 1
	for (rand.Int() & 0xFFFF) < (0xFFFF >> 2) {
		level++
	}

	if level < SKIPLIST_MAXLEVEL {
		return level
	}
	return SKIPLIST_MAXLEVEL
}

// 利用跳表实现可排序的map
/*跳表节点定义*/
type leveInfo struct {
	// 前进指针
	forward *skiplistNode
	// 跨度
	span int64
}

type skiplistNode struct {
	// value
	elem KV
	// 后退指针
	backward *skiplistNode
	// 层
	level []leveInfo
}

type Skiplist struct {
	// 跳跃表头指针
	header *skiplistNode
	tail   *skiplistNode
	// 表中节点的数量
	length int64
	// 表中层数最大的节点的层数
	level int
}

func slCreateNode(level int, e KV) *skiplistNode {
	node := &skiplistNode{
		elem:     e,
		backward: nil,
		level:    make([]leveInfo, level),
	}
	return node
}

func NewSkipList() *Skiplist {
	list := &Skiplist{
		length: 0,
		level:  1,
		header: slCreateNode(SKIPLIST_MAXLEVEL, KV{0}),
		tail:   nil,
	}

	return list
}

/*
添加元素
*/
func (list *Skiplist) Add(e KV) {
	var (
		// 存储搜索路径
		update [SKIPLIST_MAXLEVEL]*skiplistNode
		x      *skiplistNode
		// 存储经过的节点跨度
		rank [SKIPLIST_MAXLEVEL]int64
	)

	var i int

	// 1. 逐步降级寻找目标节点,得到 "搜索路径"
	for x, i = list.header, list.level-1; i >= 0; i-- {
		// 前缀和rank[i]代表 update[i]之前经过的结点个数
		if i == list.level-1 {
			rank[i] = 0
		} else {
			rank[i] = rank[i+1]
		}

		// 比较elem与e 找到当前层比e小的最后一个结点
		for ; x.level[i].forward != nil && x.level[i].forward.elem.Less(e); x = x.level[i].forward {
			rank[i] += x.level[i].span
		}

		update[i] = x
	}

	// 2. 生成新节点高度,超过了当前最大高度需要对高出部分初始化
	level := slRandomLevel()
	for i = list.level; i < level; i++ {
		rank[i] = 0
		update[i] = list.header
		update[i].level[i].span = list.length
	}
	if level > list.level {
		list.level = level
	}

	// 3. 创建新节点
	x = slCreateNode(level, e)

	// 4. 重排向前指针
	for i = 0; i < level; i++ {
		x.level[i].forward = update[i].level[i].forward
		update[i].level[i].forward = x

		// 更新跨度
		x.level[i].span = update[i].level[i].span - (rank[0] - rank[i])
		update[i].level[i].span = rank[0] - rank[i] + 1
	}

	for i = level; i < list.level; i++ {
		update[i].level[i].span++
	}

	// 向后结点更新
	if update[0] != list.header {
		x.backward = update[0]
	}

	if x.level[0].forward != nil {
		x.level[0].forward.backward = x
	} else {
		list.tail = x
	}
	list.length++
}

// 删除一个结点
func (list *Skiplist) slDeleteNode(x *skiplistNode, update [SKIPLIST_MAXLEVEL]*skiplistNode) {
	for i := 0; i < list.level; i++ {
		// 被删除节点在第i层有节点,则update[i]为被删除节点的前一个节点
		if update[i].level[i].forward == x {
			// 步长 = 原步长 + 被删除节点步长 - 1(被删除节点)
			update[i].level[i].span += x.level[i].span - 1
			// 指针越过被删除节点
			update[i].level[i].forward = x.level[i].forward
		} else {
			// 被删除节点在第i层无节点,则 步长 = 原步长 - 1(被删除节点)
			update[i].level[i].span -= 1
		}
	}
	if x.level[0].forward != nil {
		// 更新被删除节点下一节点的后退指针
		x.level[0].forward.backward = x.backward
	} else {
		list.tail = x.backward
	}
	for list.level > 1 && list.header.level[list.level-1].forward == nil {
		list.level--
	}
	list.length--
}

// 删除结点
func (list *Skiplist) Deletee(e KV) bool {
	var (
		// 存储搜索路径
		update [SKIPLIST_MAXLEVEL]*skiplistNode
		x      *skiplistNode
	)

	x = list.header;
	for i := list.level-1; i >= 0; i-- {
		for ; x.level[i].forward != nil && x.level[i].forward.elem.Less(e); x = x.level[i].forward {}
		update[i] = x;
	}

	x = x.level[0].forward;
	if x!=nil && !x.elem.Less(e) && !e.Less(x.elem) {
		list.slDeleteNode(x, update);
		return true;
	}
	return false; /* not found */
}

// 遍历结点
func (list *Skiplist) Range(f func(e KV)) {
	for x := list.header.level[0].forward; x != nil; x = x.level[0].forward {
		f(x.elem)
	}
}



var cnt =0

func minimumDeviation(nums []int) int {
	cnt++
	// if cnt==76{return 0}
	m:=map[int]int{}
	odd := []int{};
	for _, n:= range nums {
		m[n]=1;
	}

	sl := NewSkipList()

	// 筛选出所有奇数
	for k:= range m {
		sl.Add(KV{k})
		if (k&1)>0 {
			odd = append(odd, k)
		}
	}

	sort.Ints(odd); // 排序奇数
	if len(m)==1 {return 0;}


	ans := sl.tail.elem.GetKey()-   sl.header.level[0].forward.elem.GetKey()
	// 从小到大更新奇数,并更新最优解
	for _, n:=range odd{
		sl.Deletee(KV{n})
		sl.Add(KV{2*n})
		t := sl.tail.elem.GetKey() -   sl.header.level[0].forward.elem.GetKey()
		if t< ans {
			ans = t
		}
	}

	// 对最大偶数除以2,并更新结果
	for s :=sl.tail.elem.GetKey() ;(s&1)==0; s =sl.tail.elem.GetKey() {
		sl.Deletee(KV{s})
		sl.Add(KV{s>>1})
		t := sl.tail.elem.GetKey() -   sl.header.level[0].forward.elem.GetKey()
		if t< ans {
			ans = t
		}
	}

	return ans;
}

性能问题

AK F.*ing leetcode 流浪计划之跳表_第1张图片
前几次超时了,是因为用了接口转int的语句比较耗时,直接改成结构做就好了。


本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。

你可能感兴趣的:(leetcode,算法,数据结构,跳表)