【算法系列 | 2】深入解析排序算法之—插入排序

============================================================================================================================================================================

序言

你只管努力,其他交给时间,时间会证明一切。

决定开一个算法专栏,希望能帮助大家很好的了解算法。主要深入解析每个算法,从概念到示例。

我们一起努力,成为更好的自己!

今天第二讲,讲一下排序算法的插入排序

一、算法介绍

1.1 原理介绍

插入排序是一种简单直观的排序算法,它的基本思想是将一个元素插入到已经排好序的部分数组中,从而逐步扩大有序部分。

详细一点解释:

是将一个数组分成已排序和未排序两部分,初始时已排序部分只有一个元素,然后将未排序部分的每个元素插入到已排序部分的适当位置,直到所有元素都被排序。

当将一个元素插入到已排序部分时,需要将已排序部分中所有大于该元素的元素向后移动一位,然后将该元素插入到合适的位置。

具体实现时,可以使用两个嵌套的循环。外层循环遍历未排序部分中的每个元素,内层循环从待插入元素的前一个位置开始,向前遍历已排序部分,直到找到第一个小于等于待插入元素的位置,然后将待插入元素插入到该位置之后。

下面是一个伪代码实现:

css 复制代码
for i from 1 to n-1 do
    j = i
    while j > 0 and A[j-1] > A[j] do
        swap A[j] and A[j-1]
        j = j - 1

其中,变量 i 用于遍历未排序部分,变量 j 指向待插入元素在已排序部分中的位置,循环内部的 while 循环用于将待插入元素插入到正确的位置。

举个例子

下面通过一个简单的例子来图解插入排序的过程。

考虑数组 [5, 2, 4, 6, 1, 3],我们将逐步对其进行插入排序:

初始状态:

csharp 复制代码
[5, 2, 4, 6, 1, 3]

第一轮:

将第二个元素 2 插入到已排序部分 [5] 中,得到 [2, 5, 4, 6, 1, 3]

csharp 复制代码
[2, 5, 4, 6, 1, 3]

第二轮:

将第三个元素 4 插入到已排序部分 [2, 5] 中,得到 [2, 4, 5, 6, 1, 3]

csharp 复制代码
[2, 4, 5, 6, 1, 3]

第三轮:

将第四个元素 6 插入到已排序部分 [2, 4, 5] 中,得到 [2, 4, 5, 6, 1, 3]

csharp 复制代码
[2, 4, 5, 6, 1, 3]

第四轮:

将第五个元素 1 插入到已排序部分 [2, 4, 5, 6] 中,得到 [1, 2, 4, 5, 6, 3]

csharp 复制代码
[1, 2, 4, 5, 6, 3]

第五轮:

将最后一个元素 3 插入到已排序部分 [1, 2, 4, 5, 6] 中,得到 [1, 2, 3, 4, 5, 6]

csharp 复制代码
[1, 2, 3, 4, 5, 6]

最终,数组已经排好序。每一轮都是将一个元素插入到有序的部分,逐步扩大有序部分,直到整个数组有序。这就是插入排序的基本过程。

动图示例

1.2 优缺点

优点:

  1. 简单易理解: 插入排序的实现非常简单,容易理解,适用于初学者学习算法的入门。

  2. 稳定性: 插入排序是一种稳定的排序算法,相等元素的相对位置不会改变。

  3. 对小数据量有效: 在数据量较小的情况下,插入排序的性能通常比其他高级排序算法(如快速排序、归并排序)更好,因为其常数因子较小。

  4. 部分有序数组: 当数组中的数据接近有序状态时,插入排序的性能较好,因为插入的元素往往只需要移动较小的距离。

缺点:

  1. 时间复杂度较高: 插入排序的平均时间复杂度为O(n^2),在数据规模较大时,性能不如快速排序、归并排序等O(n log n)复杂度的排序算法。

  2. 对逆序数组效率低: 当数组完全逆序时,每次插入都需要移动大量元素,导致性能下降。

  3. 不适合大规模数据: 插入排序的性能在处理大规模数据时较差,不如一些更高效的排序算法。

  4. 不适合链表: 插入排序对于链表的操作相对较复杂,因为需要频繁修改指针指向,相比于数组实现,性能较差。

1.3 复杂度

时间复杂度为 O(n^2),空间复杂度为 O(1)。

1.4 使用场景

