1.引言
在计算机科学中,排序算法是基础且重要的一部分,它们在数据处理、数据库管理、算法设计等多个领域发挥着关键作用。插入排序作为一种简单直观的排序算法,非常适合小规模或者部分已排序的数据集。本文将深入探讨插入排序的工作原理、算法实现步骤、代码实现以及其性能分析。
2.什么是插入排序?
插入排序是一种简单直观的排序算法,它的工作机制类似于我们手动整理一副扑克牌的过程:想象你手里拿着一叠未排序的卡片,每次从这叠卡片中取出一张,然后插入到已经排好序的另一叠卡片中的正确位置。这个过程重复进行,直到所有卡片都被插入到正确的位置,最终形成一个有序序列。
3.算法实现步骤
3.1 初始化:假设数组的第一个元素已经被排序。
3.2 遍历数组:从第二个元素开始,对每个元素执行以下操作:
- a.取出当前元素,在已排序序列中从后向前扫描。
- b.如果当前元素(已取出的待插入元素)小于它前面的某个元素,则将该元素(及它之后的所有元素)向后移动一位,为当前元素腾出空间。
- c.将当前元素插入到已排序序列中的适当位置。
3.3 重复步骤2,直到整个数组都有序。
4.算法动态演示
5.Java代码实现
5.1.优化之前:
public class InsertSort {
/**
* 交换方法:交换一个整型数组中指定下标的两个元素。
*
* @param array 需要进行元素交换的整形数组
* @param first 需要交换的第一个元素的下标。
* @param second 需要交换的第二个元素的下标。
*/
public static void swap(int[] array, int first, int second) {
// 用临时变量存储array数组中第second个元素的值
int temp = array[second];
// 将array数组中第second个元素的值更新为第first个元素的值
array[second] = array[first];
// 将array数组中第first个元素的值更新为temp变量的值。
array[first] = temp;
}
/**
* 打印方法:打印整型数组元素
*
* @param array 等待打印的整型数组
*/
public static void printArray(int[] array) {
for (int j : array) {
System.out.print(j + " ");
}
System.out.println();
}
/**
* 插入排序:对传入的整型数组进行排序-->从小到大
*
* @param array 等待传递的整型数组
*/
public static void insertionSort(int[] array) {
// 考虑边界情况: 若只有一个元素or没有元素,则不进行排序
if (array == null || array.length < 2) {
return;
}
// 获取数组长度
int n = array.length;
// 0 ~ 0 完成 | 0 ~ 1 | 0 ~ 2 | 0 ~ 3 | 0 ~ n-1
for (int end = 1; end < n; end++) {
int newNumberIndex = end;
// 如果当前所在位置的前一个位置,存在元素,即左边有数值。并且左边的数值比右边的数值大,就需要交换。
while (newNumberIndex - 1 >= 0 && array[newNumberIndex - 1] > array[newNumberIndex]) {
// 交换:新插入进来的数值往左移动
swap(array, newNumberIndex - 1, newNumberIndex);
// 离开原来的位置,往左移动
newNumberIndex--;
}
}
}
public static void main(String[] args) {
int[] exampleArray = {66, 33, 22, 11, 22, 44, 99};
System.out.println("********未经过插入排序前的元素******");
printArray(exampleArray);
// 执行排序
insertionSort(exampleArray);
System.out.println("********经过插入排序后的元素******");
printArray(exampleArray);
}
}
最终打印的结果如下:
5.2 优化之后:
public class InsertSort {
/**
* 交换方法:交换一个整型数组中指定下标的两个元素。
*
* @param array 需要进行元素交换的整形数组
* @param first 需要交换的第一个元素的下标。
* @param second 需要交换的第二个元素的下标。
*/
public static void swap(int[] array, int first, int second) {
// 用临时变量存储array数组中第second个元素的值
int temp = array[second];
// 将array数组中第second个元素的值更新为第first个元素的值
array[second] = array[first];
// 将array数组中第first个元素的值更新为temp变量的值。
array[first] = temp;
}
/**
* 打印方法:打印整型数组元素
*
* @param array 等待打印的整型数组
*/
public static void printArray(int[] array) {
for (int j : array) {
System.out.print(j + " ");
}
System.out.println();
}
/**
* 插入排序:对传入的整型数组进行排序-->从小到大
*
* @param array 等待传递的整型数组
*/
public static void insertSort(int[] array) {
// 考虑边界情况: 若只有一个元素or没有元素,则不进行排序
if (array == null || array.length < 2) {
return;
}
// 获取数组的长度
int n = array.length;
// 0 ~ 0 完成 | 0 ~ 1 | 0 ~ 2 | 0 ~ 3 | 0 ~ n-1
for (int end = 1; end < n; end++) {
// pre 新元素的前一个位置,前一个位置的元素比后一个位置的元素大,就交换
for (int pre = end - 1; pre >= 0 && array[pre] > array[pre + 1]; pre--) {
swap(array, pre, pre + 1);
}
}
}
public static void main(String[] args) {
int[] exampleArray = {66, 33, 22, 11, 22, 44, 99};
System.out.println("********未经过插入排序前的元素******");
printArray(exampleArray);
// 执行排序
insertSort(exampleArray);
System.out.println("********经过插入排序后的元素******");
printArray(exampleArray);
}
}
最终打印的结果如下:
6.性能分析
- 时间复杂度:
a.最好情况(输入数组已经是排序好的):O(n),因为此时不需要进行内部的while循环。
b.平均情况和最坏情况(数组逆序):O(n^2),因为每插入一个新元素都需要对之前的元素进行比较和可能的移动。
- 空间复杂度:
O(1),因为插入排序是原地排序,除了几个用于交换的临时变量外,不需要额外的存储空间。
- 稳定性:
插入排序是稳定的排序算法,即相等的元素的相对顺序不会改变。
7.应用场景
由于插入排序在小规模数据或基本有序的数据集上表现良好,它常被用作更复杂排序算法(如快速排序、归并排序)的子程序,尤其是在分治策略中的排序初始阶段或合并过程中。此外,对于实时数据流的排序,插入排序因其简单和低内存消耗而显得尤为合适。
8.总结
插入排序以其简单直接的逻辑和对小规模数据集的高效处理能力,在特定场景下仍具有不可忽视的价值。尽管在大数据集上不如更高效的算法,但理解其原理和实现细节对于深入学习排序算法及其应用至关重要。希望本文能够帮助你更好地掌握插入排序算法,并启发你在实际问题解决中的思考。