七大排序算法全解析:从插入到三路快排,手把手带你掌握核心思想与实战陷阱

腾讯四大技术方向面经汇总:前端Java C++ Go面试套路与核心考点 大厂技术面试的本质,是考察候选人对基础原理的理解深度和工程实践能力。无论前端、Java、C++还是Go,底层都共享操作系统、网络、数据结构与数据库等知识底座。系统化梳理语言特性、并发模型、内存管理、框架原理和算法设计,是提升面试通过率的关键路径。无论是暑期实习、秋招提前批还是社招跳槽,掌握一套可复用的面经整理方法,能帮助求职者从海量八股文中聚焦高频考点,比如Java的OOM排查、C++的constexpr版本演进与智能指针、Go的GMP调度与channel关闭原则,以及前端的事件循环与浏览器渲染链路。结合真 阅读详情

排序算法(Sort)

排序是计算机科学中最基础、最经典的算法问题之一,也是面试和实际开发中的高频考点。本文系统性地解析了七大经典排序算法:插入排序、希尔排序、冒泡排序、选择排序、堆排序、快速排序(Hoare版+三数取中)以及三路快速排序。每种算法都配有完整的C语言实现、逐行解释、时间复杂度分析和关键陷阱提示。

目标读者:正在学习数据结构与算法的在校学生、准备技术面试的求职者,以及需要复习排序算法核心思想的开发者。

核心价值:

  1. 代码实战导向:每个算法都提供可直接运行的C语言实现,并附有逐行注释,帮助理解每一行代码的作用。
  2. 陷阱全解析:特别总结了每个算法最容易出错的地方(如选择排序的maxi修正、快排的end先走原则、三路快排的i前进逻辑),避免踩坑。
  3. 工业级优化:不仅讲解基础版本,还引入了三数取中、三路划分等工程优化技巧,提升算法在实际场景中的性能。
  4. 对比与选型:通过复杂度总表和适用场景分析,帮助你根据数据规模、有序度、稳定性等需求选择最合适的排序算法。

无论你是为了通过考试、准备面试,还是想在项目中写出更高效的排序代码,这篇文章都将为你提供清晰的思路和实用的代码参考。

排序像整理一手乱牌:有的算法是一张张往手里插(插入),有的是按间隔分组排再合并(希尔),有的是两两比较冒泡(冒泡),有的是挑最大最小放两头(选择),有的是建堆取顶(堆排),有的是分地盘递归(快排)。各有各的脾气和适用场景。

核心思想

排序没有「最好」,只有「最适合」。选择排序算法要看:数据规模、是否基本有序、是否稳定、是否要原地(空间 O(1))、是否含大量重复元素。

本项目实现了:插入、希尔、冒泡、选择、堆排、快排(Hoare + 三数取中)、三路快排。先讲辅助函数,再逐一拆解每种排序。

辅助函数

交换 Swap

void Swap(int *a, int *b)
{
    int c = *a;
    *a = *b;
    *b = c;
}

逐行解释:传指针才能修改实参。经典三变量交换。工程提示:现代编译器下,用临时变量的交换往往比「不用临时变量的位运算异或交换」更快(异或交换有数据依赖、不可并行、且 a==b 时会清零),且可读性好,所以别迷信「不用临时变量更高级」。

向下调整 AdjustDown(堆排用)

void AdjustDown(int *a, int n, int parent)
{
    int child = 2 * parent + 1;       // 左孩子
    while (child < n)
    {
        if (child+1 < n && a[child] < a[child+1])   // 大根堆:选较大孩子
            child++;
        if (a[child] > a[parent])    // 孩子比父大就下沉
        {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = 2 * parent + 1;
        }
        else break;
    }
}

逐行解释:注意这里是大根堆的向下调整(a[child] < a[child+1] 选大孩子,a[child] > a[parent] 才下沉),和堆模块里小根堆的比较符号相反。堆排升序要用大根堆——因为要把最大值换到末尾。child+1 < n 防右孩子越界。


1. 插入排序 InsertSort(O(n²),稳定,小数据王者)

void InsertSort(int *a, int sz)
{
    // 排序[0,end]有序,把 end+1 插入
    for(int i = 0; i < sz-1; i++)
    {
        int end = i;
        int tmp = a[end+1];        // 待插元素先存起来
        while(end >= 0)
        {
            if(tmp < a[end])       // 比 tmp 大的往后挪
            {
                a[end+1] = a[end];
                end--;
            }
            else break;
        }
        a[end+1] = tmp;            // 找到位置,放下
    }
}

