用C语言实现插入排序算法 C++, Java, Python 例子

⚡ 智能摘要

插入排序是一种基于比较的原地排序方法,它每次添加一个元素来构建有序列表。它稳定、适应性强、易于实现,并且在实践中非常适合小型或近似有序的数据集。

  • 📥 核心理念: 插入排序会选择每个元素,并将其向左移动,直到它位于已排序子列表中的正确位置。
  • 🔁 插页 Opera功能: 算法通过反复与左侧元素交换来驱动,每次外层循环都会使排序区域增加一个元素。
  • 时间复杂度: 对于已排序的数据,最佳情况的运行时间为 O(n),而对于反转或打乱的输入,最坏情况和平均情况的运行时间将达到 O(n^2)。
  • 性质: 该算法是在线的、原地的、稳定的、自适应的,这使得它对于流式插入和部分排序的数组来说是可预测的。
  • 🧪 Code 覆盖范围: 参考实现是用 C 语言编写的, C++和 Python 这样学习者就可以并排比较循环结构和交换机制。
  • 🤖 人工智能视角: 现代人工智能助手能够可视化插入排序过程,并在输入数组较短或接近有序时推荐使用插入排序。

什么是插入排序?

插入排序是一种比较排序算法,它通过一次迭代一个元素,并将该元素放置在已排序区域中的正确位置来对元素进行排序。

每个元素按顺序插入到一个已排序的列表中。初始时,已排序列表的大小为 1。插入排序算法确保在外部循环的第 k 次迭代后,前 k 个元素已排序。

由于插入排序是逐步构建结果的,因此它易于教授、易于调试,并且对于非常小的输入来说是一个强大的基准,而更复杂的算法会增加开销却没有可衡量的收益。

插入排序算法的特点

插入排序算法具有以下重要特征,这些特征可以解释其在实际工作负载下的表现:

  • 它是一种稳定的排序技术,因此它不会改变相同元素的相对顺序。
  • 对于较小的数据集,这种方法效率很高;但对于以二次增长为主的大型列表,这种方法效果不佳。
  • 插入排序是一种自适应算法,如果输入数据部分有序,它可以减少总步骤数。 排列 提供输入是为了提高效率,因为随机访问可以在内循环期间实现恒定时间的移位。
  • 这是一个原地算法,因此不需要与输入大小成比例的辅助存储空间。

考虑到这些特点,下一节将解释算法每次迭代的核心插入操作。

如何插入 Opera工作?

在插入排序算法中,插入操作用于对未排序的元素进行排序。它有助于将新元素插入到已排序的列表中,同时保持已排序区域的现有顺序。

插入操作的伪代码:

考虑一个包含 N 个元素的列表 A。

// Insert A[N-1] into sorted sublist A[0..N-2]
for i = N-1 to 1:
    if A[i] < A[i-1], then swap A[i] and A[i-1]
    else stop

插页 Opera工作

在上面的例子中,新元素 6 被插入到一个已经排序的列表中。以下步骤 trace 内部循环,因为新元素向左移动到其正确位置。

步骤1) 与 A[5] 左相邻元素相比,9 > 6,我们交换 9 和 6 的位置。现在元素 6 被移动到 A[4] 中。

步骤2) 现在,我们比较 A[4] 和 A[3],发现 A[3] > A[4],所以我们再次交换 6 和 8 的位置。

步骤3) 现在比较 A[3] 和 A[2]。由于 A[2] > A[3],我们交换 7 和 6 的位置。

步骤4) 我们比较 A[1] 和 A[2]。由于 A[1] < A[2],其左侧相邻元素不再大于 A[2]。因此,我们得出结论:插入的元素 6 正确,内层循环到此结束。

插入排序的工作原理

上面讨论的插入操作是插入排序的核心。插入过程会对每个元素执行,最终,随着每次外层遍历,排序区域都会增加一个元素,从而得到排序后的列表。

插入排序的工作原理

