【数据结构实战】一起开启数据结构有序之门

【数据结构实战】一起开启数据结构有序之门
Alt

🏝️专栏: 【数据结构实战篇】
🌅主页: f狐o狸x


目录

一、排序的概念及应用

        1.1 排序的概念

         1.2 排序的应用

         1.3 常见的排序算法

二、插入排序

        2.1 直接插入排序

        2.1.1 基本思想

        2.1.2 直接插入排序代码实现

        2.1.3 直接插入排序的特性总结

        2.2 希尔排序

        2.2.1 基本思想

        2.2.2 希尔排序代码实现

        2.2.3 希尔排序的特性总结

三、选择排序

        3.1 直接选择排序

        3.1.1 基本思想

         3.1.2 直接选择排序代码实现

        3.1.3 选择排序的特性总结

        3.2 堆排序

        3.2.1 基本思想

        3.2.2 堆排序代码实现

        3.1.3 堆排序的特性总结

四、交换排序

        4.1 冒泡排序

        4.1.1 基本思想

        4.1.2 冒泡排序代码实现

        4.1.3 冒泡排序的特性总结

         4.2 快速排序

        4.2.1 基本思想

        4.2.2 快速排序代码实现

        4.2.3 快速排序的特性总结

 结尾:排序性能对比


        说起排序,我想大家并不陌生,在我们的日常生活中到处都有他的影子。我们点外卖的时候,程序会优先推出评价高的店让我们选择,王者每周都要把每个英雄在每个地区的战力排序出来,甚至高考时,一个省几十万人的成绩也需要排序算法才能搞定。因此排序对我们生活的重要性不言而喻,我们之前学的那些数据结构,也是为了各种各样的排序算法。

一、排序的概念及应用

        1.1 排序的概念

        排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。        稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。        

         1.2 排序的应用

         游戏、外卖、购物……许多地方都离不开排序

        

         1.3 常见的排序算法

        经过各位前辈大佬们的不断迭代,现在排序算法已经很成熟了,主要分为下面几种排序:

         接下来我们将依次实现以上排序算法,并领悟其中的排序思想

二、插入排序

        2.1 直接插入排序

        2.1.1 基本思想

        直接插入排序的思想很简单,就是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。(也可以理解为你正在和你的好朋友们打斗地主,现在好友正在一张一张的发牌,你就只能一次摸一张上来,再将这一张牌插入到正确的位置上,理牌的过程就是插入排序)

        2.1.2 直接插入排序代码实现

// 插入排序 升序 void InsertSort(int* a, int n) { int i = 0; for (i = 1; i < n; i++) { int end = i - 1; int tmp = a[i]; while (end >= 0) { if (tmp < a[end]) { a[end + 1] = a[end]; end--; } else break; } a[end + 1] = tmp; } }

        2.1.3 直接插入排序的特性总结

元素集合越接近有序,直接插入排序算法的时间效率越高时间复杂度:O(N^2)空间复杂度:O(1),它是一种稳定的排序算法稳定性:稳定

        2.2 希尔排序

        2.2.1 基本思想

        仔细观察插入排序你就会发现,如果序列越接近有序,那么他的时间复杂度就越小,最好的情况他的时间复杂度为O(N)。那么此时聪明的人类就能想到,如果我在每次选择排序的基础上,在调整这个序列使他越来越接近有序,那他的时间复杂度是不是就会快很多。

        这个思路就是希尔排序的思路:

        先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

        2.2.2 希尔排序代码实现

//希尔排序 void ShellSort(int* a, int n) { int i = 0; int gap = n/2; while (gap >= 1) { for (i = 0; i < n - gap; i++) { int end = i; int tmp = a[end + gap]; while (end >= 0) { if (tmp < a[end]) { a[end + gap] = a[end]; end -= gap; } else { break; } } a[end + gap] = tmp; } gap /= 2; } } 

        2.2.3 希尔排序的特性总结

1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定(约为:O(N^1.3))

三、选择排序

        3.1 直接选择排序

        3.1.1 基本思想

        将数组全部遍历一遍选出最小的放到最前面,然后再继续遍历,直到有序为止。

         3.1.2 直接选择排序代码实现

// 选择排序 void SelectSort(int* a, int n) { int j = 0; for (j = 0; j < n; j++) { int i = j; int tmp = j; int mini = j; for (i = j; i < n; i++) { if (a[mini] > a[i]) { maxi = i; } } swap(&a[tmp], &a[mini]); } } 

        这样每次都选了最小的,然后依次排序,但是这样的空间复杂度为O(N^2),效率很低,可以在每次选择的时候都把最大的和最小的选出来,然后再排序,就能优化一些,不过时间复杂度依然是O(N^2)