原理:像整理扑克牌——左手中已排好序,从右手抽一张,在左手从右往左找位置,比它大的牌往后挪一格,找到空位插进去。

逐行解释:

  • 外层 i 控制把哪个元素插入(a[i+1]),[0, i] 始终有序。
  • tmp 必须先存——因为 a[end+1] 会被后面的挪动覆盖,不存就丢了。
  • 内层 while 从 end 往左扫,比 tmp 大的就右移一格,直到找到不大于 tmp 的位置。
  • a[end+1] = tmp 把待插元素放进空位。

关键点:

  • tmp 必须提前保存(最高频考点)。
  • 最好情况(基本有序)O(n),最坏 O(n²)。
  • 稳定(相等元素不交换,相对顺序不变)。
  • 小数据量(<16)时比快排还快,所以快排常在小区间切换到插入排序。

2. 希尔排序 ShellSort(O(n^1.3),不稳定,插入的升级)

void ShellSort(int *a, int n)
{
    int gap = n;
    while(gap > 1)
    {
        gap = gap/3 + 1;           // 缩小间隔,+1 保证最后 gap=1
        for(int i = 0; i < n-gap; i++)
        {
            int end = i;
            int tmp = a[end+gap];  // 跨 gap 取待插元素
            while(end >= 0)
            {
                if(tmp < a[end])
                {
                    a[end+gap] = a[end];  // 跨 gap 挪动
                    end -= gap;
                }
                else break;
            }
            a[end+gap] = tmp;
        }
    }
}

原理:插入排序在「基本有序」时很快,但整体乱时慢。希尔先按大间隔 gap 分组做插入排序(让远的元素快速归位),逐步缩小 gap,最后 gap=1 做一次普通插入排序收尾。因为前面已让数组「基本有序」,最后一次插入排序接近 O(n)。

逐行解释:

  • gap = gap/3 + 1:每次缩到约 1/3,+1 保证 gap 最终会变成 1(否则 gap 可能卡在 2→0 跳出,漏了最后一次完整排序)。
  • 内层和插入排序几乎一样,只是步长从 1 变成 gap——同一个位置可以同时排多个间隔为 gap 的组(代码里用 i 从 0 到 n-gap,相当于多组并排)。
  • a[end+gap] = tmp:放回时也要跨 gap。

注释里的笔误:项目代码注释中旧版本写成了 a[end+1] = tmp,应该是 a[end+gap] = tmp(跨 gap 放回)。当前实际代码已修正为 a[end+gap]。

关键点:gap 序列影响性能,gap/3+1 是常用序列,平均约 O(n^1.3)。不稳定(相同元素可能分到不同组,相对顺序会变)。

3. 冒泡排序 BubbleSort(O(n²),稳定,教学用)

void BubbleSort(int *a, int n)
{
    for(int j = 0; j < n-1; j++)        // n-1 趟
    {
        for(int i = 1; i < n-j; i++)     // 每趟把最大冒到末尾
        {
            if(a[i-1] > a[i])           // 前比后大就交换
                Swap(&a[i-1], &a[i]);
        }
    }
}

原理:相邻元素两两比较,前大后小就交换,每趟把当前最大值「冒泡」到末尾。

逐行解释:外层 j 控制趟数(n-1 趟),内层 i 扫一遍未排序部分。n-j 是因为末尾 j 个已排好不用再比。

优化点(未实现):加一个 flag,若某趟无任何交换说明已有序,提前结束——最好情况可到 O(n)。当前实现无论是否有序都跑满 O(n²)。

关键点:稳定(相等不交换)。简单但慢,基本只在教学和「几乎有序 + 数据量小」时用。

4. 选择排序 SelectSort(O(n²),不稳定,每轮选两头)

void SelectSort(int *a, int n)
{
    int begin = 0, end = n-1;
    while(begin < end)
    {
        int maxi = begin, mini = begin;
        for(int i = mini+1; i <= end; i++)  // 一轮同时找最大和最小
        {
            if(a[i] > a[maxi]) maxi = i;
            if(a[i] < a[mini]) mini = i;
        }
        Swap(&a[mini], &a[begin]);      // 最小放开头
        if(maxi == begin)               // ⚠️ 关键:maxi 被 mini 换走了
            maxi = mini;
        Swap(&a[maxi], &a[end]);         // 最大放末尾
        begin++;
        end--;
    }
}