插入排序的优点是对于小规模的数据集,它的效率比较高,而且它的实现比较简单。

但是对于大规模的数据集,插入排序的效率会比较低,因为它需要进行大量的元素移动操作。

二、代码实现

2.1 Java代码实现

2.1.1 代码示例

scss 复制代码
public class QuickSortExample {
    public static void main(String[] args) {
        int[] arr = {5, 2, 4, 6, 1, 3};

        System.out.println("Original Array:");
        printArray(arr);

        quickSort(arr, 0, arr.length - 1);

        System.out.println("Sorted Array:");
        printArray(arr);
    }

    public static void quickSort(int[] arr, int left, int right) {
        if (left >= right) {
            return;
        }

        int pivot = arr[left];
        int i = left + 1, j = right;

        while (i <= j) {
            if (arr[i] < pivot) {
                i++;
            } else if (arr[j] >= pivot) {
                j--;
            } else {
                swap(arr, i, j);
                i++;
                j--;
            }
        }

        swap(arr, left, j);

        quickSort(arr, left, j - 1);
        quickSort(arr, j + 1, right);
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    private static void printArray(int[] arr) {
        for (int num : arr) {
            System.out.print(num + " ");
        }
        System.out.println();
    }
}

2.1.2 代码详解

实现思路:

  1. 首先检查左右指针的位置,如果左指针大于等于右指针,说明当前的子数组已经有序,直接返回即可。

  2. 否则,选择一个基准元素(这里选择左端点的元素作为基准),将数组分成两个部分,小于基准元素的部分和大于等于基准元素的部分。

  3. 然后,使用两个指针 i 和 j 分别从左右两端开始遍历,将小于基准元素的元素移动到左边,将大于等于基准元素的元素移动到右边,直到 i 和 j 相遇。

  4. 最后,将基准元素交换到正确的位置上,递归地对左右两个部分进行快速排序。

代码实现:

  1. 使用了两个辅助指针 i 和 j,分别从左右两端开始遍历数组。

  2. 在遍历过程中,如果 arr[i] 小于 pivot,就将 i 向右移动一位;如果 arr[j] 大于等于 pivot,就将 j 向左移动一位;

  3. 否则,交换 arr[i] 和 arr[j],然后将 i 向右移动一位,将 j 向左移动一位。当 i 和 j 相遇时,遍历结束,此时 arr[j] 是小于 pivot 的最后一个元素,将 pivot 交换到 arr[j] 的位置上,然后递归地对左右两个部分进行快速排序。

2.1.3 运行结果

javascript 复制代码
Original Array:
5 2 4 6 1 3 
Sorted Array:
1 2 3 4 5 6 

2.2 Python代码实现

2.2.1 代码示例

ini 复制代码
def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    else:
        pivot = arr[0]
        less_than_pivot = [x for x in arr[1:] if x <= pivot]
        greater_than_pivot = [x for x in arr[1:] if x > pivot]
        return quick_sort(less_than_pivot) + [pivot] + quick_sort(greater_than_pivot)

# 示例
if __name__ == "__main__":
    input_array = [5, 2, 4, 6, 1, 3]

    print("Original Array:")
    print(input_array)

    sorted_array = quick_sort(input_array)

    print("Sorted Array:")
    print(sorted_array)

2.2.2 代码详解

代码思路

  1. 首先检查数组的长度,如果长度小于等于 1,说明该数组已经有序,直接返回即可。

  2. 否则,选择一个基准元素(这里选择第一个元素作为基准),将数组分成两个部分,小于基准元素的部分和大于等于基准元素的部分。

  3. 然后,递归地对这两个部分进行快速排序,并将它们拼接起来,得到最终的排序结果。

代码实现:

  1. 使用了两个辅助数组 left 和 right,分别用于存储小于基准元素和大于等于基准元素的部分。

  2. 遍历原数组时,如果当前元素小于基准元素,就将它添加到 left 数组中,否则将它添加到 right 数组中。

  3. 最后,递归地对 left 和 right 数组进行排序,并将它们和基准元素拼接起来,得到最终的排序结果。

2.2.3 运行结果

csharp 复制代码
Original Array:
[5, 2, 4, 6, 1, 3]
Sorted Array:
[1, 2, 3, 4, 5, 6]

今天就到这里了,下期见喽~

相关推荐
NiNg_1_2347 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer083 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠5 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#