757. 设置交集大小至少为2 (leetcode每日一题)

set-intersection-size-at-least-two

属性 内容
难度 hard
标签 sorting, greedy, segment-tree
链接 在线题目
评分 8
创建时间 2025-11-20 11:59:25
更新时间 2025-11-20 12:01:55

📖 题目描述

暂无题目描述


💡 解题思路

主要是学习动态开点线段树怎么写,自顶向下查询即可. 如果查询区间包含了当前node的l和r则直接返回数据,否则二分之后继续递归查询。

更新也是一样的逻辑,如果当前区间已经包含了当前的l和r则直接更新当前区间即可。

注意有一个懒更新的思想,每一个node节点都有一个lazy标记,查询和更新操作下一层子节点之前,需要判断当前node是否有lazy标记,如有,需要及时更新子节点的值,再去做查询和更新操作


🛠 代码实现

go 复制代码
package leetcode


import (
	"math"
	"sort"
)

func intersectionSizeTwo(intervals [][]int) int {
	sort.Slice(intervals, func(i, j int) bool {
		return intervals[i][1] < intervals[j][1]
	})
	// 排序之后 数组为 [1, 4] [2, 5] [6, 7]这种类似的 可以看出来 优先选择
	mx := 0
	mn := math.MaxInt32
	for i := 0; i < len(intervals); i++ {
		intervals[i] = append(intervals[i], 2)
		mx = max(mx, intervals[i][1])
		mn = min(mn, intervals[i][0])
	}
	tree := NewSegmentTree(mn, mx, &SegmentTreeOption[int]{MergeVal: func(a, b int) int {
		return a + b
	}, InitVal: func() int {
		return 0
	}, UpdateNode: func(val int, left, right int) int {
		return (right - left + 1) * val
	},
	})
	// 贪心思想 每一个区间判断当前有多少个已经存在的数字 是否还需要添加 如需要添加从最尾端开始尝试添加即可
	// 正对查询一个区间已存在多少数字的逻辑 可以通过线段树来做
	ans := 0
	vis := make(map[int]interface{})
	for i := 0; i < len(intervals); i++ {
		cnt := tree.Query(intervals[i][0], intervals[i][1])
		if cnt < intervals[i][2] {
			temp := intervals[i][2] - cnt
			for j := intervals[i][1]; j >= intervals[i][0]; j-- {
				if _, ok := vis[j]; ok {
					continue
				}
				vis[j] = struct{}{}
				temp--
				ans++
				tree.Update(j, j, 1)
				if temp == 0 {
					break
				}
			}
		}
	}
	return ans
}

type SegmentTree[T any] struct {
	root    *segmentTreeNode[T]
	options *SegmentTreeOption[T]
}

type SegmentTreeOption[T any] struct {
	MergeVal   func(a, b T) T
	UpdateNode func(val T, left, right int) T
	InitVal    func() T
}

type segmentTreeNode[T any] struct {
	left, right int
	lc, rc      *segmentTreeNode[T]
	val         T
	lazy        *T
}

func NewSegmentTree[T any](left, right int, opt *SegmentTreeOption[T]) *SegmentTree[T] {
	return &SegmentTree[T]{root: &segmentTreeNode[T]{left: left, right: right, val: opt.InitVal()}, options: opt}
}

func (tree *SegmentTree[T]) pushDown(node *segmentTreeNode[T]) {
	if node.lazy == nil {
		return
	}
	mid := (node.left + node.right) >> 1
	if node.lc == nil {
		node.lc = tree.createNode(node.left, mid)
	}
	if node.rc == nil {
		node.rc = tree.createNode(mid+1, node.right)
	}

	// 下推更新
	node.lc.val = tree.options.MergeVal(node.lc.val, tree.options.UpdateNode(*node.lazy, node.lc.left, node.lc.right))
	node.rc.val = tree.options.MergeVal(node.rc.val, tree.options.UpdateNode(*node.lazy, node.rc.left, node.rc.right))

	tree.updateLazy(node.lc, *node.lazy)
	tree.updateLazy(node.rc, *node.lazy)

	// 清空当前节点的 lazy
	node.lazy = nil
}

func (tree *SegmentTree[T]) Query(left, right int) T {
	return tree.query(tree.root, left, right)
}