原理:每轮在 [begin, end] 里同时找最小值和最大值,最小放 begin,最大放 end,两端向中间收拢。比普通「每轮只找最小」的版本少一半轮次。

逐行解释 & 关键陷阱:

  • 一轮同时更新 maxi 和 mini。
  • Swap(&a[mini], &a[begin]) 把最小换到开头——但如果 maxi 恰好等于 begin,这一步把 a[begin](即原最大值)换到了 mini 位置,maxi 下标失效!所以下一行 if(maxi == begin) maxi = mini; 修正:最大值现在在 mini 处。
  • 这是选择排序最经典的坑:先换 mini 可能破坏 maxi 指向,必须判断修正。

关键点:不稳定(交换跨越了相等元素)。虽优化了一半轮次,但比较次数仍是 O(n²)。

5. 堆排序 HeapSort(O(n log n),不稳定,原地)

void HeapSort(int *a, int n)
{
    // 建大根堆:从最后一个非叶子节点开始向下调整
    for(int i = (n-2)/2; i >= 0; i--)
        AdjustDown(a, n, i);
    // 反复取堆顶(最大)放末尾,缩小堆,再调整
    for (int end = n-1; end > 0; end--)
    {
        Swap(&a[0], &a[end]);        // 堆顶(最大)换到末尾
        AdjustDown(a, end, 0);      // 对 [0, end) 重新向下调整
    }
}

原理:升序要建大根堆。建堆后堆顶是最大值,交换到末尾,然后对剩余部分(end 减小)重新向下调整恢复堆序。重复 n-1 次,数组即升序。

逐行解释:

  • 建堆从 (n-2)/2(最后一个非叶子节点)开始,往前每个节点向下调整。这是 O(n) 建堆法(不是逐个 push 的 O(n log n))。
  • 排序循环:Swap(&a[0], &a[end]) 把当前堆顶(最大)换到 end 位置,end-- 缩小堆的范围,AdjustDown(a, end, 0) 对剩下的堆顶做一次向下调整(注意传入 end 而非 n,因为末尾已排好不再参与)。

关键点:升序用大根堆(最大值往末尾送),降序用小根堆。不稳定。原地排序,空间 O(1)。建堆 O(n),排序 n 次 O(log n) 调整,总体 O(n log n)。

6. 快速排序 QuickSort(Hoare 版 + 三数取中)

三数取中 Getmidi(优化 pivot)

int Getmidi(int *a, int left, int right)
{
    int midi = (left + right) / 2;
    if(a[left] < a[midi])
    {
        if(a[midi] < a[right]) return midi;
        else if(a[left] < a[right]) return right;
        else return left;
    }
    else
    {
        if(a[right] > a[left]) return left;
        else if(a[midi] > a[right]) return midi;
        else return right;
    }
}

原理:快排最怕有序数据——如果每次选第一个做 pivot,有序数组会让每轮只分出一个元素,退化到 O(n²)。三数取中从 left、mid、right 三个位置选中间值做 pivot,避免极端。

逐行解释:通过嵌套比较三个位置的值,返回中间值的下标。逻辑绕,但本质就是「三个数里排中间那个」。不是为了找最大或最小,而是找中位,让划分更均衡。

Hoare 划分版 QuickSort

void QuickSort(int *a, int left, int right)
{
    if (left >= right) return;      // 区间长度 <=1 终止

    int midi = Getmidi(a, left, right);
    Swap(&a[midi], &a[left]);       // pivot 换到 left 位置
    int keyi = left;
    int begin = left, end = right;
    while(begin < end)
    {
        // end 先走,找比 pivot 小的
        while(begin < end && a[end] >= a[keyi]) end--;
        // begin 后走,找比 pivot 大的
        while(begin < end && a[begin] <= a[keyi]) begin++;
        Swap(&a[begin], &a[end]);   // 交换这对逆序
    }
    Swap(&a[keyi], &a[end]);        // pivot 归位到相遇点
    keyi = begin;
    QuickSort(a, left, keyi-1);     // 递归左半
    QuickSort(a, keyi+1, right);    // 递归右半
}

