/
githubmirror
/
LeetCode-Go
Обзор
Документация
Войти
/
githubmirror
/
LeetCode-Go
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
website/public/zh/ChapterThree/index.xml
75 строк
9 KB
halfrost
update en/zh path
30 июн 2026, 01:09
30 июн 2026, 01:09
584295b
Код
Авторство
О чём код?
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title>第三章 一些模板 on LeetCode Cookbook</title> <link>https://books.halfrost.com/leetcode/zh/ChapterThree/</link> <description>Recent content in 第三章 一些模板 on LeetCode Cookbook</description> <generator>Hugo -- gohugo.io</generator> <language>zh-CN</language> <atom:link href="https://books.halfrost.com/leetcode/zh/ChapterThree/index.xml" rel="self" type="application/rss+xml" /> <item> <title>3.1 Segment Tree</title> <link>https://books.halfrost.com/leetcode/zh/ChapterThree/Segment_Tree/</link> <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> <guid>https://books.halfrost.com/leetcode/zh/ChapterThree/Segment_Tree/</guid> <description>线段树 Segment Tree # 线段树 Segment tree 是一种二叉树形数据结构,1977年由 Jon Louis Bentley 发明,用以存储区间或线段,并且允许快速查询结构内包含某一点的所有区间。 一个包含 \(n \) 个区间的线段树,空间复杂度为 \( O(n) \) ,查询的时间复杂度则为 \(O(log n&#43;k) \) ,其中 \( k \) 是符合条件的区间数量。线段树的数据结构也可推广到高维度。 一. 什么是线段树 # 以一维的线段树为例。 令 S 是一维线段的集合。将这些线段的端点坐标由小到大排序,令其为 \(x_{1},x_{2},\cdots ,x_{m} \) 。我们将被这些端点切分的每一个区间称为“单位区间”(每个端点所在的位置会单独成为一个单位区间),从左到右包含: \[ (-\infty ,x_{1}),[x_{1},x_{1}],(x_{1},x_{2}),[x_{2},x_{2}],...,(x_{m-1},x_{m}),[x_{m},x_{m}],(x_{m},&#43;\infty )\] 线段树的结构为一个二叉树,每个节点都代表一个坐标区间,节点 N 所代表的区间记为 Int(N),则其需符合以下条件: 其每一个叶节点,从左到右代表每个单位区间。 其内部节点代表的区间是其两个儿子代表的区间之并集。 每个节点(包含叶子)中有一个存储线段的数据结构。若一个线段 S 的坐标区间包含 Int(N) 但不包含 Int(parent(N)),则节点 N 中会存储线段 S。 线段树是二叉树,其中每个节点代表一个区间。通常,一个节点将存储一个或多个合并的区间的数据,以便可以执行查询操作。</description> </item> <item> <title>3.2 UnionFind</title> <link>https://books.halfrost.com/leetcode/zh/ChapterThree/UnionFind/</link> <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> <guid>https://books.halfrost.com/leetcode/zh/ChapterThree/UnionFind/</guid> <description>并查集 UnionFind # package template // UnionFind defind // 路径压缩 + 秩优化 type UnionFind struct { parent, rank []int count int } // Init define func (uf *UnionFind) Init(n int) { uf.count = n uf.parent = make([]int, n) uf.rank = make([]int, n) for i := range uf.parent { uf.parent[i] = i } } // Find define func (uf *UnionFind) Find(p int) int { root := p for root != uf.</description> </item> <item> <title>3.3 LRUCache</title> <link>https://books.halfrost.com/leetcode/zh/ChapterThree/LRUCache/</link> <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> <guid>https://books.halfrost.com/leetcode/zh/ChapterThree/LRUCache/</guid> <description>最近最少使用 LRUCache # LRU 是 Least Recently Used 的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。如上图,要插入 F 的时候,此时需要淘汰掉原来的一个页面。 根据 LRU 的策略,每次都淘汰最近最久未使用的页面,所以先淘汰 A 页面。再插入 C 的时候,发现缓存中有 C 页面,这个时候需要把 C 页面放到首位,因为它被使用了。以此类推,插入 G 页面,G 页面是新页面,不在缓存中,所以淘汰掉 B 页面。插入 H 页面,H 页面是新页面,不在缓存中,所以淘汰掉 D 页面。插入 E 的时候,发现缓存中有 E 页面,这个时候需要把 E 页面放到首位。插入 I 页面,I 页面是新页面,不在缓存中,所以淘汰掉 F 页面。 可以发现,LRU 更新和插入新页面都发生在链表首,删除页面都发生在链表尾。 解法一 Get O(1) / Put O(1) # LRU 要求查询尽量高效,O(1) 内查询。那肯定选用 map 查询。修改,删除也要尽量 O(1) 完成。搜寻常见的数据结构,链表,栈,队列,树,图。树和图排除,栈和队列无法任意查询中间的元素,也排除。所以选用链表来实现。但是如果选用单链表,删除这个结点,需要 O(n) 遍历一遍找到前驱结点。所以选用双向链表,在删除的时候也能 O(1) 完成。 由于 Go 的 container 包中的 list 底层实现是双向链表,所以可以直接复用这个数据结构。定义 LRUCache 的数据结构如下:</description> </item> <item> <title>3.4 LFUCache</title> <link>https://books.halfrost.com/leetcode/zh/ChapterThree/LFUCache/</link> <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> <guid>https://books.halfrost.com/leetcode/zh/ChapterThree/LFUCache/</guid> <description>最不经常最少使用 LFUCache # LFU 是 Least Frequently Used 的缩写,即最不经常最少使用,也是一种常用的页面置换算法,选择访问计数器最小的页面予以淘汰。如下图,缓存中每个页面带一个访问计数器。 根据 LFU 的策略,每访问一次都要更新访问计数器。当插入 B 的时候,发现缓存中有 B,所以增加访问计数器的计数,并把 B 移动到访问计数器从大到小排序的地方。再插入 D,同理先更新计数器,再移动到它排序以后的位置。当插入 F 的时候,缓存中不存在 F,所以淘汰计数器最小的页面的页面,所以淘汰 A 页面。此时 F 排在最下面,计数为 1。 这里有一个比 LRU 特别的地方。如果淘汰的页面访问次数有多个相同的访问次数,选择最靠尾部的。如上图中,A、B、C 三者的访问次数相同,都是 1 次。要插入 F,F 不在缓存中,此时要淘汰 A 页面。F 是新插入的页面,访问次数为 1,排在 C 的前面。也就是说相同的访问次数,按照新旧顺序排列,淘汰掉最旧的页面。这一点是和 LRU 最大的不同的地方。 可以发现,LFU 更新和插入新页面可以发生在链表中任意位置,删除页面都发生在表尾。 解法一 Get O(1) / Put O(1) # LFU 同样要求查询尽量高效,O(1) 内查询。依旧选用 map 查询。修改和删除也需要 O(1) 完成,依旧选用双向链表,继续复用 container 包中的 list 数据结构。LFU 需要记录访问次数,所以每个结点除了存储 key,value,需要再多存储 frequency 访问次数。</description> </item> <item> <title>3.5 Binary Indexed Tree</title> <link>https://books.halfrost.com/leetcode/zh/ChapterThree/Binary_Indexed_Tree/</link> <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> <guid>https://books.halfrost.com/leetcode/zh/ChapterThree/Binary_Indexed_Tree/</guid> <description>树状数组 Binary Indexed Tree (二叉索引树) # 树状数组或二叉索引树(Binary Indexed Tree),又以其发明者命名为 Fenwick 树,最早由 Peter M. Fenwick 于 1994 年以 A New Data Structure for Cumulative Frequency Tables 为题发表在 SOFTWARE PRACTICE AND EXPERIENCE 上。其初衷是解决数据压缩里的累积频率(Cumulative Frequency)的计算问题,现多用于高效计算数列的前缀和,区间和。针对区间问题,除了常见的线段树解法,还可以考虑树状数组。它可以以 O(log n) 的时间得到任意前缀和 \( \sum_{i=1}^{j}A[i],1&lt;=j&lt;=N \) ,并同时支持在 O(log n)时间内支持动态单点值的修改(增加或者减少)。空间复杂度 O(n)。 利用数组实现前缀和,查询本来是 O(1),但是对于频繁更新的数组,每次重新计算前缀和,时间复杂度 O(n)。此时树状数组的优势便立即显现。 一. 一维树状数组概念 # 树状数组名字虽然又有树,又有数组,但是它实际上物理形式还是数组,不过每个节点的含义是树的关系,如上图。树状数组中父子节点下标关系是 \(parent = son &#43; 2^{k}\) ,其中 k 是子节点下标对应二进制末尾 0 的个数。 例如上图中 A 和 B 都是数组。A 数组正常存储数据,B 数组是树状数组。B4,B6,B7 是 B8 的子节点。4 的二进制是 100,4 + \(2^{2}\) = 8,所以 8 是 4 的父节点。同理,7 的二进制 111,7 + \(2^{0}\) = 8,8 也是 7 的父节点。</description> </item> </channel> </rss>