Golang | 集合求交

文章目录

bitmap求交集


2个有序链表

多个有序链表

  • 为什么非最大的所有都要往后移动呢?因为现在已经知道交集即使有,也最小都是这个目前最大的了,其他不是最大的不可能是交集,所有除了最大其他都往后移动就行

跳表

  • 跳表(Skip List)是一种用于高效查找、插入和删除有序元素的数据结构,结合了链表和二分查找的思想,空间换时间。它通过构建多层链表来实现快速搜索,平均时间复杂度为 O(logn),且实现比平衡树更简单。
  • 多层链表结构:
    • 底层是完整的有序链表,包含所有元素。
    • 每一层都是下一层的"快速通道",节点随机晋升到更高层,形成类似"索引"的结构。
    • 高层节点稀疏,低层节点密集。
  • 随机晋升:
    • 插入新节点时,通过随机算法(如抛硬币)决定晋升到多少层。
    • 晋升概率通常为1/2,即每个节点有 50% 的概率出现在上一层。


go 复制代码
package main

import (
	"fmt"
	"math/rand"
	"time"
)

const (
	MaxLevel    = 16   // 跳表的最大层数
	Probability = 0.5  // 每增加一层的概率
)

// 节点结构
type Node struct {
	value int
	next  []*Node
}

// 跳表结构
type SkipList struct {
	head  *Node
	level int
}

// 创建新节点
func newNode(value, level int) *Node {
	return &Node{
		value: value,
		next:  make([]*Node, level),
	}
}

// 创建跳表
func NewSkipList() *SkipList {
	rand.Seed(time.Now().UnixNano())
	return &SkipList{
		head:  newNode(0, MaxLevel),
		level: 1,
	}
}

// 随机层数
func (sl *SkipList) randomLevel() int {
	level := 1
	for rand.Float64() < Probability && level < MaxLevel {
		level++
	}
	return level
}

// 查找
func (sl *SkipList) Search(target int) bool {
	curr := sl.head
	for i := sl.level - 1; i >= 0; i-- {
		for curr.next[i] != nil && curr.next[i].value < target {
			curr = curr.next[i]
		}
	}
	curr = curr.next[0]
	return curr != nil && curr.value == target
}

// 插入
func (sl *SkipList) Insert(value int) {
	update := make([]*Node, MaxLevel)
	curr := sl.head

	for i := sl.level - 1; i >= 0; i-- {
		for curr.next[i] != nil && curr.next[i].value < value {
			curr = curr.next[i]
		}
		update[i] = curr
	}

	level := sl.randomLevel()
	if level > sl.level {
		for i := sl.level; i < level; i++ {
			update[i] = sl.head
		}
		sl.level = level
	}

	newNode := newNode(value, level)
	for i := 0; i < level; i++ {
		newNode.next[i] = update[i].next[i]
		update[i].next[i] = newNode
	}
}

// 打印跳表(调试用)
func (sl *SkipList) Print() {
	for i := sl.level - 1; i >= 0; i-- {
		curr := sl.head.next[i]
		fmt.Printf("Level %d: ", i+1)
		for curr != nil {
			fmt.Printf("%d ", curr.value)
			curr = curr.next[i]
		}
		fmt.Println()
	}
}

func main() {
	sl := NewSkipList()
	sl.Insert(1)
	sl.Insert(3)
	sl.Insert(5)
	sl.Insert(7)
	sl.Insert(9)

	sl.Print()

	fmt.Println("Search 5:", sl.Search(5))  // true
	fmt.Println("Search 6:", sl.Search(6))  // false
}
  • 两跳表求交集
go 复制代码
func IntersectionOfSkipList(lists ...*skiplist.SkipList) (res *skiplist.SkipList) {
	if len(lists) == 0 {
		return nil
	}
	if len(lists) == 1 {
		return lists[0]
	}

	res = skiplist.New(skiplist.Uint64)
	nodes := make([]*skiplist.Element, len(lists))
	for i, list := range lists {
		if list == nil || list.Len() == 0 {
			return nil
		}
		nodes[i] = list.Front()
	}

	for {
		var maxList map[int]struct{} // 用于存储当前值最大的节点(可能有多个,所以用map来模仿集合)
		var maxValue uint64 = 0
		for i, node := range nodes {
			if node.Key().(uint64) > maxValue {
				maxValue = node.Key().(uint64)
				maxList = map[int]struct{}{i: {}}
			} else if node.Key().(uint64) == maxValue {
				maxList[i] = struct{}{}
			}
		}
		if len(maxList) == len(lists) { // 所有node节点都指向了最大值,可以添加到交集
			res.Set(nodes[0].Key(), nodes[0].Value)
			for i, node := range nodes { // 所有node节点往后移
				nodes[i] = node.Next()
				if nodes[i] == nil {
					return
				}
			}
		} else {
			for i, node := range nodes {
				if _, exists := maxList[i]; !exists {
					nodes[i] = node.Next()
					if nodes[i] == nil {
						return
					}
				}
			}
		}
	}
}