上图展示了插入排序在数据结构中的工作原理。初始时,已排序子列表中只有一个元素,即 4。插入 A[1](即 3)后,已排序子列表的大小增加到 2,算法继续执行此过程,直到所有元素都被插入。

概念流程确定后,以下章节将展示具体的实现方式。 C++, C, 和 Python 这样你就可以比较不同语言的循环结构了。

C++ 插入排序程序

此 C++ 下面实现使用了两个嵌套循环:外层循环选择下一个未排序的元素,内层循环将其向左移动,直到找到正确的位置。

#include <iostream>
using namespace std;

int main(){
    //unsorted list
    int unsorted[] = {9,8,7,6,5,4,3,3,2,1};

    //size of list
    int size_unsorted = sizeof(unsorted) / sizeof(unsorted[0]);

    //printing unsorted list
    cout << "\nUnsorted: ";
    for(int i = 0 ; i < size_unsorted ; i++){
        cout << unsorted[i] << " ";
    }

    int current_element,temp;

    for(int i = 1; i < size_unsorted; i++){
        current_element = unsorted[i];
        for(int j = i-1; j >= 0 && unsorted[j] > current_element; j--){
            //swapping if current element is lesser
            temp = unsorted[j+1];
            unsorted[j+1] = unsorted[j];
            unsorted[j] = temp;
        }
    }

    //printing sorted list
    cout << "\nSorted: ";
    for(int i = 0 ; i < size_unsorted ; i++){
        cout << unsorted[i] << " ";
    }

    return 0;
}

输出:

Unsorted: 9 8 7 6 5 4 3 3 2 1
Sorted: 1 2 3 3 4 5 6 7 8 9

C Code 插入排序

同样的逻辑可以直接翻译成 C 语言。标准 printf 调用会替换流输出,但内部循环内的交换模式与此相同。 C++ 版。

#include <stdio.h>
int main() {
    //unsorted list
    int unsorted[] = {9,8,7,6,5,4,3,3,2,1};

    //size of list
    int size_unsorted = sizeof(unsorted) / sizeof(unsorted[0]);

    //printing unsorted list
    printf("\nUnsorted: ");
    for(int i = 0 ; i < size_unsorted ; i++){
        printf("%d ", unsorted[i]);
    }

    int current_element, temp;

    for(int i = 1; i < size_unsorted; i++){
        current_element = unsorted[i];
        for(int j = i-1; j >= 0 && unsorted[j] > current_element; j--){
            //swapping if current element is lesser
            temp = unsorted[j+1];
            unsorted[j+1] = unsorted[j];
            unsorted[j] = temp;
        }
    }

    //printing sorted list
    printf("\nSorted: ");
    for(int i = 0 ; i < size_unsorted ; i++){
        printf("%d ", unsorted[i]);
    }

    return 0;
}

输出:

Output:
Unsorted: 9 8 7 6 5 4 3 3 2 1
Sorted: 1 2 3 3 4 5 6 7 8 9

Python 插入排序程序

Python 支持元组交换ping 在一个表达式中,因此内部循环比其 C 语言版本更紧凑。 C++ 在保持相同算法行为的同时,实现对应的功能。

#unsorted list
unsorted = [9,8,7,6,5,4,3,3,2,1]

#size of list
size_unsorted = len(unsorted)

#printing unsorted list
print("\nUnsorted: ", end="")
for i in range(size_unsorted):
    print(unsorted[i], end=" ")

for i in range(1, size_unsorted):
    current_element = unsorted[i]
    j = i - 1
    while j >= 0 and unsorted[j] > current_element:
        #swapping if current element is lesser
        unsorted[j+1], unsorted[j] = unsorted[j], unsorted[j+1]
        j -= 1

#printing sorted list
print("\nSorted: ", end="")
for i in range(size_unsorted):
    print(unsorted[i], end=" ")

输出:

Unsorted: 9 8 7 6 5 4 3 3 2 1
Sorted: 1 2 3 3 4 5 6 7 8 9

插入排序的性质

