排序算法(1)-插入排序【史上最保姆级的教程】

插入排序:原理、步骤与代码详解

目录

前言

一、插入排序的基本原理

二、插入排序的步骤示例

初始

先分两个部分

取未排序部分第一个元素

存储当前元素

比较

交换

三、代码以及解释

for (i = 1; i < n; i++) 

key = arr[i]

j=i-1

while(j>=0 && arr[j]>key)

arr[j+1] = arr[j]

j--

arr[j+1] = key

完整代码示例


前言

几大经典排序法的各种情况用一张图概括:

这里我们第一个先介绍插入排序。

一、插入排序的基本原理

可以对着动态示意图看着文字解释:

  1. 初始状态
    将待排序序列的第一个元素视为已排序部分(上图中的黄色部分)
    其余元素为未排序部分(蓝色部分)。
    上图中数组 {5, 2, 4, 6, 1, 3}
    第一步,首先将数组的第一个元素,也就是下标、索引为0的数--{5}看成已排序的部分
    剩下未排序部分为 {2, 4, 6, 1, 3}
    则数组{5, 2, 4, 6, 1, 3}

  2. 逐步插入
    从未排序部分取出第一个元素作为当前元素,将其与已排序部分的元素从后往前比较。
    如果当前元素比已排序部分的某个元素小,则将已排序部分的元素后移一位,腾出位置。
    重复上述步骤,直到找到当前元素的合适插入位置。

  3. 最终结果: 所有元素都被插入到已排序部分后,整个序列变为有序。

简单来说

  1. 从第一个元素开始,该元素可以认为已经被排序。

  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描

  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。

  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。

  5. 将新元素插入到该位置后。

  6. 重复步骤2-5。


二、插入排序的步骤示例

如上面动态图所示,这里我用文字形式方便大家阅读:

初始

最开始拿到下面这个数组
{5, 2, 4, 6, 1, 3}

先分两个部分

分为已排序和未排序两个部分。

将数组第一个元素看成已排序部分(橙色),剩下的为未排序部分(蓝色)
{5, 2, 4, 6, 1, 3}

取未排序部分第一个元素

取未排序部分的第一个元素作为“当前元素”。这个当前元素和已排序部分的最后一个元素比较。
他们俩的关系其实就是靠在一块的,已排序部分的最后一个元素  的  下一个元素 一定是 未排序部分的第一个元素。
这里当前元素是 2 ,未排序部分因为现在只有一个数,就是5。

存储当前元素

将当前元素 2 存储在一个中间变量 key 当中。这时候原来 2 的位置就是空的。如下:
{5,[空],4, 6, 1, 3}
也就是数组索引为 1 的位置是空的。2存在变量key当中。

比较

现在让key和已排序部分比较,从已排序部分最后一个数字开始比较。这里只有5。key是 2。
所以 5 > key

所以要交换元素

交换

现在数组
{5,[空],4, 6, 1, 3}
让 5 去 空 的那个位置。
{[空],5,4, 6, 1, 3}
这时候原来 5 的位置,即索引为 0 的位置是空的。

现在将存在 key 中的 2 赋值给空出来的那个位置。数组变为下面的样子:
{2,5,4, 6, 1, 3}

这样就完成了一次排序,重复上面的过程,到最后就可以得到正确的排序。


三、代码以及解释

 void insertionSort(int arr[], int n) 
 {
     int i, j, key;
     for (i = 1; i < n; i++) 
     {
         key = arr[i];  // 当前待排序元素
         j = i - 1;     // 已排序部分的最后一个元素索引
         
         // 在已排序部分中寻找插入位置
         while (j >= 0 && arr[j] > key) 
         {c
             arr[j + 1] = arr[j];  // 元素后移
             j--;
         }
         arr[j + 1] = key;  // 插入到正确位置
     }
 }

咱们一行一行解释:

for (i = 1; i < n; i++) 

第四行中,循环从‘i=1’开始,因为刚开始就将待排序序列的第一个元素(索引为i=0)视为“已排序部分”,所以未排序部分是从索引“i=1”开始的元素,也就是第二个数字。 

数组元素 :{5,[空],4, 6, 1, 3}
下标(i/j):[0,1 ,2,3,4,5]