原理:选一个 pivot,把数组分成「小于 pivot / pivot / 大于 pivot」三段,pivot 到位,再递归排左右两段。分治思想。

逐行解释 & 关键点:

  1. Getmidi 选 pivot 并换到 left,避免有序退化。
  2. 为什么 end 先走:pivot 在 left,end 从右找小的,begin 从左找大的。当 begin 和 end 相遇时,相遇点的值应该比 pivot 小(因为是 end 最后停在那),这样 Swap(&a[keyi], &a[end]) 才能把 pivot 换到正确的「左小右大」分界。如果 begin 先走,相遇点可能是比 pivot 大的值,pivot 换过去就错了。这是 Hoare 划分最核心的细节。
  3. a[end] >= a[keyi] 用 >= 而非 >:遇到相等的也跳过,避免相等元素卡住循环。
  4. 递归终止 left >= right:区间空或单元素已有序。

关键点:

  • 平均 O(n log n),最坏 O(n²)(三数取中极大缓解)。
  • 递归深度 O(log n),栈空间。
  • 不稳定。
  • 工程优化(未实现):小区间(如 <16)切换插入排序,减少递归开销;尾递归优化减少栈深度。

7. 三路快速排序 QuickSort3Way(解决大量重复元素)

void QuickSort3Way(int *a, int left, int right)
{
    if (left >= right) return;

    int midi = Getmidi(a, left, right);
    Swap(&a[midi], &a[left]);
    int key = a[left];

    // 三段循环不变量:
    // a[left..lt-1]  < key
    // a[lt..i-1]    = key
    // a[i..gt]      未处理
    // a[gt+1..right] > key
    int lt = left;
    int gt = right;
    int i = left;

    while (i <= gt)
    {
        if (a[i] < key)            // 小于:换到 < 区
        {
            Swap(&a[i], &a[lt]);
            lt++;
            i++;                   // i 前进,换来的已检查过
        }
        else if (a[i] > key)      // 大于:换到 > 区
        {
            Swap(&a[i], &a[gt]);  // ⚠️ i 不前进!换来的 a[gt] 没检查过
            gt--;
        }
        else                       // 等于:留在 = 区
        {
            i++;
        }
    }

    // 只递归 < 区和 > 区,= 区已就位不再处理
    QuickSort3Way(a, left, lt-1);
    QuickSort3Way(a, gt+1, right);
}

原理:荷兰国旗问题(Dutch National Flag)。传统快排两路划分在大量重复元素时会退化(很多元素等于 pivot,反复无效交换)。三路把数组分成 < key、= key、> key 三段,= key 段直接跳过不再递归,重复元素越多越快。

逐行解释 & 关键点:

  • 三个游标维护循环不变量:lt 是 < 区右端,gt 是 > 区左端,i 是当前考察位置。
  • a[i] < key:换到 < 区末尾,lt++、i++。换来的元素来自 lt 位置(属于 = 区,已检查过),所以 i 前进。
  • a[i] > key:换到 > 区开头,gt--。i 不前进——因为从 gt 换过来的元素还没检查过,下轮要再看它。
  • a[i] == key:留在 = 区,i++。
  • 递归只处理 < 区 [left, lt-1] 和 > 区 [gt+1, right],= key 段跳过。

i 是否前进是最关键的细节:< 和 == 时 i++,> 时 i 不动。搞反了会漏判或多判。

关键点:解决大量重复元素的退化问题,重复越多越接近 O(n)。仍然 O(n log n) 平均、不稳定。

复杂度与特性总表

算法平均时间最坏时间空间稳定性适用场景
插入排序O(n²)O(n²)O(1)✅稳定小数据 / 基本有序
希尔排序O(n^1.3)O(n²)O(1)❌不稳定中等规模
冒泡排序O(n²)O(n²)O(1)✅稳定教学 / 几乎有序
选择排序O(n²)O(n²)O(1)❌不稳定数据量小
堆排序O(n log n)O(n log n)O(1)❌不稳定大数据 + 原地
快排(两路)O(n log n)O(n²)O(log n)❌不稳定通用最快
三路快排O(n log n)O(n log n)O(log n)❌不稳定大量重复元素

