【不三不四的脑洞】一个梦所引发关于排序算法的思考

举报
ShaderJoy 发表于 2022/08/06 01:03:38 2022/08/06
【摘要】 前一段时间,我做了一个梦 一个漆黑的深夜里,我正在躲避日本皇军和伪满军人的追捕,突然发现前方有一个很大的古刹,虽然有点阴森,但是随着追兵的步步逼近,我也别无选择,只能咬咬牙打算藏身在里面。 我翻墙...

前一段时间,我做了一个梦

一个漆黑的深夜里,我正在躲避日本皇军和伪满军人的追捕,突然发现前方有一个很大的古刹,虽然有点阴森,但是随着追兵的步步逼近,我也别无选择,只能咬咬牙打算藏身在里面。

在这里插入图片描述

我翻墙便进了寺庙,在里头转啊转啊,突然发现天空出现了几只小蝙蝠(因为离得远所以看着小),而且有越来越多的趋势,逐渐漫天都是,而且越变越大(开始往下降、准备着陆)。

在这里插入图片描述

我躲在大柱子后面观察其中先着陆的几只,大的有将近一人高,而且乍看之下有些人形。

在这里插入图片描述

直觉告诉我,此地不宜久留,我尽量回避它们的眼神,不敢直视它们的眼睛(依稀记得 Discovery 之类的探索频道有说过,盯着动物看是一种挑衅行为),但是情急之下我还是得强装镇定的往前继续走,心中一边默念 “佛祖保佑!佛祖保佑!佛门净地,不能杀生,希望它们对人不感兴趣,不会吃人。。。”。
在这里插入图片描述

我往前走着走着,不知经过了多久,忽然发现前方有几个蝙蝠队伍,它们有序聚集在各自对应的小窗户前。我凑近仔细观察,发现原来是它们正在从中领取一种食物。

突然背后有什么东西拍了拍我的肩膀(着实吓了我一跳),我惊恐地回头一看,原来是位面容清秀的小沙弥,他微微一笑告诉我:“施主莫慌,看您是生面孔,想必是第一次来敝寺。您所见的这些巨蝙蝠是敝寺的守护,性格温顺,不轻易伤人,如若有邪物想进犯本寺,它们就会倾巢而出赶走邪物,那些追捕你的军队,已经被它们所吓跑,施主不必多虑。此外,它们所吃的东西叫 ‘尸净’,是它们的最爱。”

说着,他便拿出一块来,我凑近一看,那个食物看起来是一种小小的带有人形的果冻(胶状体),但是通体晶莹,有多种颜色。

说着这位小沙弥就邀请我进了他们的后厨。进屋一看,我被眼前的一切所 震惊 了。。。他们的炊具 —— 竟然是一个 马桶型 的大锅,里头烧着满满的一锅杂烩,各种各样的颜色,但是看不出是什么食材。

