<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>NotionNext BLOG</title>
        <link>https://tangly1024.com/</link>
        <description>这是一个由NotionNext生成的站点</description>
        <lastBuildDate>Tue, 11 Apr 2023 18:23:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2023, NotionNext</copyright>
        <item>
            <title><![CDATA[电子饭圈的本质是什么]]></title>
            <link>https://tangly1024.com/article/gossip1</link>
            <guid>https://tangly1024.com/article/gossip1</guid>
            <pubDate>Tue, 03 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[互联网上经常看到很多人因为手机、游戏撕逼。甚至到同一个小圈子里，都能因为喜欢的角色不同而破口大骂，我想把他们统称为电子饭圈，不如我们探讨下电子饭圈的本质是什么吧]]></description>
            <content:encoded><![CDATA[<div id="container" class="mx-auto undefined"><main class="notion light-mode notion-page notion-block-2dc1f77ef16c46ec85d79c3b4606508e"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-sync-block notion-block-f231af448b5643a5beb4e135a446bae5"><div class="notion-text notion-block-1ff218e6a1b148f589a0b852ebaac650">如果你是 B 站一位经常刷游戏数码汽车区的网友，会发觉这几个分区充斥着无处不在的撕逼谩骂、互相诋毁、踩一捧一。喜欢 A 品牌手机的人，会不断谩骂 B 品牌和品牌的用户。对于国内几款玩家众多的游戏，完整之间会形成一段非常完成的鄙视链，后来饭圈这词火了，类似的行为就成了所谓的“饭圈”，</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[迁移到事件驱动架构的好处]]></title>
            <link>https://tangly1024.com/article/eventdriven</link>
            <guid>https://tangly1024.com/article/eventdriven</guid>
            <pubDate>Fri, 18 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[之前在搜系统设计的时候，无意看到了 AWS Blog 上的这篇文章，我想着把文章翻译一下，又是学习又是学英语的一种方式吧，本文是翻译 AWS BLOG 上的一篇关于事件驱动的文章]]></description>
            <content:encoded><![CDATA[<div id="container" class="mx-auto undefined"><main class="notion light-mode notion-page notion-block-ce7a44460f07423bade9e685cf43b1c6"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-87211bcd7db0430083b6be35f36aab86"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://aws.amazon.com/cn/blogs/compute/benefits-of-migrating-to-event-driven-architecture/">Benefits of migrating to event-driven architecture | AWS Compute Blog (amazon.com)</a> </div><div class="notion-sync-block notion-block-975806a98d2c4d62a2c15f0e36f5293d"><div class="notion-text notion-block-7e649ca5892b4068975250cf84538900">Two common options when building applications are request-response and event-driven architecture. In request-response architecture, an application’s components communicate via API calls. The client sends a request and expects a response before performing the next task. In event-driven architecture, the client generates an event and can immediately move on to its next task. Different parts of the application then respond to the event as needed.</div><blockquote class="notion-quote notion-block-551df13bf5864668b40ab11ac6517157"><div>两种常见的构建应用程序选项 <b>请求响应</b> 和 <b>事件驱动 </b>架构<b>。</b>在请求响应架构下，应用的组件通过 API 调用通信。客服端发送一个请求，并期望在下一个任务之前得到响应。在事件驱动的架构下，客户端将生成一个事件，并能立即继续执行下一个任务，应用程序的不同部分将根据需要响应事件。</div></blockquote><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-4380831f40a04c4b892ea34de62c81ad"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F9bc2c6e1-57a9-42f0-8dcd-71de6a4de19a%2FUntitled.png?table=block&amp;id=4380831f-40a0-4c4b-892e-a34de62c81ad" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-09fd9187ecad402897bb21ad9a3c1734">In this post, you learn about reasons to consider moving from request-response architecture to an event-driven architecture.</div><blockquote class="notion-quote notion-block-298a93aa591a4c9b928886603003c635"><div>通过这篇文章，你将了解到考虑将请求响应架构迁移到事件驱动架构的原因。</div></blockquote><h3 class="notion-h notion-h2 notion-block-1cfda3e6287b4e6faeaf7912a75d50bb" data-id="1cfda3e6287b4e6faeaf7912a75d50bb"><span><div id="1cfda3e6287b4e6faeaf7912a75d50bb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1cfda3e6287b4e6faeaf7912a75d50bb" title="Challenges with request-response architecture「请求响应架构面临的挑战」"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Challenges with request-response architecture「请求响应架构面临的挑战」</b></span></span></h3><div class="notion-text notion-block-fbbcb38a559e4058af10230b3dfb422c">When starting to a build a new application, many developers default to a request-response architecture. A request-response architecture may tightly integrate components and those components communicate via synchronous calls. While a request-response approach is often easier to get started with, it can become challenging as your application grows in complexity.</div><blockquote class="notion-quote notion-block-fcfc65ed5bfa42c597e6746bdf9159ac"><div>当开始构建一个新的应用，许多的开发者默认使用请求响应架构。请求响应架构可能使得组件耦合紧密，这些组件使用同步调用来通信。通常请求响应架构能更简单地开始，但随着应用变得复杂这个架构可能变得很具有挑战性。</div></blockquote><div class="notion-text notion-block-be90019585d443f3bb764c6cb7f40f33">In this post, I review an example request-response ecommerce application and demonstrate the challenges of tightly coupled integrations. Then I show you how building the same application with an event-driven architecture can give you increased scalability, fault tolerance, and developer velocity.</div><blockquote class="notion-quote notion-block-a00b9daa5ae2423db5557919f30e8213"><div>在这篇文章中，我将回顾一个基于请求响应架构的电子交易应用的案例，并演示紧密耦合接入所带来的挑战。之后，我将向你展示如何通过事件驱动架构构建相同的应用，帮助你提高扩展性、分区容错性和开发速度。</div></blockquote><h4 class="notion-h notion-h3 notion-block-eb978544b9dd47a4b52297ab6c8f10a5" data-id="eb978544b9dd47a4b52297ab6c8f10a5"><span><div id="eb978544b9dd47a4b52297ab6c8f10a5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#eb978544b9dd47a4b52297ab6c8f10a5" title="Close coordination between microservices 「微服务之间协调」"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Close coordination between microservices 「微服务之间协调」</b></span></span></h4><div class="notion-text notion-block-f211dae428ab4f409daddd5e9a84152f">In a typical ecommerce application that uses a synchronous API, the client makes a request to place an order and the order service sends the request downstream to an invoice service. If successful, the order service responds with a success message or confirmation number.</div><blockquote class="notion-quote notion-block-7cb3e19b109948838d7d2e6a2c54e504"><div>在一个使用同步API的经典的商业应用中，客户端通过发送请求去创建一个订单，之后订单服务通过发送请求给下游去调用发票服务。如果成功订单服务将响应一个成功的信息或者是确认数字。</div></blockquote><div class="notion-text notion-block-4979b81e227c4ef290546868d292c9a9">In this initial stage, this is a straightforward connection between the two services. The challenge comes when you add more services that integrate with the order service.</div><blockquote class="notion-quote notion-block-360508e2f1984e0da5b9f698338d173e"><div>在这个初始化阶段，两个服务是直接连接的。如果你将集成更多的服务到订单服务中，你将面临更多的挑战。</div></blockquote><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-409eb74150c84837a28a0d9dcb669d71"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fc6355a67-13cf-4fd6-b4bf-827e00fb9a81%2FUntitled.png?table=block&amp;id=409eb741-50c8-4837-a28a-0d9dcb669d71" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-94f6d8de1117411abe4fcf0cb6ff0df3">If you add a fulfillment service and a forecasting service, the order service has more responsibilities and more complexity. The order service must know how to call each service’s API, from the API call structure to the API’s retry semantics. If there are any backwards incompatible changes to the APIs, the order service team must update them. The system forwards heavy traffic spikes to the order service’s dependency, which may not have the same scaling capabilities. Also, dependent services may transmit errors back up the stack to the client.</div><blockquote class="notion-quote notion-block-81bd3b6225b649568f1039a3a0f1e94c"><div>如果你增加一个履约服务和预约服务，订单服务将承担更多的职责和变的更复杂。订单服务必须知道每一个服务的 API 的调用结构和重试逻辑。系统将转发大量的请求到订单服务所依赖的其他服务，但这些服务可能不会拥有同样的伸缩性。此外依赖的服务可能将错误传输到客户端。</div></blockquote><h4 class="notion-h notion-h3 notion-block-e44684ff61e4482880751ab651fe00fa" data-id="e44684ff61e4482880751ab651fe00fa"><span><div id="e44684ff61e4482880751ab651fe00fa" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e44684ff61e4482880751ab651fe00fa" title="Error handling and retries 「错误处理与重试」"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Error handling and retries 「错误处理与重试」</b></span></span></h4><div class="notion-text notion-block-66999b81f51949c198c68a1acfd77b36">Now, you add new downstream services for fulfillment and shipping orders to the ecommerce application.</div><blockquote class="notion-quote notion-block-846087097a0b4526b6df87ba378b1261"><div>现在，你为这个电子商务应用增加新的下游服务为履约服务和订单发货</div></blockquote><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-92276cdfd05a4070aea7f9072fa096bc"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F02811f39-1297-4f50-a566-1f6f63e05df6%2FUntitled.png?table=block&amp;id=92276cdf-d05a-4070-aea7-f9072fa096bc" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-8c9d9e93fb2741a297feae2cdc613ba0">In the happy path, everything works as expected: The order service triggers invoicing, payment systems, and updates forecasting. Once payment clears, this triggers the fulfillment and packing of the order, and then informs the shipping service to request tracking information.</div><blockquote class="notion-quote notion-block-acf45eff4863487fb3fb5eccee641c1a"><div>如果幸运的话，一切都按照预期工作：订单服务触发发票、支付系统，并更新预测系统。付款完成后，将触发订单履约和包装，之后通知运输服务去请求追踪信息。</div></blockquote><div class="notion-text notion-block-fdd022ca5cca4550983d54eda363a55e">However, if the fulfillment center cannot find the product because they are out of stock, then fulfillment might have to alert the invoice service, then reverse the payment or issue a refund. If fulfillment fails, then the system that triggers shipping might fail as well. Forecasting must also be updated to reflect the change. This remediation workflow is all just to address one of the many potential “unhappy paths” that can occur in this API-driven ecommerce application.</div><blockquote class="notion-quote notion-block-40211b8f85b9426e88a3954ea802f92d"><div>然而，如果履约中心因为超出库存找不到这个商品，履约服务可能必须要通知发票服务，之后去扭转付款或者是发起退款。如果履约失败，那么出发运输的系统可能也一样会失败。预测服务也必须更新以反应这些变化。这个修复工作流仅仅是为了解决 API 驱动的电子商务应用中可能发生的许多潜在的“不幸的事情”之一。</div></blockquote><h4 class="notion-h notion-h3 notion-block-c1628d00953b4ce59d49c61ea388d896" data-id="c1628d00953b4ce59d49c61ea388d896"><span><div id="c1628d00953b4ce59d49c61ea388d896" class="notion-header-anchor"></div><a class="notion-hash-link" href="#c1628d00953b4ce59d49c61ea388d896" title="Close coordination between development teams 「开发团队间的密切协调」"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>Close coordination between development teams 「开发团队间的密切协调」</b></span></span></h4><div class="notion-text notion-block-5bd07bab774941f28c6f643275809bb7">n a synchronously integrated application, teams must coordinate any new services that are added to the application. This can slow down each development team’s ability to release new features. Imagine your team works on the payment service but you weren’t told that another team added a new rewards service. What now happens when the fulfillment service errors?</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[草履虫都能看懂的并查集]]></title>
            <link>https://tangly1024.com/article/unionfind</link>
            <guid>https://tangly1024.com/article/unionfind</guid>
            <pubDate>Thu, 20 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[介绍一下动态连通性问题和并查集，草履虫都能看懂哦]]></description>
            <content:encoded><![CDATA[<div id="container" class="mx-auto undefined"><main class="notion light-mode notion-page notion-block-5e4b9fbf16f94142a4c25b1c71092bad"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-sync-block notion-block-19b560223a27472e892e0d30a0510fe6"><h3 class="notion-h notion-h2 notion-block-7f5709e319c642ddb84600c4b1506703" data-id="7f5709e319c642ddb84600c4b1506703"><span><div id="7f5709e319c642ddb84600c4b1506703" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7f5709e319c642ddb84600c4b1506703" title="动态连通性（Dynamic Connectivity）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">动态连通性<b><b>（Dynamic Connectivity）</b></b></span></span></h3><div class="notion-row"><a target="_blank" rel="noopener noreferrer" class="notion-bookmark notion-block-07608ae26b24428282a1265d1d7aba05" href="https://www.bilibili.com/video/BV1Jt411P77c?p=2"><div><div class="notion-bookmark-title">01_dynamic-connectivity 动态连通性_哔哩哔哩_bilibili</div><div class="notion-bookmark-description">01_dynamic-connectivity 动态连通性是普林斯顿大学丨算法第四版 Princeton University 丨 Algorithms Part 1的第2集视频，该合集共计59集，视频收藏或关注UP主，及时了解更多相关视频内容。</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.bilibili.com/favicon.ico" alt="01_dynamic-connectivity 动态连通性_哔哩哔哩_bilibili" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://www.bilibili.com/video/BV1Jt411P77c?p=2</div></div></div><div class="notion-bookmark-image"><img style="object-fit:cover" src="http://i0.hdslb.com/bfs/archive/57e927b817053b87c1005cfdee8dc9cdccf69310.png" alt="01_dynamic-connectivity 动态连通性_哔哩哔哩_bilibili" loading="lazy" decoding="async"/></div></a></div><div class="notion-text notion-block-50bcca4f47334dc3894d89b5ec35ed66">我们定义有 N 个相互独立的节点，并提供以下两个操作。</div><div class="notion-text notion-block-460e0a396c9b48c29a14efe1ea40cda2"><b>连接：将两个节点进行连接。</b></div><div class="notion-text notion-block-7cb84df0a069440ab8784327ae7a52de"><b>是否连通：判断两个节点是否连通</b></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-193cd010708b40e591b910b2ae1c67d0"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F4dfea2d0-084c-4c7e-be0e-cc6c8e2f1879%2FUntitled.png?table=block&amp;id=193cd010-708b-40e5-91b9-10b2ae1c67d0" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-e85b8758ce394ef09bb93e7ccd684c58">如图，初始状态下，每一个节点都是单独独立的节点。我们先后连接 <code class="notion-inline-code">节点3</code> 和 <code class="notion-inline-code">节点4</code> 、<code class="notion-inline-code">节点3</code> 和 <code class="notion-inline-code">节点8</code>、<code class="notion-inline-code">节点6</code> 和 <code class="notion-inline-code">节点5</code>、<code class="notion-inline-code">节点9</code> 和 <code class="notion-inline-code">节点4</code>、<code class="notion-inline-code">节点2</code> 和 <code class="notion-inline-code">节点1</code>、<code class="notion-inline-code">节点5</code> 和 <code class="notion-inline-code">节点0</code>、<code class="notion-inline-code">节点2</code> 和 <code class="notion-inline-code">节点7</code>、<code class="notion-inline-code">节点6</code> 和 <code class="notion-inline-code">节点1</code>、<code class="notion-inline-code">节点0</code> 和 <code class="notion-inline-code">节点1</code> ，此时我们去判断 <code class="notion-inline-code">节点0</code> 和 <code class="notion-inline-code">节点7</code> 是否连通。在执行完多个节点连接的操作后，去判断节点是否连通的问题就是动态连通性问题。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-5bdfb1da9dbc4d538eb4a0cd37a58788"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F65b112c5-560c-407e-af0c-1956297aef8f%2FUntitled.png?table=block&amp;id=5bdfb1da-9dbc-4d53-8eb4-a0cd37a58788" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-a6db8b0e7d0f4eef84b5fa44f943a1df">我们会运用到什么场景？</div><div class="notion-text notion-block-74f9a7a8a8ad437c8a0ce8b4e98ce84e">两个国家是不是在同一片大陆</div><div class="notion-text notion-block-c95756ac67a947e7a5dc7d37b7fd30a7">社交关系的两个人</div><h3 class="notion-h notion-h2 notion-block-fc45f41c68504d07b27128dd11c27aab" data-id="fc45f41c68504d07b27128dd11c27aab"><span><div id="fc45f41c68504d07b27128dd11c27aab" class="notion-header-anchor"></div><a class="notion-hash-link" href="#fc45f41c68504d07b27128dd11c27aab" title="什么是并查集？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">什么是并查集？</span></span></h3><div class="notion-text notion-block-8529867af53d46a5860d96c6aaac2ba8">并查集就是<b>合并（Union）、查询（Find）</b></div><div class="notion-text notion-block-c30c4562d1504028b62eec6f2b465f01"><b>合并（Union）</b>：把两个不相交的集合合并为一个集合。</div><div class="notion-text notion-block-7977ba192d7c4797b493ddbd621c07eb"><b>查询（Find）</b>：查询两个元素是否在同一个集合中。</div><div class="notion-text notion-block-f1eb761c75c64f2f8326c87d7a1bd037">对于动态连通性问题，我们就可以使用并查集来解决。</div><h3 class="notion-h notion-h2 notion-block-4c18475ad2614067adeb8a880ac235c3" data-id="4c18475ad2614067adeb8a880ac235c3"><span><div id="4c18475ad2614067adeb8a880ac235c3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4c18475ad2614067adeb8a880ac235c3" title="建立模型"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">建立模型</span></span></h3><div class="notion-text notion-block-6755eb921d6b48a8bf916f1d73edfa13">我们使用并查集的思想，去解决动态连通性问题。</div><ol start="1" class="notion-list notion-list-numbered notion-block-d9812ff476e54335866d8962d826b504"><li>我们可以把连通的节点放入到同一个集合中。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-5995f5daac2941a1a36af8729ad8260c"><li>当我门连接两个不连通的节点时，就将这两个节点所在的集合合并成一个集合。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-3968bf3d8a6448eb8eb92094485273ff"><li>当查询两个节点是否连通的时候，只用判断这两个节点是否在一个集合中即可。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-7655d0daa85f44faa2a77135352ae1fa"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fcad83c76-8f9a-46e1-91ad-4fe8d04e9069%2FUntitled.png?table=block&amp;id=7655d0da-a85f-44fa-a2a7-7135352ae1fa" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-744f27ec585a410f8156a93e623dad1d">我们抽象如下的接口，实现这个接口以完成不同并查集实现的算法。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public interface IDynamicConnectivity {
    void union(int p,int q);
    boolean connected(int p,int q);
}</code></pre><div class="notion-blank notion-block-590a40e97e26416da0f9f0cc2ebee3a1"> </div><h3 class="notion-h notion-h2 notion-block-8f9c8e5f09b54c3db2b701756962574e" data-id="8f9c8e5f09b54c3db2b701756962574e"><span><div id="8f9c8e5f09b54c3db2b701756962574e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8f9c8e5f09b54c3db2b701756962574e" title="Quick-Find 快速查询 "><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Quick-Find 快速查询 </span></span></h3><div class="notion-text notion-block-77f1a5f768b6451fb3b65d89a4b7ae59">我们使用一个 <code class="notion-inline-code">int</code> 数组来表示每一个节点所在的集合。如 <code class="notion-inline-code">节点0</code> 所在的集合标记由 <code class="notion-inline-code">id[0]</code> 来存储。由于初始状态下，每一个节点都是一个独立的节点，那么 N 个节点就需要 N 个集合，<code class="notion-inline-code">id</code> 数组的初始化就显而易见了即 <code class="notion-inline-code">id[i] = i</code> 。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public class QuickFind implements IDynamicConnectivity {
	private int[] id;
	public QuickFind(int size) {
    id = new int[size];
    for (int i = 0; i &lt; id.length; i++) {
        id[i] = i;
    }
	}
}</code></pre><blockquote class="notion-quote notion-block-5c87f5da410f4518b7366034b2b69a1c"><div>tips: 其实这里等式的右边半只要不重复皆可，它就是集合的唯一标识，不一定需要等于 <code class="notion-inline-code">i</code>，如果你喜欢 <code class="notion-inline-code">id[i] = i + 114514</code> 都是可以的。</div></blockquote><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-db59ef6cb7214e9e9166f86893944981"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F55f63416-4080-4020-959f-bbb7af2a2aa7%2FUntitled.png?table=block&amp;id=db59ef6c-b721-4e9e-9166-f86893944981" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2c16ea55f9b64fc59717a5a6c55c3736">当我们判断 <code class="notion-inline-code">节点3</code> 和 <code class="notion-inline-code">节点9</code> 是否在一个集合的时候，只需要判断 <code class="notion-inline-code">id[3] == id[9]</code> 即可。那 <code class="notion-inline-code">connected</code> 方法的实现就很简单明了了，而此方法也因此得名 <code class="notion-inline-code">Quick-Find</code> ，因为他查询仅需要 O1 的时间复杂度。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">@Override
public boolean connected(int p, int q) {
    return id[p] == id[q];
}</code></pre><div class="notion-text notion-block-152470de36ad447288b6f19ffb74d49e">那是用这个模型合并两个集合的方法就显而易见了，只需要将需要合并的两个集合的编号 <code class="notion-inline-code">id[node]</code> 设置为其中一个集合的编号 <code class="notion-inline-code">id[node]</code> 即可。现在我们假设连通 <code class="notion-inline-code">p node</code> 和 <code class="notion-inline-code">q node</code> ，他们所对应的集合的编号是 <code class="notion-inline-code">pid</code> 和 <code class="notion-inline-code">qid</code> ，当 <code class="notion-inline-code">id[i] == pid</code> 将 <code class="notion-inline-code">id[i]</code> 设置为 <code class="notion-inline-code">qid</code>  。所以得到<code class="notion-inline-code">union</code> 方法的实现如下。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">@Override
public void union(int p, int q) {
    int pid = id[p];
    int qid = id[q];
    for (int i = 0; i &lt; id.length; i++) {
        if (id[i] == pid) {
            id[i] = qid;
        }
    }
}</code></pre><blockquote class="notion-quote notion-block-6396e9b0803649e2b95db5c5236a006e"><div>tips: 这里很容易犯一个错，那就是 <code class="notion-inline-code">id[i] == pid</code> 这里，有人可能会写成 <code class="notion-inline-code">id[i] == id[p]</code> ，这显然是错误的。因为过程中会修改到 <code class="notion-inline-code">id[p] = qid</code> 那么 <code class="notion-inline-code">id[p]</code> 之后的节点就变成判断 <code class="notion-inline-code">id[i] == qid</code>。</div></blockquote><h4 class="notion-h notion-h3 notion-block-e62d86e49729406c9aa27de8d7b5b462" data-id="e62d86e49729406c9aa27de8d7b5b462"><span><div id="e62d86e49729406c9aa27de8d7b5b462" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e62d86e49729406c9aa27de8d7b5b462" title="复杂度分析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">复杂度分析</span></span></h4><div class="notion-text notion-block-8eb3d4e611d745f2adbcb48a13014068">空间复杂度：由于需要 N 个空间存储每个节点所在的集合，所以空间复杂度就是 On</div><div class="notion-text notion-block-db39ed6afe7a4de7ae8e28424472557a">时间复杂度分析如下</div><table class="notion-simple-table notion-block-7dc1b096997d4c31a4b037cb2f59cbdc"><tbody><tr class="notion-simple-table-row notion-block-d8f868e5ac5943adba9f98d6662b5e41"><td class="" style="width:120px"><div class="notion-simple-table-cell">Init</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Union</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Find</div></td></tr><tr class="notion-simple-table-row notion-block-40e0101620f144e09011464fc7254a68"><td class="" style="width:120px"><div class="notion-simple-table-cell">N</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">N</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1</div></td></tr></tbody></table><div class="notion-text notion-block-82b2cd7424cd49b39cc6b37c83a73034">Init：初始化数组需要进行遍历时间复杂度为 On</div><div class="notion-text notion-block-ff9888edf8264624ac497dff5cfded22">Union：合并集合也需要遍历所以时间复杂度为 On</div><div class="notion-text notion-block-40cecfeba8404c368efb08387baa9e5f">Find: 查询操作通过数组下标直接对比得出，时间复杂度为 O1</div><h4 class="notion-h notion-h3 notion-block-b41f7a03a7a64f70a186498f3b17b827" data-id="b41f7a03a7a64f70a186498f3b17b827"><span><div id="b41f7a03a7a64f70a186498f3b17b827" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b41f7a03a7a64f70a186498f3b17b827" title="总结"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">总结</span></span></h4><div class="notion-text notion-block-f562ac58c52242c6ba7c36ebf53ed4a8"><code class="notion-inline-code">Quick-Find</code> 实现顾名思义，查询很快，他能在 O1 的时间复杂度中获取到结果，但所带来的代价就是 <code class="notion-inline-code">Union</code> 操作的时间复杂度很慢，当我们数组元素非常多时 N 会线性增长。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public class QuickFind implements IDynamicConnectivity{
    private int[] id;
    public QuickFind(int size) {
        id = new int[size];
        for (int i = 0; i &lt; id.length; i++) {
            id[i] = i;
        }
    }

    @Override
		// On的查找时间
		public void union(int p, int q) {
        int pid = id[p];
        int qid = id[q];
        for (int i = 0; i &lt; id.length; i++) {
            if (id[i] == pid) {
                id[i] = qid;
            }
        }
    }

    @Override
		// O1的比较时间
		public boolean connected(int p, int q) {
        return id[p] == id[q];
    }
}
</code></pre><div class="notion-blank notion-block-229c50ee6f274196ae2730dedf062589"> </div><h3 class="notion-h notion-h2 notion-block-082a0296fca447e09805803641b76755" data-id="082a0296fca447e09805803641b76755"><span><div id="082a0296fca447e09805803641b76755" class="notion-header-anchor"></div><a class="notion-hash-link" href="#082a0296fca447e09805803641b76755" title=" Quick-Union"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"> Quick-Union</span></span></h3><div class="notion-text notion-block-f6d6f736d24f4f50aa0cec50e5616cff">刚刚我们了解完了 <code class="notion-inline-code">Quick-Find</code> 实现，如果我们面对的是一个 <code class="notion-inline-code">Union</code> 操作频繁的场景，那么 <code class="notion-inline-code">Quick-Find</code> 就无法体现出他的优势，我们需要更新我们的模型，以提高 <code class="notion-inline-code">Union</code> 操作的效率。我们分析在 <code class="notion-inline-code">Quick-Find</code> 中 <code class="notion-inline-code">Union</code> 的实现。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">@Override
public void union(int p, int q) {
    int pid = id[p];
    int qid = id[q];
    for (int i = 0; i &lt; id.length; i++) {
        if (id[i] == pid) {
            id[i] = qid;
        }
    }
}</code></pre><div class="notion-text notion-block-e7ab7b0ca074490d8eae72f480528250">可以发现，当我们仅仅连通两个节点的时候，他需要将某个节点所在集合中的所有元素都进行更新，这就是造成需要遍历的核心原因。</div><div class="notion-text notion-block-d2ae1e97af994fe3ab8a621ce70a7a74">我们需要提出一个更高效的关系结构，以达到更新更少的节点。除去线性表这种存储结构，我们还能容易想到<b>树</b>这个更紧凑的数据结构。</div><h4 class="notion-h notion-h3 notion-block-e16f64540c504da7862647a9aa6a3f33" data-id="e16f64540c504da7862647a9aa6a3f33"><span><div id="e16f64540c504da7862647a9aa6a3f33" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e16f64540c504da7862647a9aa6a3f33" title="把他们挂在树上"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">把他们挂在树上</span></span></h4><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-e871760c70844cdb89e7e804d600be23"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:384px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fef63592d-c91d-4957-a4f5-ee39cf0e460a%2FUntitled.png?table=block&amp;id=e871760c-7084-4cdb-89e7-e804d600be23" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-e26c6f1808064f508a57e26338854bdf">Find：当连通的节点都存储在一个树上，那么判断他们是否连通只需要判断他们的根节点是否为同一个。例如我们要判断 <code class="notion-inline-code">节点2</code> 和 <code class="notion-inline-code">节点3</code> 是否连通，只需要找到他们的根节点也就是 <code class="notion-inline-code">节点9</code> ，因此可以判断他们连通。</div><div class="notion-text notion-block-09155323f444477a946053f0c9ee4a91">Union：当我们要连通两个节点的时候，需要找寻到他们的根节点，并让某一个根节点成为另一个根节点的叶子结点。如将 <code class="notion-inline-code">节点3</code> 和 <code class="notion-inline-code">节点5</code> 连通，我们就需要把根节点为<code class="notion-inline-code">节点9</code> 和 根节点为<code class="notion-inline-code">节点6</code> 的两个树进行合并，合并成如下图所示的样子。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-d7756c113039459b99800f971829f2f2"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:384px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fce931e38-af5f-4933-b100-2e8d53378aba%2FUntitled.png?table=block&amp;id=d7756c11-3039-459b-9980-0f971829f2f2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-block-6410a85f7301425090f2d6b08273a467" data-id="6410a85f7301425090f2d6b08273a467"><span><div id="6410a85f7301425090f2d6b08273a467" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6410a85f7301425090f2d6b08273a467" title="把树捋顺了！！"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">把树捋顺了！！</span></span></h4><div class="notion-text notion-block-fe6d46318aca44c2a39bec99573e5968">我们怎么存储这个树结构也是一个学问，你或许写过如下所示的二叉树。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public class TreeNode {
		int value;
		TreeNode left;
		TreeNode right;
}</code></pre><div class="notion-text notion-block-59d8b4c3e35c43468825b7de72035d3a">但我们或许可以换个思路，这种数据结构是由父节点查找到子节点，自顶向下的。而我们要连通的两个节点，可能是两个叶子结点，这种数据结构我们无法向上查找。所以需要用子节点查询父节点的数据结构。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public class TreeNode {
		int value;
		TreeNode father;
}</code></pre><div class="notion-text notion-block-5df0656e2ff646379a901d86476ad56c">这样我们就可以查询到该节点的父节点了。如果这个节点的父节点就是他自己，则说明他是这个树的根节点。</div><div class="notion-text notion-block-c81d7e5bf8974906abd587b6c954ecb8">而最后我们可以通过链表模拟树的方式，来简化这个树的存储。依然是用一个 <code class="notion-inline-code">int[] id</code> 数组，我们使用 <code class="notion-inline-code">id 数组</code> 存储每个节点的父节点，也就是 <code class="notion-inline-code">节点i</code> 的父节点为 <code class="notion-inline-code">id[i]</code> 。如何在这个数组中获取到 <code class="notion-inline-code">节点i</code> 的 <code class="notion-inline-code">根节点</code> 呢？一直通过这个节点向上查询，直到某个节点的父节点是他本身，那么这个节点就是 <code class="notion-inline-code">节点i</code> 的 <code class="notion-inline-code">根节点</code> ，即 <code class="notion-inline-code">id[id[…id[i]…]]</code> 就是 <code class="notion-inline-code">节点i</code> 的 <code class="notion-inline-code">根节点</code> 。下图展示的就是这些节点集合在 <code class="notion-inline-code">id数组</code> 中的表示方式。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-cf0260b8a0ea4a36bee01ebf3f2f8299"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:384px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fce931e38-af5f-4933-b100-2e8d53378aba%2FUntitled.png?table=block&amp;id=cf0260b8-a0ea-4a36-bee0-1ebf3f2f8299" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-51ced5d54aa24a0fb24cb0628ba29e0a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:384px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fd068a246-d016-42f4-91fe-34dd6d296a1e%2FUntitled.png?table=block&amp;id=51ced5d5-4aa2-4a0f-b24c-b0628ba29e0a" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-81e1df017ba545ae8ed8e5da6d8c7fe8">初始化，由于每个节点最开始的时候，都是一个单独的集合，所以每个节点都是根节点，因此需要让 <code class="notion-inline-code">id[i] = i</code> ，则可以让每一个节点的父节点是自己了。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public QuickUnion(int size) {
    id = new int[size];
    for (int i = 0; i &lt; id.length; i++) {
        id[i] = i;
    }
}</code></pre><blockquote class="notion-quote notion-block-2ac7510bbda54a5c897fea34d96837e1"><div>tips: 与 Quick-Find 不同，这里 <code class="notion-inline-code">id[i]</code> 不再是单纯的标记，他需要承接节点的关系。</div></blockquote><div class="notion-text notion-block-3c7dc530382a4996a45aa83a789bd74b">我们再复习一遍在树上的 Union &amp; Find</div><ul class="notion-list notion-list-disc notion-block-8a3fd0c3c61f4b958b15f80329fb67d3"><li>Find：当连通的节点都存储在一个树上，那么判断他们是否连通只需要判断他们的<b>根节点</b>是否为同一个。</li></ul><ul class="notion-list notion-list-disc notion-block-3215615ada7347dc91ab216f0ee902b2"><li>Union：当我们要连通两个节点的时候，需要找寻到他们的<b>根节点</b>，并让某一个根节点成为另一个根节点的叶子结点。</li></ul><div class="notion-text notion-block-6579c78b44164af0a5f73b483a8df444">是的，Find &amp; Union 都需要有一个找寻梗节点的方法所以我们先实现找寻根节点的方法，我们只需要不断找寻某个节点的父节点，并继续找寻父节点的父节点直到父节点是他本身。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">private int findRoot(int p) {
    while (p != id[p]) {
        p = id[p];
    }
    return p;
}</code></pre><div class="notion-text notion-block-6d411d83df1a474ab24da1f339ca98d0">Find：我们只需要找寻到 <code class="notion-inline-code">节点q</code> 的根节点 和 <code class="notion-inline-code">节点p</code> 的根节点，并比较他们是否是同一个节点即可。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">@Override
public boolean connected(int p, int q) {
    return findRoot(p) == findRoot(q);
}</code></pre><div class="notion-text notion-block-68688141e9bd483abe51af9c53e377c0">Union：我们找寻到 <code class="notion-inline-code">节点q</code> 的根节点 和 <code class="notion-inline-code">节点p</code> 的根节点，并放 <code class="notion-inline-code">节点p</code> 的根节点的父节点设置为 <code class="notion-inline-code">节点q</code> 的根节点即可。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">@Override
public void union(int p, int q) {
    int proot = findRoot(p);
    int qroot = findRoot(q);
    id[proot] = qroot;
}
</code></pre><h4 class="notion-h notion-h3 notion-block-f7fb073e604143c8ae212229593fcb37" data-id="f7fb073e604143c8ae212229593fcb37"><span><div id="f7fb073e604143c8ae212229593fcb37" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f7fb073e604143c8ae212229593fcb37" title="复杂度分析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">复杂度分析</span></span></h4><div class="notion-text notion-block-6684e77cfd8e44b2a5a9d054556bf815">空间复杂度：由于需要 N 个空间存储每个节点所在的集合，所以空间复杂度就是 On</div><div class="notion-text notion-block-df5d544ff0154f56bb58847d7405c510">时间复杂度分析如下</div><table class="notion-simple-table notion-block-9941921dfdc24b308d9f0b8504c4329f"><tbody><tr class="notion-simple-table-row notion-block-347a0cca86dd4b79ad7d702eaba0f18e"><td class="" style="width:120px"><div class="notion-simple-table-cell">Init</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Union</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Find</div></td></tr><tr class="notion-simple-table-row notion-block-dd4d216f4d1243f7ac4568290ec21278"><td class="" style="width:120px"><div class="notion-simple-table-cell">N</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">最快O1 最差On</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">最快O1 最差On</div></td></tr></tbody></table><div class="notion-text notion-block-adeaa5594cd8451ebab74573e78b9c66">Init：初始化的时候需要遍历一遍数组，所以时间复杂度是 On</div><div class="notion-text notion-block-55da2e3bea7e4ec1bf75e9d1c53eee70">Union &amp; Find：从代码可知，这两个方法的实现都关联到 <code class="notion-inline-code">findRoot</code> 方法的时间复杂度。而<code class="notion-inline-code">findRoot</code> 方法时间复杂度是不稳定的。他的时间复杂度，与节点距离根节点的高度有关。当每个节点都是根节点的时候，<code class="notion-inline-code">findRoot</code> 方法的时间复杂度就是 O1，当所有节点都在同一棵树上，并且这个树退化成了链表，那么此时时间复杂度就是 On。</div><blockquote class="notion-quote notion-block-3d8f4a42cba642d184a4250cdfb62ccc"><div>On 是怎么诞生的？我们现在有 5 个节点，那么当我们<code class="notion-inline-code">union(0,1)</code>的时候他会形成一个2层的树，但我们继续 <code class="notion-inline-code">union(0,2)</code> 的时候就会形成一个3层的树，依次类推，N 个节点最终会形成一个 N 层的树，此时树退化成了链表。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-24f937c9e3a34ee485bf91bd5f2a9b8b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:672px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Feef2fa73-a1d0-44aa-98dd-0295ed771869%2FUntitled.png?table=block&amp;id=24f937c9-e3a3-4ee4-85bf-91bd5f2a9b8b" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-row notion-block-a030c5a724eb4df9b0b01a17480285d9"><div class="notion-column notion-block-edf15195e8994c9bbab0db2325b60958" style="width:calc((100% - (2 * min(32px, 4vw))) * 0.3333333333333333)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-b71261fb054542fe84bf4477b2e8d422"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F128f7b53-78bb-4966-94ac-fa4fc7ffd310%2FUntitled.png?table=block&amp;id=b71261fb-0545-42fe-84bf-4477b2e8d422" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-324a0f27a4094eccbf5dcf21a1f28ee5" style="width:calc((100% - (2 * min(32px, 4vw))) * 0.3333333333333333)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-db40b7c0500f4469af4f8f43a5cf126c"><div style="position:relative;display:flex;justify-content:center;align-self:end;width:288px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Ffd116e4b-4175-491c-9efa-0bdc5b05253c%2FUntitled.png?table=block&amp;id=db40b7c0-500f-4469-af4f-8f43a5cf126c" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-0c898d5128614048b95fbc715070a680" style="width:calc((100% - (2 * min(32px, 4vw))) * 0.3333333333333334)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-be965d74a3204dec9ff160ccc3343a72"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:192px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F39f344e8-1154-4ad2-b288-f2d628fd4693%2FUntitled.png?table=block&amp;id=be965d74-a320-4dec-9ff1-60ccc3343a72" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><div class="notion-blank notion-block-46b75dc067a54c028f685a749902b766"> </div></blockquote><div class="notion-blank notion-block-517ee84235764bbebe790a44286afb14"> </div><h4 class="notion-h notion-h3 notion-block-3cd1be92bca34cd18fd8eb5e6166d24b" data-id="3cd1be92bca34cd18fd8eb5e6166d24b"><span><div id="3cd1be92bca34cd18fd8eb5e6166d24b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3cd1be92bca34cd18fd8eb5e6166d24b" title="总结"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">总结</span></span></h4><div class="notion-text notion-block-4d17f8028da34d3cb4060a7396d33a38"><code class="notion-inline-code">Quick-Union</code> 实现是为了能提高我们合并两个集合的效率，把节点存储在树结构上，可以使得查询只需要访问一条路径上的节点，减少了访问无关节点的访问。虽然 <code class="notion-inline-code">Union &amp; Find</code> 的时间复杂度都是最差 On，看起来好像没有 <code class="notion-inline-code">Quick-Find</code> 实现来的稳定高效，但如果是出现在一个 <code class="notion-inline-code">Union</code> 操作频繁的场景，他带来的收益是客观的。虽然我们提出了一个不稳定的实现，但我们得到了个更高效的模型。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public class QuickUnion implements IDynamicConnectivity{

		private int[] id;
		public QuickUnion(int size) {
		    id = new int[size];
		    for (int i = 0; i &lt; id.length; i++) {
		        id[i] = i;
		    }
		}
		
		@Override
		public void union(int p, int q) {
		    int proot = findRoot(p);
		    int qroot = findRoot(q);
		    id[proot] = qroot;
		}
		
		@Override
		public boolean connected(int p, int q) {
		    return findRoot(p) == findRoot(q);
		}
		
		// 最坏 On 的查找时间
		private int findRoot(int p) {
		    while (p != id[p]) {
				    p = id[p];
		    }
		    return p;
    }
}</code></pre><h3 class="notion-h notion-h2 notion-block-51c11d12e09e41db8011fd19547ed42a" data-id="51c11d12e09e41db8011fd19547ed42a"><span><div id="51c11d12e09e41db8011fd19547ed42a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#51c11d12e09e41db8011fd19547ed42a" title="Weighted Quick-Union"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Weighted Quick-Union</span></span></h3><div class="notion-text notion-block-6a78c74caf274da59221d3aa1ae256a8">在刚刚我们一起实现完 <code class="notion-inline-code">Quick-Union</code> 后，得到了一个不稳定的最差时间复杂度为 On 的 <code class="notion-inline-code">Union &amp; Find</code> 实现，我们知道影响这个时间复杂度的核心原因是树的层数，很快我们就能想到优化数的层数，以达到优化找寻到根节点的时间复杂度。</div><h4 class="notion-h notion-h3 notion-block-041d1b8ceaa04b34abaa523bd9ea7579" data-id="041d1b8ceaa04b34abaa523bd9ea7579"><span><div id="041d1b8ceaa04b34abaa523bd9ea7579" class="notion-header-anchor"></div><a class="notion-hash-link" href="#041d1b8ceaa04b34abaa523bd9ea7579" title="干掉On"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">干掉On</span></span></h4><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-63cb56993467457186b2da7205e4fa32"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:192px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F39f344e8-1154-4ad2-b288-f2d628fd4693%2FUntitled.png?table=block&amp;id=63cb5699-3467-4571-86b2-da7205e4fa32" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-abbd90d56b484a35b67825297ab2e209">在 <code class="notion-inline-code">Quick-Union</code> 的形成 On 的原因其实很好理解，聪明的你也肯定想到了解决的办法。那就是反过。让那个独立的树合并进大的树。这样即使合并完所有的节点，树的层数也是2层，当我们只有 5 个节点的时候不明显，但是如果 N = 1000000 呢？原先的 <code class="notion-inline-code">Quick-Union</code> 需要 1000000 次向上查找才能查找到根节点，而这种方式，只需要 2 次，优化效果显著。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0e9fc50e0fe94971b281eb0f13657a20"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:624px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F6c98693d-82e2-46d8-becb-a41816830fc4%2FUntitled.png?table=block&amp;id=0e9fc50e-0fe9-4971-b281-eb0f13657a20" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-block-a7c77ab32cda4341a094c827ca9a3593" data-id="a7c77ab32cda4341a094c827ca9a3593"><span><div id="a7c77ab32cda4341a094c827ca9a3593" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a7c77ab32cda4341a094c827ca9a3593" title="枝繁叶茂"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">枝繁叶茂</span></span></h4><div class="notion-text notion-block-209c13bd60e348198e4413b329fab778">利用刚刚优化的思想，我们很容易相当到，合并两个树的时候，当哪个树拥有的节点数量更多的时候，让他成为另一个的节点的父节点。因此我们需要一个结构存储这个根节点包含的节点数量，我们使用一个 <code class="notion-inline-code">int[] sz</code> 数组存储每个节点包含的节点数量。当我们合并两个节点，如 <code class="notion-inline-code">节点big </code>和 <code class="notion-inline-code">节点small</code> 的时候，<code class="notion-inline-code">sz[big] = sz[big] + sz[small]</code> 。</div><div class="notion-text notion-block-01db9fb1a9814ae8b76050d8c2426ea9">Init：由于加入了新的 <code class="notion-inline-code">sz</code> 数组我们的初始化也要初始化 <code class="notion-inline-code">sz</code> 数组，初始状态下，每个节点都是独立的根节点，每个节点只包含他一个节点。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public class WeightedQuickUnion implements IDynamicConnectivity {
    private int[] id;
    private int[] sz;
    public WeightedQuickUnion(int size) {
        id = new int[size];
        sz = new int[size];
        for (int i = 0; i &lt; id.length; i++) {
            id[i] = i;
            sz[i] = 1;
        }
    }
}</code></pre><div class="notion-text notion-block-1fa74120b8a4436295097e5446dd7fea">Union：当合并两个节点的时候，需要比较他们的所包含的节点数量，让小根节点成为大根节点的叶子节点。并更新大根节点所包含的节点数量。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">@Override
public void union(int p, int q) {
    int proot = findRoot(p);
    int qroot = findRoot(q);
    if (sz[proot] &lt; sz[qroot]) {
        id[proot] = qroot;
        sz[qroot] += sz[proot];
    } else {
        id[qroot] = proot;
        sz[proot] += sz[qroot];
    }
}</code></pre><div class="notion-text notion-block-e777548c7c56486aa77746438428d996">Find &amp; findRoot： 没有变化。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">@Override
public boolean connected(int p, int q) {
    return findRoot(p) == findRoot(q);
}