常见陷阱清单

  1. 插入排序不先存 tmp → 待插元素被覆盖丢失。
  2. 希尔排序 gap 不加 1 → gap 卡在 2 跳出,漏最后一次完整排序。
  3. 选择排序 maxi == begin 不修正 → maxi 指向被 mini 交换走的值,结果错误。
  4. 堆排序升序建小根堆 → 应建大根堆(最大值往末尾送)。
  5. Hoare 快排 begin 先走 → 相遇点可能是大值,pivot 归位错误。
  6. 三路快排 a[i] > key 时 i++ → 漏判换来的元素。
  7. quicksort_ 残缺函数 → 代码里有个未完成的 quicksort_,含未定义的 swap、逻辑残缺,会导致编译失败(建议删除)。
  8. 快排不处理有序退化 → 应加三数取中或随机化 pivot(本项目已加三数取中 ✅)。

我的实现 vs 教科书实现

  • 亮点:快排加了三数取中(防有序退化)+ 三路划分(防重复退化),这是工业级优化意识。选择排序双向选 max/min 减半轮次。
  • 残缺代码:quicksort_ 函数未完成,会编译失败,应删除。
  • 注释笔误:希尔排序注释旧版本 a[end+1] = tmp 应为 a[end+gap](实际代码已对)。
  • 缺归并排序:稳定 O(n log n),外部排序、链表排序必备。
  • 缺非比较排序:计数排序、基数排序、桶排序(线性时间,适合整数范围有限)。
  • 缺快排优化:小区间切换插入排序、非递归(用栈模拟)。
  • 测试覆盖:三路快排测了全相等/大量重复/已排序/逆序四种 case,测试意识好。

拓展知识点

变体与进阶

  • 归并排序:分治 + 合并,稳定 O(n log n),适合链表排序和外部排序(数据放不下内存时)。手写重点在 merge 的双指针合并和临时数组。
  • 计数排序:统计每个值出现次数,适合值域小的整数,O(n+k)。非比较排序,能突破 O(n log n) 下界。
  • 基数排序:按位(个十百…)用计数排序,适合定长整数或字符串。
  • 桶排序:分桶后桶内排序,适合均匀分布数据。
  • 双轴快排(Dual-Pivot):JDK Arrays.sort 对基本类型的实现,两个 pivot 分三段,实测比单轴快。

常见考点(必刷)

  • 快排的 pivot 优化:三数取中、随机化,防有序退化。
  • 三路划分:大量重复元素的优化,荷兰国旗问题。
  • 堆排序建堆 O(n) 证明:调整距离求和。
  • 排序稳定性:哪些稳定(插入/冒泡/归并),哪些不稳定(希尔/选择/堆/快),及稳定性的应用场景(多关键字排序)。
  • 快排非递归:用栈模拟递归,避免栈溢出。
  • 各排序手写:面试常要求手写快排、归并、堆排。

与其他结构的关系

堆排序依赖堆结构(见 Binary_Tree 模块的 AdjustDown/AdjustUp);归并排序体现分治,和快排同源;快排的划分思想与 BST 的构建同构(一次快排划分 ≈ 把 pivot 插入 BST)。排序是二分查找、贪心、双指针等算法的前置条件——很多算法要求输入有序。

一句话记忆法

插入像理牌先存 tmp,希尔按 gap 缩,冒泡相邻换,选择两头挑且防 maxi 被换走,堆排建大根堆取顶,快排 end 先走 + 三数取中,三路分 < = > 且遇大于 i 不动。

5个4000比特大状态置换算法的设计与分析 下面是 5 个概念性、实验性的 4000 位可逆置换设计。它们都保证是双射,但未经过密码分析,不可直接用于生产系统。风格分别覆盖:三维 SPN、Feistel、可逆元胞自动机、ARX、宽轨迹/Keccak 式结构。 这 5 个设计分别代表了 SPN、Feistel、元胞自动机、ARX、宽轨迹 Keccak 五种不同风格,覆盖了主流大状态置换的构造思路。再次强调:它们只是实验性概念,若要用于实际协议,必须经过严格安全性分析。 阅读详情

相关推荐

SceneSplat

目标:训练一个网络,输入高斯参数 → 输出与自然语言空间对齐的特征。考虑到标注语义的 3DGS 数据有限,作者设计了。策略,让模型在无标签场景上学到有用表征。都有一个与自然语言对齐的语义特征。这是整个方法的第一步,目标是让。

weixin_61091146的博客 1284

快速排序算法详解:从分治思想到工程优化实现

排序算法是计算机科学中处理数据组织的核心基础,其核心目标是将无序数据集合按照特定顺序重新排列。基于分治策略的快速排序算法,通过选取基准元素将数组划分为两个子序列,递归地对子序列进行排序,从而实现高效的原址排序。该算法平均时间复杂度为O(n log n),空间复杂度为O(log n),在处理大规模随机数据时展现出卓越性能。在工程实践中,通过随机化基准选择、三数取中、小数组优化和三路快排等策略,能够有效避免最坏情况下的O(n²)时间复杂度退化,并提升对重复元素的处理效率。这些优化技术使得快速排序成为处理通用数据

weixin_30600197的博客 389

Kali Linux下John the Ripper实战:从识别yescrypt哈希到破解zip压缩包(保姆级避坑指南)

本文详细介绍了在Kali Linux中使用John the Ripper工具从识别yescrypt哈希到破解zip压缩包的全过程。针对常见的'No password hashes loaded'报错,提供了两种解决方案,并扩展讲解了哈希识别、zip文件破解及性能优化技巧,是安全测试人员的实用指南。

weixin_29323365的博客 236

视频隐私脱敏工程化:CSRT/KCF追踪算法在本地推理中的实现与边界

在短视频内容合规审查中,动态遮挡(Dynamic Masking)是高频且高耗时的环节。传统手动关键帧打码不仅效率低下,且在目标快速移动或遮挡时极易出现遮罩漂移。本文从计算机视觉工程角度,解析基于CSRT与KCF算法的目标追踪方案在视频隐私处理中的技术路径、性能权衡及落地实践。

Mysssone的博客 218

深入解析车载以太网DoIP诊断:Wireshark实战TCP/UDS报文分析

本文深入解析车载以太网DoIP诊断,重点介绍如何利用Wireshark进行TCP/UDS报文实战分析。文章详细讲解了从以太网帧、IP帧、TCP段到DoIP协议及UDS服务的逐层拆解方法,并提供了使用Wireshark定位通信故障(如乱序、丢包、重传)的实用技巧,帮助工程师高效排查车载诊断网络问题。

c2d3e4f的博客 674

Go语言AI智能体开发实战:基于LangGraphGo构建下一代工作流引擎

在AI应用开发领域,工作流编排是构建复杂智能系统的核心技术。其核心原理是将任务执行过程建模为状态图,通过节点和边的组合定义数据流转路径,实现多步骤推理与工具调用的自动化。这种基于状态图的编程模型,为智能体提供了清晰的“思考-行动-观察”循环抽象,显著提升了复杂业务逻辑的可维护性和可观测性。从技术价值看,它解决了传统代码中状态分散、流程僵化的问题,支持条件分支、并行执行等高级模式,是实现生产级AI应用的关键基础设施。在应用场景上,该技术广泛适用于客服机器人、自动化审批、数据ETL等需要多步骤协调的领域。本文以

weixin_30457551的博客 361

【算法拾光】链表 哈希表|K个一组翻转链表 + 两数之和

K个一组翻转链表需分段反转,借助哑节点与前驱指针维护连接,避免断链;两数之和利用哈希表实现O(1)查找,空间换时间,高效定位目标组合。前者考察链表指针操作,后者聚焦映射关系构建,均为面试高频题。

wuqingyigexiaoba的博客 314

Python爬虫实战:用Selenium+ChromeDriver批量抓取政府网站政策文件(附完整代码)

本文详细介绍了使用Python的Selenium和ChromeDriver进行政府网站政策文件批量抓取的实战技巧。从环境配置、动态页面元素定位到分页机制破解和反爬虫策略,提供了完整的解决方案和代码示例,帮助开发者高效获取结构化政策数据。

weixin_42531710的博客 266

排序算法深度笔记(下):非比较类排序的线性突破与工程边界

排序算法时间复杂度额外空间复杂度稳定性是否基于比较选择排序O(N^2)(O(1))不稳定是冒泡排序O(N^2)(O(1))稳定是插入排序O(N^2)(O(1))稳定是归并排序O(N\log N)(O(N))稳定是快速排序O(N\log N)O(log N)不稳定是堆排序O(N\log N)O(1)不稳定是计数排序(O(N + M))O(M))稳定否基数排序O(N)稳定否桶排序平均O(N) / 最坏O(N^2)

2401_87069538的博客 590

【知识分享】单端到混合S参数转换指南

信号完整性(SI)工程师几乎总是必须使用S参数,因为许多的高速接口和行业标准都正在转向串行链路架构,并使用基于S参数测量的频域限制指标,了解其工作过程将会很有意义。

一只豌豆象的博客 1580

基于最小二乘法(LS)和最小均方误差(MMSE)算法的导频信道估计

导频信道估计在无线通信系统中起着至关重要的作用,它用于估计无线信道的特性,以便在接收端进行正确的信号解调和解码。最小二乘法(LS)和最小均方误差(MMSE)是两种常用的导频信道估计算法,它们在估计精度和计算复杂度之间取得了良好的平衡。首先,我们提取当前的导频符号,然后将接收到的信号与导频符号相乘得到接收导频信号。最小二乘法是一种经典的导频信道估计算法,它通过最小化接收信号与预测信号之间的均方误差来估计信道。下面将详细介绍基于LS和MMSE算法的导频信道估计,并提供相应的MATLAB代码实现。

pytorchCode的博客 679

算法日常・每日刷题--<动态规划>42

本题为“不相交的线”,与最长公共子序列问题相似。使用动态规划求解:定义 dp[i][j] 表示 nums1[0:i] 与 nums2[0:j] 的最大不相交连线数。若元素相等,则继承左上值加1;否则取上方或左方最大值。最终返回 dp[m][n],即两数组的最大不相交连线数。时间复杂度 O(mn),空间复杂度 O(mn)。

2503_92261635的博客 20

探秘RK3588:OverlayFS挂载与固件升级全攻略

本文围绕RK3588芯片的OverlayFS文件系统挂载与固件升级技术展开详细阐述。首先介绍了RK3588芯片的卓越性能及其在嵌入式领域的广泛应用,重点分析了OverlayFS文件系统的分层架构原理及其在RK3588设备上的挂载方法,包括详细的挂载步骤、常见问题解决方案。随后详细阐述了RK3588设备在Windows和Linux环境下的固件升级方案,涵盖升级准备、具体操作流程及故障处理等内容。最后通过智能安防监控设备的实际案例,展示了OverlayFS挂载与固件升级技术的应用效果。文章为嵌入式开发者提供了全

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 286

C语言程序设计题|有序数组插入数字冒泡排序

题目要求将一个数插入有序数组末尾,并用冒泡排序实现整体升序。代码先将输入数存入数组最后位置,再通过双重循环进行冒泡排序,每轮将较大元素向后移动,最终输出11个升序元素。该方法虽时间复杂度为O(n²),但符合题目要求,逻辑清晰,适合初学者理解排序原理。

2301_81429079的博客 750

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【算法拾光】模拟|外观数列 + 数青蛙

本文总结两道模拟题解法:外观数列通过迭代统计连续数字个数生成下一项,关键在于处理末尾字符;数青蛙则模拟青蛙叫声“croak”状态转移,维护各阶段青蛙数量,记录最大并发数并确保全部完成。共性在于严格遵循流程、细致处理边界与非法情况,锻炼字符串遍历与状态管理能力。

wuqingyigexiaoba的博客 370

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

牛客周赛165C题(前后缀性质,递推,排序,枚举)

题目大意:给定长度为 `n` 的数组,选择 `k ∈ [1,n]`,将前 `k` 个元素整体翻转,后面的元素不变。要求操作后的整个数组非递减,统计满足条件的 `k` 的数量。题目链接:https://ac.nowcoder.com/acm/contest/141721/C。时间复杂度:O(nlogn)

2501_94316951的博客 171

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

408 算法 04|主元素与三元组最小距离:两种“扫描时做决定“的思路

2013(41) 的主元素用摩尔投票一遍选出候选,但真正的考点是"选完必须验票";2020(41) 的三元组最小距离,三指针每次只挪最小的那个,靠的是 D=2×(max−min) 这个恒等式。两套完整 C 代码加真实输出,附官方评分档位表。

m0_56226131的博客 409

【数据结构-3】算法美学之降复杂度

在while循环的循环体内,首先用临时变量保存最后一个位置的数据,再通过for循环把所有元素往后移动一位,最后把tmp中保存的值放到下标为0的位置上去。方法二的思路是:创建新数组,让原数组中后k个数据移动到新数组中前面的位置,让原数组中前numsSize-k个数据移动到新数组中后面的位置,这。这段代码的思路是:三次逆置,第一次逆置前numsSize-k个数据,第二次逆置后k个数据,最后整体逆置。这篇博文分享一道解法不一的算法题,求解过程的目的在于降低算法的时间复杂度,题源力扣。,属于空间换时间的操作。