go 复制代码
func (sl *SkipList) Intersection(other *SkipList) []int {
	result := []int{}

	// 从最底层第一个节点开始
	p1 := sl.head.next[0]
	p2 := other.head.next[0]

	for p1 != nil && p2 != nil {
		if p1.value == p2.value {
			result = append(result, p1.value)
			p1 = p1.next[0]
			p2 = p2.next[0]
		} else if p1.value < p2.value {
			p1 = p1.next[0]
		} else {
			p2 = p2.next[0]
		}
	}

	return result
}


// 加速查找


// Search查找一个元素,返回是否存在
func (sl *SkipList) Search(value int) bool {
	curr := sl.head
	for i := sl.level - 1; i >= 0; i-- {
		for curr.next[i] != nil && curr.next[i].value < value {
			curr = curr.next[i]
		}
	}
	curr = curr.next[0]
	return curr != nil && curr.value == value
}

// 交集(基于快速查找)
func (sl1 *SkipList) IntersectionWith(sl2 *SkipList) []int {
	result := []int{}

	curr := sl2.head.next[0] // 遍历第二个跳表的底层节点
	for curr != nil {
		if sl1.Search(curr.value) { // 用第一个跳表查找
			result = append(result, curr.value)
		}
		curr = curr.next[0]
	}

	return result
}
  • 多跳表求交集
go 复制代码
// Search查找一个元素,返回是否存在
func (sl *SkipList) Search(value int) bool {
	curr := sl.head
	for i := sl.level - 1; i >= 0; i-- {
		for curr.next[i] != nil && curr.next[i].value < value {
			curr = curr.next[i]
		}
	}
	curr = curr.next[0]
	return curr != nil && curr.value == value
}

// 多跳表交集(基于跳表加速查找)
func IntersectionSkipLists(lists []*SkipList) []int {
	if len(lists) == 0 {
		return []int{}
	}
	if len(lists) == 1 {
		// 只有一个跳表,直接返回所有元素
		result := []int{}
		curr := lists[0].head.next[0]
		for curr != nil {
			result = append(result, curr.value)
			curr = curr.next[0]
		}
		return result
	}

	// 1. 找到元素最少的跳表作为主跳表
	mainList := lists[0]
	for _, sl := range lists[1:] {
		if sl.Size() < mainList.Size() {
			mainList = sl
		}
	}

	// 2. 依次遍历主跳表的元素
	result := []int{}
	curr := mainList.head.next[0]
	for curr != nil {
		found := true
		for _, sl := range lists {
			if sl == mainList {
				continue
			}
			if !sl.Search(curr.value) {
				found = false
				break
			}
		}
		if found {
			result = append(result, curr.value)
		}
		curr = curr.next[0]
	}
	return result
}

// Size 计算跳表元素数量
func (sl *SkipList) Size() int {
	cnt := 0
	curr := sl.head.next[0]
	for curr != nil {
		cnt++
		curr = curr.next[0]
	}
	return cnt
}
相关推荐
兜小糖的小秃毛5 分钟前
文号验证-同时对两个输入框验证
开发语言·前端·javascript
brzhang6 分钟前
代码越写越乱?掌握这 5 种架构模式,小白也能搭出清晰系统!
前端·后端·架构
Asthenia04128 分钟前
为什么MySQL关联查询要“小表驱动大表”?深入解析与模拟面试复盘
后端
南雨北斗10 分钟前
分布式系统中如何保证数据一致性
后端
Asthenia041214 分钟前
Feign结构与请求链路详解及面试重点解析
后端
左灯右行的爱情17 分钟前
缓存并发更新的挑战
jvm·数据库·redis·后端·缓存
brzhang21 分钟前
告别『上线裸奔』!一文带你配齐生产级 Web 应用的 10 大核心组件
前端·后端·架构
shepherd11122 分钟前
Kafka生产环境实战经验深度总结,让你少走弯路
后端·面试·kafka
anqi2731 分钟前
如何在 IntelliJ IDEA 中编写 Speak 程序
java·大数据·开发语言·spark·intellij-idea
XuX0335 分钟前
MATLAB小试牛刀系列(1)
开发语言·matlab