// 选择排序 void SelectSort(int* a, int n) { int left = 0, right = n - 1; while(left < right) { int maxi = right, mini = left; int i = left; for(i = left; i < right; i++) { if (a[mini] > a[i]) { mini = i; } if (a[maxi] < a[i]) { maxi = i; } } swap(&a[right], &a[maxi]); if(mini ==right) { mini = maxi; } swap(&a[left], &a[mini]); left++; right--; } }

        3.1.3 选择排序的特性总结

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

        3.2 堆排序

        巧妙地利用堆的数据结构来选择数据会有意想不到的效果,可以让时间复杂度减小到O(NlogN),可以大大的提高选择排序的效率

        3.2.1 基本思想

        运用堆的数据结构,对数据建堆,然后依次出堆,就可以了

        3.2.2 堆排序代码实现

//堆排序 void AdjustDwon(int* a, int n, int root) { int parent = root; int child = parent * 2 + 1; while (child < n) { if (child + 1 < n && a[child + 1] > a[child]) { child++; } if (a[child] > a[parent]) { swap(&a[child], &a[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } void HeapSort(int* a, int n) { //建堆 int i = 0; for (i = (n - 2) / 2; i >=0 ; i--) { AdjustDwon(a, n, i); } //排序 for (i = 0; i < n; i++) { swap(&a[0], &a[n - i -1]); AdjustDwon(a, n - i - 1, 0); } }

        3.1.3 堆排序的特性总结

        有了堆的数据结构之后,选择一个数据将会变得简单很多,所以他的空间复杂度也会大大提升

1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

四、交换排序

        4.1 冒泡排序

        4.1.1 基本思想

        冒泡排序可是我们的老相识了,在学C语言的时候我们就认识过冒泡排序啦,就是将大的数像冒泡泡一样交换上去,就能得到有序序列

        4.1.2 冒泡排序代码实现

//冒泡排序 void BubbleSort(int* a, int n) { int i = 0; for (i = 0; i < n - 1; i++) { bool order = 1; int j = 0; for (j = 0; j < n - i - 1; j++) { if (a[j] > a[j + 1]) { swap(&a[j], &a[j + 1]); order = 0; } } if (order) { break; } } } 

        4.1.3 冒泡排序的特性总结

1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定

         4.2 快速排序

        快速排序就是我们需要讲解的重点了,之所以称他为快速排序是有他的道理的,让我们一起来感受一下前辈们的智慧吧

        4.2.1 基本思想

       任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

        请看下买你的动图,来掌握第一种快排的思想

        上面动画演示的就是第一个把基准值排到合适位置的方法,下面这个动图将会展示第二种将基准值排到恰当位置的方法(挖坑法):

        还有最后一种,前后指针法:

        理解了单次排序的思路,就掌握快排思路的一半了:

先将左边第一个数字设为基准值(也可以是其他的数字,这里以左边第一个为例子)再把他放到合适的位置上面(合适的位置指的就是前面的数字比他小,后面的数字比他大)再依次重复上述步骤,直到序列有序为止

        4.2.2 快速排序代码实现

         这里我把三个方法都写出来给大家:
 

//快速排序 int FindMid(int a, int b, int c) { if (a > b) { if (b > c) return b; else if (a > c) return c; else return a; } else //a < b { if (a > c) return a; else if (b > c) return c; else return b; } } int PartSort1(int* a, int left, int right) { int mid = FindMid(a[left], a[right], a[(left + right) / 2]); if (mid != left) { swap(&a[mid], &a[left]); } int key = left; while (left < right) { while (left < right && a[right] >= a[key]) { right--; } while (left < right && a[left] <= a[key]) { left++; } swap(&a[left], &a[right]); } swap(&a[key], &a[left]); return left; } int PartSort2(int* a, int left, int right) { int mid = FindMid(a[left], a[right], a[(left + right) / 2]); if (mid != left) { swap(&a[mid], &a[left]); } int hole = left, key = a[left]; while (left < right) { while (left < right && a[right] >= a[key]) { right--; } swap(&a[right], &a[hole]); hole = right; while (left < right && a[left] <= a[key]) { left++; } swap(&a[left], &a[hole]); hole = left; } swap(&a[key], &a[left]); return left; } int PartSort3(int* a, int left, int right) { int mid = FindMid(a[left], a[right], a[(left + right) / 2]); if (mid != left) { swap(&a[mid], &a[left]); } int key = left; int prev = left, cur = left + 1; while (cur <= right) { if (a[cur] < a[key] && ++prev != cur) { swap(&a[cur], &a[prev]); } cur++; } swap(&a[key], &a[prev]); return prev; } void QuickSort(int* a, int left, int right) { if (left >= right) return; int begin = left, end = right; //小区间优化 if (end - begin + 1 <= 10) { InsertSort(a + begin, end - begin + 1); return; } //int mid = PartSort1(a, begin, end); //int mid = PartSort2(a, begin, end); int mid = PartSort3(a, begin, end); QuickSort(a, begin, mid - 1); QuickSort(a, mid + 1, end); }

        4.2.3 快速排序的特性总结

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(logN)
4. 稳定性:不稳定

 结尾:排序性能对比

       最后在给大家看看每个排序的对比吧(10w个数据排序,单位是ms):

        再看希尔排序、堆排序和快速排序的对比(100w个数据):

        1000w个数据:

         这样我们就可以清晰地看到每个排序的不同的用法和性能差距

       最后还剩下一个归并排序,下期在给大家补吧,最近期末考啦,狐狸要忙着速成捏~

        886~记得给个三连QAQ