weixin_44218929的博客 85

算法61,判定是否为字符重排

两字符串能互为重排 ⇔。

sheshibenniaodan的博客 218

算法日常・每日刷题--<动态规划>32

给定严格递增的正整数数组,求最长斐波那契子序列长度。使用二维DP,dp[i][j]表示以arr[i]、arr[j]为最后两项的序列长度,初始化均为2。通过哈希表快速查找前一项target = arr[j] - arr[i],若存在且下标小于i,则状态转移:dp[i][j] = dp[k][i] + 1。遍历更新全局最大长度,最终若结果小于3则返回0。时间复杂度O(n²),空间复杂度O(n²)。

2503_92261635的博客 178

算法札记:几何分布在算法题UVA1262的运用

集卡问题求解核心:将集齐n种优惠券的过程分为n个阶段,每个阶段期望购买次数为 $ \frac{n}{k} $(k为剩余种类数),总期望为 $ n \cdot H_n $(调和数)。通过自洽方程推导单阶段期望,无需几何分布或无穷级数,逻辑清晰且适用于大一水平。例如,n=5时期望约11.42,非5。题目要求以带分数形式输出精确结果,算法上需累加分数并化简。

Keven_11的博客 353

SSL证书选RSA还是ECDSA:公钥算法、CA签名与兼容性怎么核对

申请SSL证书时,RSA和ECDSA该怎么选?先分清站点公钥算法、CA签名算法和TLS实际兼容性,再用隔离OpenSSL实验读取两类材料,核对申请、续期和部署交接,避免只看Signature Algorithm就选错。

谁呀!的博客 399

考拉兹猜想 - 7

本文通过引入虚数单位重构Collatz猜想的迭代逻辑,指出偶数减半与奇数“乘3加1”的过程在虚数坐标下等价于归结操作。关键在于:乘3加1使奇数跃升至偶数,随后持续减半,而虚数单位代表“无限减1”,实现无限向有限的转化。无论初始值如何,减半始终主导,最终归结为4-2-1循环。该视角消解了正向思维中的速度竞争疑虑,证明归结是必然结果。

杨铸人的博客 42

从曲线到储层:Techlog/KAPPA 全流程算法、UltraLAB 选型与本地 AI 部署实战

以高频 CPU 保证交互,以充足内存承载多井与多任务,以 NVMe 打通项目和知识库 I/O,再按本地模型、上下文与并发配置 GPU。对 UltraLAB 用户而言,更合理的采购方式是带着真实项目做选型:一组典型井、一套完整解释模板、一个目标大模型、一次多人并发测试。用实测结果确定配置,才能把预算转化为解释效率,而不是闲置算力。软件模块、操作系统与许可条件以厂商当前正式说明为准;硬件建议需结合项目实测。AI 输出只能作为辅助证据,不能替代岩石物理与油藏工程专业审核。

wdf8088的博客 181

贪心题目:可以到达的最远建筑

为了到达尽可能远的建筑物,在将砖块替换成梯子时,应将高度差最大位置的砖块替换成梯子,此时剩余的砖块数量最多,如果将高度差较小位置的砖块替换成梯子,则剩余的砖块数量较少,能到达的最远建筑物下标不可能更大。开始向后面的建筑物移动,移动过程中维护优先队列存储正高度差,优先队列的队首元素是最大元素,每次遇到正高度差时优先考虑使用砖块,如果砖块数量不够则使用梯子替换最大高度差位置的砖块,直到砖块和梯子都使用完毕时,到达的建筑物即为最远建筑物。如果以最佳方式使用给定的梯子和砖块,返回可以到达的最远建筑物的下标。

stormsunshine的博客 290

强化学习第十一课:完整 Q-Learning 算法

表示有:\[ 5 \]0 1 2 3 40:向左1:向右目标位置:\[ 4 \]

qq_56657939的博客 303
上一篇: 手撕基础二叉树:从链式存储到层序遍历,彻底搞懂所有核心接口
下一篇: Claude Code桌面版:CLI的GUI化革命
DDXYcoder
博客等级 码龄1年 140粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值