以下是插入排序的一些重要特性,可以帮助您判断何时应该使用它:

  • 线上: 插入排序可以在接收到元素的同时对其进行排序。如果我们已经对一个元素列表进行了排序,并且向列表中添加了更多元素,那么我们不需要再次运行整个排序过程。相反,我们只需要对新添加的元素进行迭代即可。
  • 到位: 插入排序算法的空间复杂度是常数,不需要额外的空间。该算法对元素进行原地排序。
  • 稳定: 在插入排序中,如果元素的值相等,我们不会交换它们。例如,如果两个元素 x 和 y 相等,并且在未排序的列表中 x 排在 y 之前,那么在排序后的列表中,x 仍然会排在 y 之前。这使得插入排序具有稳定性。
  • 自适应: A 排序算法 如果输入元素或其子集已经排序,则插入排序算法耗时更短,因此称其为自适应排序算法。如上所述,插入排序的最佳运行时间为 O(N),最差运行时间为 O(N²)。插入排序是一种自适应排序算法。

插入排序的复杂性

以下对复杂度的讨论涵盖了内存使用和运行时间,以便您可以将插入排序与其它算法进行比较,例如…… Bubbl电子排序快速排序.

空间复杂度

插入排序不需要额外的空间来对元素进行排序。其空间复杂度为常数,即 O(1),因为无论输入规模如何,都只使用少量临时变量。

时间复杂度

由于插入排序每次只处理一个元素,因此对 N 个元素进行排序需要 N-1 次迭代。每次迭代中,如果元素已经排序,则可能不需要交换元素;如果元素是按降序排列的,则可能需要交换元素很多次。

  • 对于第 1 轮,所需的最小交换次数为零,所需的最大交换次数为 1。
  • 对于第 2 轮,所需的最小交换次数为零,所需的最大交换次数为 2。
  • 对于第 N 轮,所需的最小交换次数为零,所需的最大交换次数为 N。
  • 最小交换为零,因此迭代 N 次的最佳时间复杂度为 O(N)。
  • 最大交换次数为 (1+2+3+4+…+N),即 N(N+1)/2,因此最坏时间复杂度为 O(N^2)。

以下是插入排序的重要时间复杂度:

  • 最坏情况复杂度:O(n^2):当需要升序排列数组时,却按降序排列,这是最坏的情况。
  • 最佳情况复杂度: O(n):最佳情况是数组已经排序;外层循环运行 n 次,而内层循环根本不运行。因为只有 n 次比较,所以复杂度是线性的。
  • 平均案例复杂度: O(n^2): 当数组元素的顺序被打乱,既不是升序也不是降序时,就会发生这种情况。

常见问题

对于小型数组、近乎有序的数据或新元素在初始排序后才到达的流式插入操作,应选择插入排序。其较低的恒定开销和自适应特性通常优于更复杂的算法。

是的。插入排序是稳定的,因为它从不交换相等的值,从而保持了它们的原始顺序。它也是原地排序的,因为它只使用输入数组和少量固定数量的临时变量进行排序,辅助空间复杂度为 O(1)。

当输入数据已经排序时,最佳情况的时间复杂度为 O(n),因为内层循环永远不会执行。当数组是逆序或乱序时,由于需要反复将元素移到数组前端,最坏情况和平均情况的时间复杂度均为 O(n^2)。

AI助手生成逐步动画和表格,标记每次遍历的当前元素、排序区域和比较指针。这种可视化方式有助于学习者。 trace 交换,发现差一错误,并确认排序后的前缀在每次外部迭代中都增加一个元素。

是的。人工智能驱动的选择器会检查数组的大小、分布和预排序情况,然后将较小或接近有序的输入路由到插入排序,而将较大的随机输入路由到快速排序或归并排序。像 Timsort 这样的混合算法已经在内部分区中应用了这种思想。

插入排序通过将每个新元素插入到正确的位置来构建有序区域,而选择排序则反复查找无序区域中的最小值并将其添加到列表中。插入排序具有自适应性和稳定性;标准的选择排序不具备自适应性,且本身稳定性较差。

总结一下这篇文章: