<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>原理 on 洛克里亚的五度 博客</title>
    <link>https://lv-blog.pages.dev/tags/%E5%8E%9F%E7%90%86/</link>
    <description>Recent content in 原理 on 洛克里亚的五度 博客</description>
    <generator>Hugo</generator>
    <language>zh</language>
    <lastBuildDate>Sat, 11 Jul 2026 17:00:18 +0800</lastBuildDate>
    <atom:link href="https://lv-blog.pages.dev/tags/%E5%8E%9F%E7%90%86/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>线性探测与链地址法的剖析</title>
      <link>https://lv-blog.pages.dev/posts/programming/backend/%E7%BA%BF%E6%80%A7%E6%8E%A2%E6%B5%8B%E4%B8%8E%E9%93%BE%E5%9C%B0%E5%9D%80%E6%B3%95%E7%9A%84%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90/</link>
      <pubDate>Sat, 11 Jul 2026 17:00:18 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/posts/programming/backend/%E7%BA%BF%E6%80%A7%E6%8E%A2%E6%B5%8B%E4%B8%8E%E9%93%BE%E5%9C%B0%E5%9D%80%E6%B3%95%E7%9A%84%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90/</guid>
      <description>&lt;h2 id=&#34;为什么我们需要两种冲突解决策略&#34;&gt;为什么我们需要两种冲突解决策略？&lt;/h2&gt;
&lt;p&gt;哈希表的核心矛盾在于：&lt;strong&gt;哈希函数将无限的定义域映射到有限的地址空间&lt;/strong&gt;。根据鸽巢原理（Pigeonhole Principle），冲突是必然的。于是，计算机科学家们在&amp;quot;&lt;strong&gt;时间换空间&lt;/strong&gt;&amp;ldquo;和&amp;rdquo;&lt;strong&gt;空间换时间&lt;/strong&gt;&amp;ldquo;的永恒博弈中，诞生了两大流派：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;开放定址法（Open Addressing）&lt;/strong&gt;：所有元素都存储在数组本身，冲突时寻找其他空位——&lt;strong&gt;原地消化&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;链地址法（Separate Chaining）&lt;/strong&gt;：数组存储指针，冲突的元素在数组外形成链表——&lt;strong&gt;外部延展&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两者看似只是实现细节的差异，实则反映了对&lt;strong&gt;内存模型、缓存行为、并发控制、最坏情况保证&lt;/strong&gt;等底层哲学的不同理解。&lt;/p&gt;
&lt;h2 id=&#34;一线性探测linear-probing极简主义的得与失&#34;&gt;一、线性探测（Linear Probing）：极简主义的得与失&lt;/h2&gt;
&lt;h3 id=&#34;11-算法精要&#34;&gt;1.1 算法精要&lt;/h3&gt;
&lt;p&gt;线性探测的规则极其简单：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;插入: h(k), h(k)+1, h(k)+2, ... (mod m)
查找: 同插入序列，直到找到或遇到空位
删除: 标记为&amp;#34;墓碑&amp;#34;(DELETED)，不能物理清空
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：线性探测的查找路径是&lt;strong&gt;确定性的&lt;/strong&gt;，这意味着它拥有完美的CPU缓存预取特性（Prefetching），在现代CPU架构下，连续内存访问的速度比指针跳转快一个数量级。&lt;/p&gt;
&lt;h3 id=&#34;12-主聚集primary-clustering的数学本质&#34;&gt;1.2 主聚集（Primary Clustering）的数学本质&lt;/h3&gt;
&lt;p&gt;这是线性探测最致命的缺陷，也是理解其性能天花板的钥匙。&lt;/p&gt;
&lt;p&gt;假设负载因子为 α（α = n/m），当插入一个新元素时，它在某个位置发生冲突的概率不是简单的 α，而是&lt;strong&gt;该位置所在的连续占用块的长度占比&lt;/strong&gt;。这个正反馈机制导致：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一旦形成长度为 L 的连续占用块，新元素落入该块的概率为 L/m&lt;/li&gt;
&lt;li&gt;落入后，块长度变为 L+1，进一步增加未来冲突概率&lt;/li&gt;
&lt;li&gt;最终导致&lt;strong&gt;占用块像雪崩一样快速增长&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;数学结论&lt;/strong&gt;（Knuth 的经典分析）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;成功查找的平均探测次数 ≈ &lt;code&gt;0.5 * (1 + 1/(1-α))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;插入的平均探测次数 ≈ &lt;code&gt;0.5 * (1 + 1/(1-α)^2)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当 α = 0.9 时，插入需要平均约 &lt;strong&gt;50.5 次&lt;/strong&gt;探测，性能崩溃。这也是为什么线性探测要求负载因子严格控制在 &lt;strong&gt;0.7 以下&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;13-删除与墓碑的隐藏代价&#34;&gt;1.3 删除与墓碑的隐藏代价&lt;/h3&gt;
&lt;p&gt;线性探测的删除必须使用墓碑，这带来三重悲剧：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;空间污染&lt;/strong&gt;：墓碑占用的位置无法被新数据直接覆盖（需要判断是否允许覆盖，复杂化逻辑）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查询退化&lt;/strong&gt;：大量的墓碑会导致查找遍历过长的已删除序列&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要周期性重建&lt;/strong&gt;（Rehash）：当墓碑比例超过阈值（如 30%），需要重新插入所有存活元素&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 墓碑导致的查找效率下降示意
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ A ] [ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;墓碑&lt;/span&gt; ] [ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;墓碑&lt;/span&gt; ] [ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;墓碑&lt;/span&gt; ] [ B ] [ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;空&lt;/span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 查找 B：需要跳过 3 个墓碑，即使它们是空的，线性探测依然要检查
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;14-线性探测的真香场景&#34;&gt;1.4 线性探测的&amp;quot;真香&amp;quot;场景&lt;/h3&gt;
&lt;p&gt;尽管有上述缺陷，线性探测在工业界依然被广泛使用，原因在于：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
