3yx这个网站做刷单广州网络营销推广
2026/6/11 4:01:24 网站建设 项目流程
3yx这个网站做刷单,广州网络营销推广,短视频营销案例分析,网址转换成二维码这篇文章记录的是自己刷 LeetCode 148「Sort List」时的思考过程#xff1a; 从一开始想用冒泡排序交换链表节点位置#xff0c;到最后理解并实现 O(n log n)、O(1) 额外空间的归并排序链表版本。 题目链接#xff1a;LeetCode 148. Sort List。leetcode​ 题目概述 给你一…这篇文章记录的是自己刷 LeetCode 148「Sort List」时的思考过程从一开始想用冒泡排序交换链表节点位置到最后理解并实现 O(n log n)、O(1) 额外空间的归并排序链表版本。题目链接LeetCode 148. Sort List。leetcode​题目概述给你一个单链表的头结点 head请将其按升序排序并返回排序后的链表头结点。链表长度范围为 [0, 5 * 10^4]节点值在 [-10^5, 10^5] 之间。进阶要求是时间复杂度 O(n log n)额外空间复杂度 O(1)。leetcode​初始想法冒泡排序 交换链表节点最直观的想法是冒泡排序不停比较相邻两个节点如果前一个比后一个大就交换它们在链表中的位置。对于链表可以通过调整指针而不是交换值来实现交换节点。csdn1​这种做法可以工作但有两个明显问题时间复杂度是 O(n²)。每一趟需要从头遍历链表整体重复多次不满足题目进阶要求的 O(n log n)。geeksforgeeks1​在数据量接近 5 * 10⁴ 时O(n²) 很容易超时。所以这道题推荐的做法不是冒泡而是归并排序链表。为什么链表适合用归并排序数组排序时常见的高效算法有快速排序、堆排序、归并排序等但在链表上归并排序有天然优势baeldung1​快速排序依赖随机访问按下标访问做分区在链表上实现既不方便又不够高效。stackoverflow​堆排序需要数组形式的堆结构也同样依赖下标。归并排序只需顺序遍历与指针操作拆分通过快慢指针把链表一分为二。合并按值大小像合并两个有序链表一样把两个有序段合起来。归并排序对长度为 n 的链表只会遍历 O(log n) 层每层合并代价 O(n)总时间复杂度 O(n log n)。如果使用迭代自底向上的写法额外空间还可以做到 O(1)。题解中通常用递归版虽然严格说递归栈会占 O(log n) 空间但在面试和本题环境下一般是可以接受的。geeksforgeeks2​Top-Down 归并排序链表整体思路所谓 Top-Down就是先递归拆分到底再一层层往上合并。整体逻辑可以分成三步分裂、递归排序、合并。geeksforgeeks2​递归终止条件当前链表 head 为空链表head NULL或单节点链表head-next NULL这两种情况链表长度 ≤ 1天然有序直接返回 head。这就是递归的返回条件。scaler1​用快慢指针找到中点把链表一分为二使用 slow、fast 两个指针slow 每次走 1 步。fast 每次走 2 步。循环条件一般写成while (fast ! NULL fast-next ! NULL)。geeksforgeeks1​同时用一个 prev 指针记录 slow 的前一个节点以便之后把链表从中间断开prev-next NULL。循环结束时slow 位于中间节点奇数长度时是真·中点偶数长度时是中偏右或中偏左根据写法略有不同整体不影响排序正确性。左半段[head … prev]。右半段[slow … tail]。baeldung1​对左右两半分别递归排序 merge 合并对左半段递归left sortList(left)。对右半段递归right sortList(right)。这两个递归返回时各自已经局部有序。调用merge(left, right)把两个有序链表按升序合并成一个更长的有序链表然后返回给上一层。真正的排序行为就发生在这里的 merge 中。geeksforgeeks1​“递归只拆不排”L 和 R 为什么有序一个常见疑问是递归往下的时候只是不断拆分链表那怎么保证每一层拿到的 left / right 是有序的呢这里用归纳的角度理解归并排序的正确性youtube​geeksforgeeks​基础情况当链表长度 ≤ 1 时sortList 直接返回这个链表天然有序。归纳假设假设对所有长度 n 的链表sortList 都能返回有序结果。归纳步骤对于长度为 n 的链表先用快慢指针拆成左右两半 L、R。对 L 调用sortList(L)根据假设返回值 leftSorted 一定有序对 R 同理得到有序的 rightSorted。在当前层调用merge(leftSorted, rightSorted)按归并规则合并两个有序链表得到新的有序链表返回。因此在当前层调用 merge 时L 和 R 已经是由更深一层的递归排好序的结果当前层只负责把两段有序链表进一步合并。这就是往下拆、往上合的精髓所在。read.learnyard1​merge 两个有序链表的思路merge 的目标是给定两个已按升序排好的链表 left 和 right生成一个新的升序链表复用原来的节点只动指针不新建数据节点。geeksforgeeks1​核心步骤如下准备哑结点和指针新建一个 dummy 节点只当占位不关心它的值用 tail 指向 dummy表示当前结果链表的尾节点。用指针p leftq right。scaler1​循环比较把小的接到 tail 后面当p ! NULL q ! NULL时反复执行如果p-val q-val则tail-next pp p-next。否则tail-next qq q-next。然后tail tail-next保持 tail 永远指向结果链表的末尾。这一步就是比头部、接较小的过程和经典题「Merge Two Sorted Lists」完全一致。leetcode1​一边用完后直接挂接另一边剩余部分跳出循环后p 或 q 至少有一个为 NULL。如果p ! NULL说明 left 还有剩余tail-next p。否则tail-next q。所有剩余节点本身已经有序整体接上去仍然有序。geeksforgeeks1​返回结果头结点最终结果从dummy-next开始dummy 自身可以释放或忽略。geeksforgeeks1​这一步就是整个算法中真正进行比较和排序的地方每个元素会在不同层的 merge 中被比较若干次总体比较次数是 O(n log n)。wikipedia1​快慢指针找中点的细节奇数 vs 偶数长度在链表归并排序中如何用快慢指针准确切半是一个细节问题。scaler1​典型写法如下伪代码风格初始化 slow head fast head prev NULL 循环 条件while (fast ! NULL fast-next ! NULL) 循环体 prev slow slow slow-next fast fast-next-next 循环结束后 slow 在中点位置。 prev 在中点前一个。 通过 prev-next NULL 把链表断成两半 左半[head ... prev] 右半[slow ... tail]奇数长度如 5fast 每轮走两步最后 fast 落在最后一个节点再往前走会使fast-next为 NULL循环结束此时 slow 在第 3 个节点左边 2 个右边 2 个。偶数长度如 4fast 最终走到 NULL循环停止slow 一般位于第 3 个节点prev 是第 2 个。断开后左半长度 21,2右半长度 23,4。有些变体会故意让 slow 落在偏左中点区别只是在于左右子链表长度差是否允许为 1本题中两种都可以正常工作不影响归并排序的正确性与复杂度。geeksforgeeks1​递归整体伪代码Top-Down结合上面的讨论可以把 Top-Down 归并排序链表的伪代码抽象成这样接近 C 风格stackoverflow1​ListNode* sortList(ListNode* head) { // 1. 递归终止条件长度 1 if (head NULL || head-next NULL) { return head; } // 2. 用快慢指针找到中点并断开成左右两半 ListNode* slow head; ListNode* fast head; ListNode* prev NULL; while (fast ! NULL fast-next ! NULL) { prev slow; slow slow-next; fast fast-next-next; } // 此时 slow 在中点prev 在中点前一个 prev-next NULL; // 断开左半head右半slow ListNode* left head; ListNode* right slow; // 3. 递归排序左右两半 left sortList(left); right sortList(right); // 4. 合并两个有序链表 return merge(left, right); }merge(left, right) 的伪代码就是前面描述的哑结点 两指针比较 掛剩余的那一套逻辑这里不再重复。可以直接复用你在「Merge Two Sorted Lists」那题里写过的版本。geeksforgeeks1​小结整套算法的核心可以用一句话概括往下递归时只负责把链表拆到足够小长度 0 或 1往上返回时每一层负责 merge 它的左右两个有序子链表。对应到代码上就是这三个关键点递归终止条件head NULL || head-next NULL。快慢指针切半fast 走两步、slow 走一步prev-next NULL断开链表。merge 两个有序链表哑结点 比较当前节点值 掛剩余部分。把这三块细节想清楚、写顺之后这类链表 归并排序的题就比较稳了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询