private int findRoot(int p) {
    while (p != id[p]) {
		    p = id[p];
    }
    return p;
}</code></pre><h4 class="notion-h notion-h3 notion-block-83524db60d7d4340b529b62d4c9d1044" data-id="83524db60d7d4340b529b62d4c9d1044"><span><div id="83524db60d7d4340b529b62d4c9d1044" class="notion-header-anchor"></div><a class="notion-hash-link" href="#83524db60d7d4340b529b62d4c9d1044" title="复杂度分析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">复杂度分析</span></span></h4><div class="notion-text notion-block-0b3eccf0636249ff90e457245651530b">空间复杂度：由于需要 N 个空间存储每个节点所在的集合，所以空间复杂度就是 On</div><div class="notion-text notion-block-66bf46db5d3a4d99bba880eb9866feae">时间复杂度分析如下</div><table class="notion-simple-table notion-block-d06c3297f1b3482cb28b8482c11e8356"><tbody><tr class="notion-simple-table-row notion-block-eda518ee554544bbad16787761620516"><td class="" style="width:120px"><div class="notion-simple-table-cell">Init</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Union</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Find</div></td></tr><tr class="notion-simple-table-row notion-block-d5ac849964114d0dace74fd6114ca7d0"><td class="" style="width:120px"><div class="notion-simple-table-cell">N</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">最快O1 最差OlgN</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">最快O1 最差OlgN</div></td></tr></tbody></table><div class="notion-text notion-block-66d1b3a96a8e448db088b36906c0d7b2">Init：初始化的时候需要遍历一遍数组，所以时间复杂度是 On</div><div class="notion-text notion-block-a4d7fd9187854edd95f5423a8ecb0ed2">Find &amp; Union：和 <code class="notion-inline-code">Quick-Union</code> 一样，<code class="notion-inline-code">Find &amp; Union</code> 的时间复杂是由 <code class="notion-inline-code">findRoot</code> 方法决定的，确切来说是由树的层数决定的。在我们优化后的 <code class="notion-inline-code">Union</code> 实现，他只会在一种情况下出现树的层数变大，那就是两个根节点的层数相同，小根节点成为大根节点叶子节点，而导致大根节点的层数增加。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-41fdedf7334846ab9a94309c4fa23cad"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:700px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Faa065be9-7349-4b4b-88be-95670f86e70c%2FUntitled.png?table=block&amp;id=41fdedf7-3348-46ab-9a94-309c4fa23cad" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-20ac7f93c4a54cffadf777147b82854b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:532px"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fd66f902d-89f6-4110-89f5-e615c132bf79%2FUntitled.png?table=block&amp;id=20ac7f93-c4a5-4cff-adf7-77147b82854b" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-fa48ea7ac20844178d830fe72fc20cf5">那么节点的层数怎么计算？我们不难发现，我们可以粗浅的认为，但出现节点层数增加的时候，大根节点所包含的节点数量是翻倍的。我们假设有 N 个节点，我们通过 <code class="notion-inline-code">Union</code> 操作使得所有的节点合并成了一个树，那么他最多发生 x 次翻倍，也就是 <code class="notion-inline-code">2^x = N</code>，得到 <code class="notion-inline-code">x = lgN</code> ，而翻倍一次层数+1，可以粗浅的认为这棵树最多有 <code class="notion-inline-code">x = lgN</code> 层。所以 <code class="notion-inline-code">findRoot</code> 的时间复杂度为 lgN。</div><h4 class="notion-h notion-h3 notion-block-f3911be320234a398341f7b4d013ca70" data-id="f3911be320234a398341f7b4d013ca70"><span><div id="f3911be320234a398341f7b4d013ca70" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f3911be320234a398341f7b4d013ca70" title="路径压缩"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">路径压缩</span></span></h4><div class="notion-text notion-block-5e3603a083704fddb005bb6d15914dde">我们仔细想想，其实对于同一个根下的节点，他们的关系都是一样的，就是他们都在一个集合中，只要他最终的根节点还是那个根节点就行。至于他的父节点？只要是这个树下的任何一个节点都行。那我们其实在 <code class="notion-inline-code">findRoot</code> 的过程就可以想到，把这条路径进行压缩，以减小树的层数。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-71b6c38197c5415ea280fa3d8f7d8922"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:532px"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fd66f902d-89f6-4110-89f5-e615c132bf79%2FUntitled.png?table=block&amp;id=71b6c381-97c5-415e-a280-fa3d8f7d8922" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-6bca73c413d94d24b61de6af5f58719d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F0c9c649a-7397-4861-bbb1-2937131c7de6%2FUntitled.png?table=block&amp;id=6bca73c4-13d9-4d24-b61d-e6af5f58719d" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1c0fb2fb78eb482d9079fec8423e8ae4">而压缩的办法呢？那就是更新我们所遇到的节点的父节点为祖父节点。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">private int findRoot(int p) {
    while (p != id[p]) {
				//路径压缩，节点的父节点，变成祖父节点相当于减少了一层。
				id[p] = id[id[p]];
        p = id[p];
    }
    return p;
}</code></pre><div class="notion-text notion-block-13a10907bae9424cad34b71aed03f941">如经过路径压缩，我们使得节点的层数减半。那么最后层数是lg（lg N）也就是lg*N。</div><h4 class="notion-h notion-h3 notion-block-2b8b0fc5655d4479b59627c4b7c19e83" data-id="2b8b0fc5655d4479b59627c4b7c19e83"><span><div id="2b8b0fc5655d4479b59627c4b7c19e83" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2b8b0fc5655d4479b59627c4b7c19e83" title="总结"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">总结</span></span></h4><div class="notion-text notion-block-fa6db1eb079f46868c86008182472304">我们最后通过加权和路径实现枝繁叶茂，而压缩了层数，提高了 <code class="notion-inline-code">findRoot</code> 的效率，得到了一个高效的并查集算法。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-java">public class WeightedQuickUnion implements IDynamicConnectivity{

    private int[] id;
    private int[] sz;
    public WeightedQuickUnion(int size) {
        id = new int[size];
        sz = new int[size];
        for (int i = 0; i &lt; id.length; i++) {
            id[i] = i;
            sz[i] = 1;
        }
    }

		//每次合并选择深度最小的那个节点合并
		//什么情况下树的深度会加1？当两个节点节点数量一样大的时候深度就会+1
    //此时节点数量翻倍，所以我们假设一个节点最多可以翻x倍
		//得到 2^x = N所以树最大深度是 lgN
		@Override
    public void union(int p, int q) {
        int proot = findRoot(p);
        int qroot = findRoot(q);
        if (sz[proot] &lt; sz[qroot]) {
            id[proot] = qroot;
            sz[qroot] += sz[proot];
        } else {
            id[qroot] = proot;
            sz[proot] += sz[qroot];
        }
    }

    @Override
    public boolean connected(int p, int q) {
        return findRoot(p) == findRoot(q);
    }

		//由于 union保证了树深 lgN所以 findRoot最差情况是 lgN
		private int findRoot(int p) {
        while (p != id[p]) {
						//路径压缩，节点的父节点，变成祖父节点相当于减少了一层。
						id[p] = id[id[p]];
            p = id[p];
        }
        return p;
    }
}
</code></pre><div class="notion-blank notion-block-b09a83ec826a46f4945ec8c5a5c60ade"> </div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Oh My ZSH 配置]]></title>
            <link>https://tangly1024.com/article/ohmyzsh</link>
            <guid>https://tangly1024.com/article/ohmyzsh</guid>
            <pubDate>Thu, 04 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[在 MacBook 上配置 Oh My ZSH 以达到优化 terminal 的效果]]></description>
            <content:encoded><![CDATA[<div id="container" class="mx-auto undefined"><main class="notion light-mode notion-page notion-block-e10fc6563d794c0dbc96946fa5692a1c"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-sync-block notion-block-c06577d89f494277b5ea062fca072563"><h3 class="notion-h notion-h2 notion-block-94059c44cab6452e8bebad5abebf62aa" data-id="94059c44cab6452e8bebad5abebf62aa"><span><div id="94059c44cab6452e8bebad5abebf62aa" class="notion-header-anchor"></div><a class="notion-hash-link" href="#94059c44cab6452e8bebad5abebf62aa" title="download"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">download</span></span></h3><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-bash">sh -c &quot;$(curl -fsh -c &quot;$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)&quot;sSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)&quot;

chsh -s /bin/zsh</code></pre><div class="notion-text notion-block-500aef1cf2054ff1ad4a24e3d30538a5">重启 zsh</div><h3 class="notion-h notion-h2 notion-block-0c81088fadac4319a13e99997355c24d" data-id="0c81088fadac4319a13e99997355c24d"><span><div id="0c81088fadac4319a13e99997355c24d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0c81088fadac4319a13e99997355c24d" title="Plugins"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Plugins</span></span></h3><h4 class="notion-h notion-h3 notion-block-ff43f035a1774398a4d29300f4533819" data-id="ff43f035a1774398a4d29300f4533819"><span><div id="ff43f035a1774398a4d29300f4533819" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ff43f035a1774398a4d29300f4533819" title="语法高亮 zsh-syntax-highlighting"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">语法高亮 zsh-syntax-highlighting</span></span></h4><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-bash">git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.oh-my-zsh/plugins/zsh-syntax-highlighting</code></pre><h4 class="notion-h notion-h3 notion-block-217cc4a8567e40ee9ba9f7f032e9f576" data-id="217cc4a8567e40ee9ba9f7f032e9f576"><span><div id="217cc4a8567e40ee9ba9f7f032e9f576" class="notion-header-anchor"></div><a class="notion-hash-link" href="#217cc4a8567e40ee9ba9f7f032e9f576" title="自动补全 zsh-autosuggestions"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">自动补全 zsh-autosuggestions</span></span></h4><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-bash">git clone https://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/plugins/zsh-autosuggestions</code></pre><div class="notion-blank notion-block-367ec3506540446bb4c820db18de4a36"> </div><h3 class="notion-h notion-h2 notion-block-e1acfcab009f436d8c5000a8776ba831" data-id="e1acfcab009f436d8c5000a8776ba831"><span><div id="e1acfcab009f436d8c5000a8776ba831" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e1acfcab009f436d8c5000a8776ba831" title="Plugins Config"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Plugins Config</span></span></h3><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-bash">vim ~/.zshrc</code></pre><div class="notion-text notion-block-d0738966130f468e967265413146cc55">找到 <code class="notion-inline-code">plugins=()</code> 在 () 填入要生效的插件名称，生效多个插件中间使用空格隔开</div><div class="notion-text notion-block-caf3bb9314c24e8c93bec94288f72460">ep. 生效 <code class="notion-inline-code">zsh-syntax-highlighting</code> 和 <code class="notion-inline-code">zsh-autosuggestions</code> 。</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-bash">plugins=(zsh-syntax-highlighting zsh-autosuggestions)</code></pre><div class="notion-text notion-block-f7136d51fdf64cb092d9caed6f98d6cf">当修改 config 后执行以下操作生效 config</div><pre class="notion-code"><div class="notion-code-copy"><div class="notion-code-copy-button"><svg fill="currentColor" viewBox="0 0 16 16" width="1em" version="1.1"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg></div></div><code class="language-bash">source ~/.zshrc</code></pre></div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>