小沙弥继续介绍说,其实他们也不用往里头加任何食材,只要加 “水” 熬制七七四十九个小时就能形成晶莹剔透的胶状物质。“是什么神奇的水吗?” 我好奇地问道。小沙弥说:“是洗干净尸体的水”。。。(听到此处,我心中不自觉一阵翻江倒海。。。

在这里插入图片描述
越怕什么越来什么,说话间,小沙弥便热情地用大勺盛了一碗给我,虽然心中万般不情愿,但是无奈盛情难却,我又脸皮薄不好意思拒绝,所以。。。正当我在考虑先吃哪一碗的时候突然灵机一动,岔开话题,询问小沙弥他们这样给蝙蝠发食物是否有遇到过什么效率问题。小沙弥说:“的确是有一个问题,虽然这些蝙蝠很有素质,能够依序排队领取食物,但是每只蝙蝠的食量却不一样,有多有少,要是能够对它们依据食量提前进行排序就好了!

小沙弥还提醒了我有额外要求

  • 时间复杂度:nlogn
  • 空间复杂度:常量

听到这里,我突然醒了,但是本着对小沙弥负责的态度(绝对不是为了蹭 CSDN 的礼物),我还是敏感地发觉这是一个 LeetCode 148 相关的排序问题

所以

如何才能满足上述要求对蝙蝠进行排序?

在这里插入图片描述

首先,通过查阅相关资料,常见的几种排序算法的性能如下

方法 容器 时间复杂度 空间复杂度
插入排序 数组/链表 O(n^2) O(1)
堆排序 数组/链表/链表 O(nlogn)/O(nlogn)/O(n^2logn) O(1)/O(n)/O(1)
快速排序 数组/链表 O(nlogn)~O(n^2) O(logn)~O(n)
归并排序(自上而下) 数组/链表 O(nlogn)/O(nlogn) O(n+logn)/O(logn)
归并排序(自下而上) 数组/链表 O(nlogn)/O(nlogn) O(n)/O(1)

从上表可知,堆排序在 数组 情况下、归并排序(自下而上)在 链表 情况下都满足以上条件。我这里着重介绍一下 归并排序(自下而上)

  • 代码和详细注释如下
/// @note 代码原作者: Huahua
/// 详细注释:ShaderJoy

/// @note 一只蝙蝠结点
struct ListNode{
    int val;        ///< 蝙蝠食量
    ListNode *next; ///< 后一只蝙蝠
    ListNode(int x): val(x), next(NULL){}
};

class Solution {
public:
  ListNode* sortList(ListNode* head) {
    /// @note 完成状态,只剩 0 或者 1 只蝙蝠
    if (!head || !head->next) return head;
    
    int len = 1;
    ListNode* cur = head;
    while (cur = cur->next) ++len; ///< 首先统计队列中有多少只蝙蝠
    
    ///@note 工具结点
    ListNode dummy(0); 
    dummy.next = head; ///< 它的后面保存的是头蝙蝠
    ListNode* l;
    ListNode* r;
    ListNode* tail;
    
    /// @note n 表示每个分组的蝙蝠个数
    /// 每次迭代后 n 都会翻倍
    /// 保证执行 log(len) 次
    for (int n = 1; n < len; n <<= 1) {      
      cur = dummy.next; ///< 当前处理的队头蝙蝠(已经部分排序的头)
      tail = &dummy; ///< 队尾
      while (cur) {
        l = cur;
        r = split(l, n);            ///< 这两行代码将当前列表分割两次,结果是 l 和 r 都是 n 个蝙蝠
        cur = split(r, n);          ///< 而此时 cur 指向了 l 和 r 的后面
        auto merged = merge(l, r);  ///< 将两个列表进行合并(并从小到大排序)
        tail->next = merged.first;  ///< 合并且有序的 head 接到 tail 的后面
        tail = merged.second;       ///< 合并且有序的 tail 成为新的 tail
      }
    }      
    return dummy.next;
  }
private:
  /// @note 将列表分成两部分,前 n 个元素和其余元素。
  /// 返回其余部分的头。
  ListNode* split(ListNode* head, int n) {    
    /// @note 往后走 n 步
    while (--n && head)
      head = head->next;
    /// @note 把 rest 保存此时的 head 后方(如果 head 存在的话)
    ListNode* rest = head ? head->next : nullptr;
    /// @note 断开 head 后方
    if (head) head->next = nullptr;
    return rest;
  }
  
  /// @note 合并两个列表,返回合并后列表的头和尾。
  pair<ListNode*, ListNode*> merge(ListNode* l1, ListNode* l2) {
    ListNode dummy(0);
    ListNode* tail = &dummy;
    while (l1 && l2) {
      /// @note 将小的放在 l1,大的放在 l2 (所以 while 循环中始终只要操作 l1 和 tail)
      if (l1->val > l2->val) swap(l1, l2);
      tail->next = l1;   ///< 保存 l1 为 tail 的下一位
      l1 = l1->next;     ///< l1 后移一位(继续和 l2 比较)
      tail = tail->next; ///< tail 也后移一位
    }
    tail->next = l1 ? l1 : l2; ///< 善后 l1, l2
    while (tail->next) tail = tail->next; ///< 并让 tail 指向合并后队列的尾部
    return {dummy.next, tail};
  }
};

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 程序运行结果如下(以 4 只蝙蝠为例)

在这里插入图片描述

文章来源: panda1234lee.blog.csdn.net,作者:panda1234lee,版权归原作者所有,如需转载,请联系作者。

原文链接:panda1234lee.blog.csdn.net/article/details/126186692

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。