❤️五万字《十大排序算法》动图讲解❤️(建议收藏)
Posted 英雄哪里出来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️五万字《十大排序算法》动图讲解❤️(建议收藏)相关的知识,希望对你有一定的参考价值。
今天的内容,将围绕这几张动图来展开。可以大致先简单看一下,这是一个归并排序的动图演示,我会对以上几个排序从 算法原理、动图详解 讲到 C语言 的 源码分析。
零、算法概述 今天要讲的内容是 「 十大排序算法 」。各个排序算法中的思想都非常经典,如果能够一一消化,那么在学习算法的路上也会轻松许多。
相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,没错!利用这个时间 「 学好算法 」,三年后的你自然「 不能同日而语 」。
那么这里,我整理了「 几十个基础算法 」 的分类,有需要可以找我领取。大致一览:
🔥让天下没有难学的算法🔥
C语言免费动漫教程,和我一起打卡! 🌞《光天化日学C语言》🌞
入门级C语言真题汇总 🧡《C语言入门100例》🧡
几张动图学会一种数据结构 🌳《画解数据结构》🌳
组团学习,抱团生长 🌌《算法入门指引》🌌
竞赛选手金典图文教程 💜《夜深人静写算法》💜
一、插入排序 「 插入排序 」 是比较好理解且编码相对简单的排序算法,虽然效率不是很高。
一、🎯简单释义
1、算法目的
将原本乱序的数组变成有序,可以是 「升序」 或者 「降序」 (为了描述统一,本文一律只讨论 「 升序」 的情况)。
2、算法思想
通过不断将当前元素 「插入」 到 「升序」 序列中,直到所有元素都执行过 「插入」 操作,则算法结束。
3、命名由来
每次都是将元素 「插入」 到 有序 序列中,故此命名 「 插入排序 」 。
二、🧡核心思想
- 「迭代」:类似的事情,不停地做。
- 「比较」:关系运算符 小于等于( ≤ \\le ≤) 的运用。
- 「移动」:原地后移元素。
三、🔆动图演示
1、样例
8 | 5 | 6 | 4 | 3 | 7 | 10 | 2 |
- 初始情况下的数据如 图二-1-1 所示,基本属于乱序,纯随机出来的数据。
2、算法演示
- 接下来,我们来看下排序过程的动画演示。如 图二-2-1 所示:
3、样例说明
图示 | 含义 |
---|---|
■ 的柱形 | 代表尚未排好序的数 |
■ 的柱形 | 代表正在执行 比较 和 移动 的数 |
■ 的柱形 | 代表已经排好序的数 |
■ 的柱形 | 代表待执行插入的数 |
我们看到,首先需要将 「第二个元素」 和 「第一个元素」 进行 「比较」,如果 前者 小于等于 后者,则将 后者 进行向后 「移动」,前者 则执行插入;
然后,进行第二轮「比较」,即 「第三个元素」 和 「第二个元素」、「第一个元素」 进行 「比较」, 直到 「前三个元素」 保持有序 。
最后,经过一定轮次的「比较」 和 「移动」之后,一定可以保证所有元素都是 「升序」 排列的。
四、🌳算法前置
1、循环的实现
- 这个算法本身需要做一些「 循环 」进行迭代计算,所以你至少需要知道「 循环 」 的含义,这里以 「 c++ 」 为例,来看下一个简单的「 循环 」是怎么写的。代码如下:
int n = 520;
for(int i = 0; i < n; ++i) {
// TODO : 。。。
}
- 这个语句就是一个最简单的循环语句,它会将循环体内的语句执行 n n n 次,而这里的 n n n 等于 1314 1314 1314,也就是会执行 1314 1314 1314 次。
2、比较的实现
- 「比较」两个元素的大小,可以采用关系运算符,本文我们需要排序的数组是按照 「升序」 排列的,所以用到的关系运算符是 「小于等于运算符(即 <=)」 。
- 我们可以将两个数的「比较」写成一个函数
smallerEqualThan
,以 「 c++ 」 为例,实现如下:
#define Type int
bool smallerEqualThan(Type a, Type b) {
return a <= b;
}
- 其中
Type
代表数组元素的类型,可以是整数,也可以是浮点数,也可以是一个类的实例,这里我们统一用int
来讲解,即 32位有符号整型。
3、移动的实现
- 所谓「移动」,其实是将某个元素执行后移,实现如下:
a[j + 1] = a[j];
五、🥦算法描述
1、问题描述
给定一个 n n n 个元素的数组,数组下标从 0 0 0 开始,采用「 插入排序 」将数组按照 「升序」排列。
2、算法过程
整个算法的执行过程分以下几步:
1) 循环迭代变量 i = 1 → n − 1 i = 1 \\to n-1 i=1→n−1;
2) 每次迭代,令 x = a [ i ] x = a[i] x=a[i], j = i − 1 j = i-1 j=i−1,循环执行比较 x x x 和 a [ j ] a[j] a[j],如果产生 x ≤ a [ j ] x \\le a[j] x≤a[j] 则执行 a [ j + 1 ] = a [ j ] a[j+1] = a[j] a[j+1]=a[j]。然后执行 j = j + 1 j = j + 1 j=j+1,继续执行 2);否则,跳出循环,回到 1)。
六、🧶算法分析
1、时间复杂度
- 我们假设 「比较」 和 「移动」 的时间复杂度为 O ( 1 ) O(1) O(1)。
- 「 插入排序 」 中有两个嵌套循环。
外循环正好运行 n − 1 n-1 n−1 次迭代。 但内部循环运行变得越来越短:
当 i = 1 i = 1 i=1,内层循环 1 1 1 次「比较」操作。
当 i = 2 i = 2 i=2,内层循环 2 2 2 次「比较」操作。
当 i = 3 i = 3 i=3,内层循环 3 3 3 次「比较」操作。
……
当 i = n − 2 i = n-2 i=n−2,内层循环 n − 2 n-2 n−2 次「比较」操作。
当 i = n − 1 i = n-1 i=n−1,内层循环 n − 1 n-1 n−1 次「比较」操作。
- 因此,总「比较」次数如下:
- 1 + 2 + . . . + ( n − 1 ) = n ( n − 1 ) 2 1 + 2 + ... + (n-1) = \\frac {n(n-1)}{2} 1+2+...+(n−1)=2n(n−1)
- 总的时间复杂度为: O ( n 2 ) O(n^2) O(n2)
2、空间复杂度
- 由于算法在执行过程中,只有「移动」变量时候,需要事先将变量存入临时变量
x
,而其它没有采用任何的额外空间,所以空间复杂度为 O ( 1 ) O(1) O(1)。
七、🧢优化方案
「 插入排序 」在众多排序算法中效率较低,时间复杂度为 O ( n 2 ) O(n^2) O(n2) 。
想象一下,当有 n = 1 0 5 n = 10^5 n=105 个数字。 即使我们的计算机速度超快,并且可以在 1 秒内计算 1 0 8 10^8 108 次操作,但冒泡排序仍需要大约一百秒才能完成。
考虑,在进行插入操作之前,我们找位置的过程是在有序数组中找的,所以可以利用「二分查找」 来找到对应的位置。然而,执行 「 插入 」 的过程还是 O ( n ) O(n) O(n),所以优化的也只是常数时间,最坏时间复杂度是不变的。
- 「改进思路」执行插入操作之前利用 「 插入 」 来找到需要插入的位置。
八、💙源码详解
#include <stdio.h>
int a[1010];
void Input(int n, int *a) {
for(int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
}
void Output(int n, int *a) {
for(int i = 0; i < n; ++i) {
if(i)
printf(" ");
printf("%d", a[i]);
}
puts("");
}
void InsertSort(int n, int *a) { // (1)
int i, j;
for(i = 1; i < n; ++i) {
int x = a[i]; // (2)
for(j = i-1; j >= 0; --j) { // (3)
if(x <= a[j]) { // (4)
a[j+1] = a[j]; // (5)
}else
break; // (6)
}
a[j+1] = x; // (7)
}
}
int main() {
int n;
while(scanf("%d", &n) != EOF) {
Input(n, a);
InsertSort(n, a);
Output(n, a);
}
return 0;
}
-
(
1
)
(1)
(1)
void InsertSort(int n, int *a)
为 插入排序 的实现,代表对a[]
数组进行升序排序。 -
(
2
)
(2)
(2) 此时
a[i]
前面的i-1
个数都认为是排好序的,令x = a[i]
; - ( 3 ) (3) (3) 逆序的枚举所有的已经排好序的数;
-
(
4
)
(4)
(4) 如果枚举到的数
a[j]
比需要插入的数x
大,则当前数往后挪一个位置; - ( 5 ) (5) (5) 执行挪位置的 O ( 1 ) O(1) O(1) 操作;
- ( 6 ) (6) (6) 否则,跳出循环;
-
(
7
)
(7)
(7) 将
x
插入到合适位置;
- 关于 「 插入排序 」 的内容到这里就结束了。
二、冒泡排序 想要养成 「算法思维」,每一个简单的问题都要思考它背后的真正含义,做到 举一反三,触类旁通。
「 冒泡排序 」 是最好理解且编码最简单的排序算法。
一、🎯简单释义
1、算法目的
将原本乱序的数组变成有序,可以是 「升序」 或者 「降序」 (为了描述统一,本文一律只讨论 「 升序」 的情况)。
2、算法思想
通过不断比较相邻的元素,如果「左边的元素」 大于 「右边的元素」,则进行「交换」,直到所有相邻元素都保持升序,则算法结束。
3、命名由来
数值大的元素经过交换,不断到达数组的尾部,就像气泡,逐渐浮出水面一样,故此命名 「 冒泡排序 」 。
二、🧡核心思想
- 「迭代」:类似的事情,不停地做。
- 「比较」:关系运算符 大于( > \\gt >) 的运用。
- 「交换」:变量或者对象的值的互换。
三、🔆动图演示
1、样例
8 | 5 | 6 | 4 | 3 | 7 | 10 | 2 |
- 初始情况下的数据如 图二-1-1 所示,基本属于乱序,纯随机出来的数据。
2、算法演示
- 接下来,我们来看下排序过程的动画演示。如 图二-2-1 所示:
3、样例说明
图示 | 含义 |
---|---|
■ 的柱形 | 代表尚未排好序的数 |
■ 的柱形 | 代表正在执行比较的两个数 |
■ 的柱形 | 代表已经排好序的数 |
我们看到,首先需要将 「第一个元素」 和 「第二个元素」 进行 「比较」,如果 前者 大于 后者,则进行 「交换」,然后再比较 「第二个元素」 和 「第三个元素」 ,以此类推,直到 「最大的那个元素」 被移动到 「最后的位置」 。
然后,进行第二轮「比较」,直到 「次大的那个元素」 被移动到 「倒数第二的位置」 。
最后,经过一定轮次的「比较」 和 「交换」之后,一定可以保证所有元素都是 「升序」 排列的。
四、🌳算法前置
1、循环的实现
- 这个算法本身需要做一些「 循环 」进行迭代计算,所以你至少需要知道「 循环 」 的含义,这里以 「 c++ 」 为例,来看下一个简单的「 循环 」是怎么写的。代码如下:
int n = 520;
for(int i = 0以上是关于❤️五万字《十大排序算法》动图讲解❤️(建议收藏)的主要内容,如果未能解决你的问题,请参考以下文章
❤️五万字❤️离职后一天4面,总结了204道高频Java面试题,已拿阿里offer(建议收藏)
❤️[数据结构]动图+万字详解栈和队列(动图+实例)建议收藏❤️
❤️五万字C语言入门图文教材,写到最后我电脑卡爆了❤️(建议收藏)