初始时已排序部分只有 {5},未排序部分为 {2, 4, 6, 1, 3}

i=0 就是{5}    i=1 就是{2}

从索引i=1开始循环直到数组末尾(i < n

key = arr[i]

第六行key = arr[i]; 就是将当前待排序元素的值保存到 key 变量中

数组元素 :{5,[空],4, 6, 1, 3}
下标(i/j):[0,1 ,2,3,4,5]

i=1的时候,arr[i] 就是2,也就是将 2 存放在变量key中。 这时,arr[1]就是一个空壳子,里面没有数值了。

数组元素:{5,[空],4, 6, 1, 3}
下标(i) :[0,1 ,2,3,4,5]
key = 2

需要注意的是,代码中的  i  永远表示待排序部分的第一个,那么 i 前一个就是 i-1,就是已排序部分的 最后一个,在下面我们使用 j 表示。

j=i-1

第7行 j=i-1; 这里j 指向已排序部分的最后一个元素(当前元素的前一个位置)

while(j>=0 && arr[j]>key)

第十行 while (j >= 0 && arr[j] > key) 是说在 j 的值大于等于0,并且 arr[j] 大于 key的值 对于数组{5,[空],4, 6, 1, 3},在刚刚 i=1时候,j = 0
那么 arr[j]=arr[0]=5
则   key = arr[i] = arr[1] = 2
由于arr[1]已经赋值给key,现在就是一个空壳子,里面没有数值了。=

他们的作用:

  • 条件 j >= 0:确保不越界(不到达数组开头之前)

  • 条件 arr[j] > key:当前比较元素大于待插入元素时继续循环

arr[j+1] = arr[j]

第十二行arr[j + 1] = arr[j]; 是将较大的元素向右移动一位,为 key 腾出插入空间

对于数组
数组元素 :{5,[空],4, 6, 1, 3}
下标(i/j):[0,1 ,2,3,4,5]

 

同样i=1时候,j = 0
arr[j+1]=arr[j] 就是 arr[1]=arr[0]
arr[1]已经赋值给key,现在就是一个空壳子
现在 arr[0]的数值 5 给了这个空壳子,也就是就是将数字5赋值给arr[1]
那么数组变成下面这样:

数组元素 :{[空],5,4, 6, 1, 3}
下标(i/j):[ 0 ,1,2,3,4,5]
key = 2

这样数字5从原来序列的第一个变成序列的第二个
索引为 0 的元素变成空壳,下面用来放 key 的值达到交换让索引 0 和 1 的值交换的目的。

j--

第十三行j--; 将内层循环索引向左移动,继续与前一个元素比较。

注意,j原来是j = i-1 = 0j--之后 j = -1
也就是现在 j = -1

arr[j+1] = key

第十五行arr[j + 1] = key;key的值赋值给arr[j+1]
key是从arr[1]里面拿的2,
现在把2赋值给arr[j+1]
注意现在j=-1,所以arr[j+1] = arr[0]
就是将 赋值给arr[0]

现在的序列是: 

数组元素 :{2,5,4, 6, 1, 3}
下标(i/j):[0,1,2,3,4,5]

这样就完成了一次排序 接着让 i=2 循环多次,就可以排序完成。

 

完整代码示例

#include <stdio.h>

void insertionSort(int arr[], int n) // n 是数组的长度
{
    int i, j, key;
    for (i = 1; i < n; i++) 
    {
        key = arr[i];  // 当前待排序元素
        j = i - 1;     // 已排序部分的最后一个元素索引
        
        // 在已排序部分中寻找插入位置
        while (j >= 0 && arr[j] > key) 
        {
            arr[j + 1] = arr[j];  // 元素后移
            j--;
        }
        arr[j + 1] = key;  // 插入到正确位置
    }
}

int main() 
{
    int arr[] = {12, 11, 13, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
    
    printf("排序前: ");
    for (int i = 0; i < n; i++) 
    {
        printf("%d ", arr[i]);
    }
    
    insertionSort(arr, n);
    
    printf("\n排序后: ");
    for (int i = 0; i < n; i++) 
    {
        printf("%d ", arr[i]);
    }
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DonisLi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值