Read more

Android WebView 版本升级方案详解

Android WebView 版本升级方案详解 目录 1. 问题背景 2. WebViewUpgrade 项目介绍 3. 升级方法详解 4. 替代方案对比 5. 接入与使用步骤 6. 注意事项与限制 7. 总结与建议 问题背景 WebView 版本差异带来的问题 Android 5.0 以后,WebView 升级需要去 Google Play 安装 APK,但即使安装了也不一定能正常工作。像华为、Amazon 等特殊机型的 WebView 的 Chromium 版本一般比较低,只能使用它自己的 WebView,无法使用 Google 的 WebView。 典型问题场景 H.265 视频播放问题:

By Ne0inhk
AI Skills:前端新的效率神器

AI Skills:前端新的效率神器

近来,AI 领域有个火爆的话题:Skills。 Github 上被疯狂 star 的仓库,很多都是和 skills 有关的。 有的仓库仅仅上线三个月就获得了快 50K 的 star,Skills 的火热可见一斑。 不管是大模型,还是 Cursor、Codex、Claude、Trae、Copilot 等编程 IDE 都在争先支持 Skills。 围绕 Skills,它们在做的就是为了完成一件事情:技能是通过学习和反复练习获得的,而 Skills 是把经验和最佳实践沉淀为 AI 能力,将“知道”转化为“做到”的本领。 详解什么是 Skills 要说清楚什么是 Skills,先来了解一下关于 AI 的 2

By Ne0inhk
自动化打造信息影响力:用 Web Unlocker 和 n8n 打造你的自动化资讯系统

自动化打造信息影响力:用 Web Unlocker 和 n8n 打造你的自动化资讯系统

一、研究背景 在信息爆炸的时代,及时获取高质量行业资讯成为内容创作者、运营者以及研究者的刚需。无论是IT、AI领域的技术动态,还是招聘、人才市场的趋势新闻,第一时间掌握热点、总结观点并进行内容输出,正逐渐成为提升影响力与构建个人/组织品牌的关键手段。 为实现“日更内容”目标,很多人开始探索自动化的路径——使用爬虫工具定期抓取目标网站内容,借助 AI 模型自动生成摘要,再将结果推送至社群平台。这一流程的核心,是稳定、高效地获取网页数据,在实际操作中,却出现了很多问题: * 首先是出现了验证码,阻断自动化流程; * 紧接着是请求返回403 Forbidden,提示IP被封; * 最终是目标网站直接对我们常用IP段进行了临时封禁,哪怕切换机器或重启网络都无济于事。 按照检查方法,当处于非爬虫操作时,我们在F12控制台输入window.navigator.webdriver时,显示的是false,输入进去出现了刺眼的红色报错,而且显示也出现了True, “Failed to load resource: the server responded with

By Ne0inhk

B站PC端web自动开启字幕脚本(2025新版适配)

B站自动字幕用户脚本:快捷键开关 + 自动开启字幕(2026新版适配) 作者:Apixus 更新日期:2026年3月5日 项目地址:GitHub仓库 一、脚本介绍 你是否经常在B站看视频时反复手动开启字幕?是否希望切换视频时字幕能自动开启? 这个用户脚本就是为了解决这些问题而开发的。 B站自动字幕脚本 提供了以下功能: * 🎯 快捷键控制:按 C 键快速开启或关闭字幕 * 🔄 自动开启:切换分P、点击推荐视频时自动打开字幕 * 🆕  2026新版适配:专为B站最新版播放器优化 * ⚡ 性能优化:智能监听,告别卡顿轮询 * 🛡️ 防冲突:自动识别输入框,避免误触 二、适用页面 * 普通视频页:https://www.bilibili.com/video/* * 播放列表页:https://www.bilibili.com/list/* 支持普通视频页、番剧页、播放列表页等常见场景。 三、

By Ne0inhk