手撕排序算法:插入排序


插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入,直到整个数组有序。

1.算法思想

第一步:从第一个元素开始,将其视为已排序部分。
第二步:取出下一个元素,与已排序部分的元素进行比较。
第三步:如果该元素小于已排序部分的最后一个元素,则将其插入到已排序部分的适当位置。
重复步骤二和三,直到整个数组都被排序。

2.算法分析

2.1时间复杂度

我们假设「比较」和「移动」的时间复杂度为O(1)的。
「插入排序」中有两个嵌套循环。
外循环正好运行一1次迭代。但内部循环运行变得越来越短:
当i=1,内层循环1次「比较」操作。
当i=2,内层循环2次「比较」操作。
当i=3,内层循环3次「比较」操作。
当i=n-2,内层循环n一2次「比较」操作。
当i=n-1,内层循环n-1次「比较」操作。
因此,总「比较」次数如下:1+2+…+(n一1)=n(n-1)/2。总的时间复杂度为:O(n^2)。

2.2 空间复杂度

由于算法在执行过程中,只有「移动」变量时候,需要事先将变量存入临时变量X,而其它没
有采用任何的额外空间,所以空间复杂度为O(1)。

3.算法的优缺点

3.1 优点

1.简单易懂,易于实现。
2.适用于小型数组或基本有序的数组。
3.稳定性好不会改变

3.2缺点

1.对于大型无序数组,效率较低。
2.不适合大规模数据排序。

4.优化方案

「插入排序」在众多排序算法中效率较低,时间复杂度为O(2)。
考虑,在进行插入操作之前,我们找位置的过程是在有序数组中找的,所以可以利用「二分查
找」来找到对应的位置。然而,执行「插入」的过程还是O(),所以优化的也只是常数时
间,最坏时间复杂度是不变的。
「改进思路」执行插入操作之前利用「二分查找」来找到需要插入的位置。

5.代码实现

void insertsort(int* nums,int numsSize) {  
    for (int i = 1; i < numsSize; i++) {  
        int key = nums[i];  // 记录当前要插入的元素  
        int j = i - 1;  
        while (j >= 0 && nums[j] > key) {  
            nums[j+1] = nums[j];  
            j--;  
        }  
        nums[j + 1] = key;  
    }  
}
  1. 主循环 (for 循环):
    • 从 i = 1 开始遍历数组,因为第一个元素可以视为已排序的部分。
    • key = nums[i] 表示当前要插入的元素。
  2. 插入过程 (while 循环):
    • j = i - 1 初始化为当前元素的前一个位置,即已排序部分的末尾。
    • while (j >= 0 && nums[j] > key) 循环条件是 j 大于等于 0 并且当前位置的元素大于 key
    • 在循环内部,将比 key 大的元素向右移动一个位置,为 key 腾出插入的空间。
  3. 插入操作
    • nums[j + 1] = key 将 key 插入到合适的位置,即 j + 1 处。
  4. 循环结束
    • 当内部的 while 循环条件不满足时,表示已经找到了 key 的正确位置,此时将 key 插入到 nums[j + 1] 处。

6.实战

6.1力扣1491.去掉最低工资和最高工资后的工资平均值

给你一个整数数组salary,,数组里每个数都是唯一的,其中salary[i]是第i个员工的工资。请你返回去掉最低工资和最高工资以后,剩下员工工资的平均值。

void insertsort(int* nums,int numsSize) {  
    for (int i = 1; i < numsSize; i++) {  
        int key = nums[i];  // 记录当前要插入的元素  
        int j = i - 1;  
        while (j >= 0 && nums[j] > key) {  
            nums[j+1] = nums[j];  
            j--;  
        }  
        nums[j + 1] = key;  
    }  
}  
double average(int* salary, int salarySize) {  
    insertsort(salary,salarySize);  
    int sum = 0;  
    for(int i = 1; i < salarySize - 1; i++){  
        sum += salary[i];  
    }  
    return (double)sum / (salarySize - 2);  
}

6.2力扣1619.删除某些元素后的数组均值

给你一个整数数组「r,请你删除最小5%的数字和最大5%的数字后,剩余数字的平均值。
与标准答案误差在105的结果都被视为正确结果。

void insertsort(int* nums,int numsSize) {  
    for (int i = 1; i < numsSize; i++) {  
        int key = nums[i];  // 记录当前要插入的元素  
        int j = i - 1;  
        while (j >= 0 && nums[j] > key) {  
            nums[j+1] = nums[j];  
            j--;  
        }  
        nums[j + 1] = key;  
    }  
}  
double trimMean(int* arr, int arrSize) {  
    insertsort(arr,arrSize);  
    int sum = 0;  
    int count = 0;  
    for(int i = arrSize / 20; i < arrSize - arrSize / 20; i++){  
        sum += arr[i];  
        count++;  
    }  
    return (double)sum / count;  
}

6.3 力扣1984.学生分数的最小差值

给你一个下标从0开始的整数数组nums,其中nums[i]表示第i名学生的分数。另给你一个整数k。
从数组中选出任意k名学生的分数,使这k个分数间最高分和最低分的差值达到最小化。
返回可能的最小差值。

void insertsort(int* nums,int numsSize) {  
    for (int i = 1; i < numsSize; i++) {  
        int key = nums[i];  // 记录当前要插入的元素  
        int j = i - 1;  
        while (j >= 0 && nums[j] > key) {  
            nums[j+1] = nums[j];  
            j--;  
        }  
        nums[j + 1] = key;  
    }  
}  
int minimumDifference(int* nums, int numsSize, int k) {  
    insertsort(nums,numsSize);  
    int ret = 10000000;  
    for(int i = 0; i + k - 1 < numsSize; i++){  
        int l = i;  
        int r = i + k - 1;  
        if(nums[r] - nums[l] < ret){  
            ret = nums[r] - nums[l];  
        }  
    }  
    return ret;  
}
posted @ 2024-07-15 01:00  写代码的大学生  阅读(8)  评论(0编辑  收藏  举报  来源