func (tree *SegmentTree[T]) query(node *segmentTreeNode[T], left, right int) (ans T) {
	if right < node.left || left > node.right {
		panic("right out of range or left out of range")
	}

	if node.left >= left && node.right <= right {
		return node.val
	}
	tree.pushDown(node)
	mid := (node.left + node.right) >> 1
	if node.lc == nil {
		node.lc = tree.createNode(node.left, mid)
	}
	if node.rc == nil {
		node.rc = tree.createNode(mid+1, node.right)
	}

	if tree.options.InitVal != nil {
		ans = tree.options.InitVal()
	}

	if left <= mid {
		ans = tree.options.MergeVal(ans, tree.query(node.lc, left, min(mid, right)))
	}
	if right > mid {
		ans = tree.options.MergeVal(ans, tree.query(node.rc, max(left, mid+1), right))
	}
	return ans
}

func (tree *SegmentTree[T]) Update(left, right int, val T) {
	tree.update(tree.root, left, right, val)
}

func (tree *SegmentTree[T]) update(node *segmentTreeNode[T], left, right int, val T) {
	if right < node.left || left > node.right {
		return
	}
	if node.left >= left && node.right <= right {
		node.val = tree.options.MergeVal(node.val, tree.options.UpdateNode(val, node.left, node.right))
		tree.updateLazy(node, val)
		return
	}
	tree.pushDown(node)
	mid := (node.left + node.right) >> 1
	if node.lc == nil {
		node.lc = tree.createNode(node.left, mid)
	}
	if node.rc == nil {
		node.rc = tree.createNode(mid+1, node.right)
	}
	if left <= mid {
		tree.update(node.lc, left, right, val)
	}
	if right > mid {
		tree.update(node.rc, left, right, val)
	}
	node.val = tree.options.MergeVal(node.lc.val, node.rc.val)
}

func (tree *SegmentTree[T]) updateLazy(node *segmentTreeNode[T], val T) {
	if node.left != node.right {
		if node.lazy == nil {
			node.lazy = new(T)
			*node.lazy = val
		} else {
			*node.lazy = tree.options.MergeVal(*node.lazy, val)
		}
	}
}

func (tree *SegmentTree[T]) createNode(left, right int) *segmentTreeNode[T] {
	node := &segmentTreeNode[T]{
		left:  left,
		right: right,
	}
	if tree.options.InitVal != nil {
		node.val = tree.options.InitVal()
	}
	return node
}
相关推荐
weixin199701080168 分钟前
[特殊字符] 1688开放平台API Sign签名算法详解(Java / Python / PHP 实现)
java·python·算法
未若君雅裁20 分钟前
JVM 垃圾回收算法与分代回收机制
java·jvm·算法
智者知已应修善业26 分钟前
【51单片机初始化D5-D8亮,每按键按下D1到D4全亮,再按下恢复,如此循环】2024-3-26
c++·经验分享·笔记·算法·51单片机
8Qi81 小时前
LeetCode 4:寻找两个正序数组的中位数 —— 二分查找法
java·算法·leetcode·职场和发展·二分查找
8Qi81 小时前
LeetCode 32:最长有效括号 —— 栈 + 标记法 题解
java·数据结构·算法·leetcode·职场和发展··括号匹配
机器学习之心1 小时前
198种组合算法+优化CNN-LSTM+SHAP分析+新数据预测+多输出!深度学习可解释分析,强烈安利,粉丝必备
深度学习·算法·cnn-lstm·shap分析·198种组合算法
Tairitsu_H1 小时前
[LC优选算法#3] 滑动窗口 | 将x减到0的最⼩操作数 | ⽔果成篮 | 字⺟异位词
c++·算法·leetcode·滑动窗口
bIo7lyA8v1 小时前
算法复杂度与能耗关系的多变量分析研究的技术8
算法
洛水水2 小时前
【力扣100题】76.搜索插入位置
数据结构·算法·leetcode
Techblog of HaoWANG2 小时前
智巡守卫:多模态巡检智能体算法服务端设计与实现——基于Ollama+Qwen3.5的自动化巡检报告生成系统
运维·人工智能·算法·目标检测·自动化·边缘计算