插入排序通过交换

Insertion Sort by swapping

本文关键字:交换 插入排序      更新时间:2023-10-16

我刚开始使用DSA,有一个关于插入排序的问题。

这是课本/教程的版本。

void insertion_sort(int A[], int n) {
    for (int i = 0; i < n; i++) {
        int temp = A[i];    
        int j = i;
        while (temp < A[j - 1] && j > 0) {
            A[j] = A[j - 1];   
            j = j - 1;
        }
        A[j] = temp;       
    }  
}

我在想,如果我们使用swapping数字而不是移动数字并将temp值插入正确的孔位置,会有什么不同吗?

void insertionSort(int A[], int n) {
    for (int i = 0; i < n; i++) {
        int temp = A[i];
        int j = i;
        while (temp < A[j - 1] && j > 0) {
            swap(A[j], A[j - 1]);
            j--;
        }
    }
}

交换代码:

void swap(int &a,int &b){
    int temp = a;
    a = b;
    b = temp;
}

哦,如果有人能解释这两者的时间复杂性,那就太棒了。

两种方法的时间复杂度在最坏情况下都是O(N^2)。但是,与第一种方法相比,第二种方法中的操作次数更多,因为第二种方法执行的交换次数与第一种方法中的移位次数相同,但是交换需要3次赋值,而基于移位的方法只需一次。因此,与仅仅移动元素相比,您提出的方法将会更慢。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
void insertion_shift(int* arr, int n){
    int i,j,k;
    for(i=1;i<n;++i){
        int temp=arr[i];
        for(j=i;j>0 && arr[j-1]>temp;--j)
            arr[j]=arr[j-1];
        arr[j]=temp;
    }
}
void swap(int* a, int* b){
  int temp= *a;
  *a= *b;
  *b= temp;
}
void insertion_swap(int* arr, int n){
   int i,j,k;
   for(i=1;i<n;++i){
      int temp=arr[i];
      for(j=i;j>0 && arr[j-1]>temp;--j)
         swap(&arr[j-1],&arr[j]);
   }
 }      
void print_arr(int* arr, int n){
   int i;
   for(i=0;i<n;++i)
      printf("%d ",arr[i]);
   printf("n");
}
int main(){
   int n;
   scanf("%d",&n);
   int* arr1= (int*)malloc(sizeof(int)*n);
   int* arr2= (int*)malloc(sizeof(int)*n);
   int i;
   for(i=0;i<n;++i){
      scanf("%d",&arr1[i]);
      arr2[i]=arr1[i];
   }
   struct timespec start, end;
   clock_gettime(CLOCK_MONOTONIC_RAW,&start);
   insertion_shift(arr1,n);
   clock_gettime(CLOCK_MONOTONIC_RAW,&end);
   uint64_t time_shift= (end.tv_sec - start.tv_sec)*1000000 +
                        (end.tv_nsec - start.tv_nsec)/1000;
   printf("using shift: %lld microsecondsn",time_shift);
   clock_gettime(CLOCK_MONOTONIC_RAW,&start);
   insertion_swap(arr2,n);
   clock_gettime(CLOCK_MONOTONIC_RAW,&end);
   uint64_t time_swap= (end.tv_sec - start.tv_sec)*1000000 +
                       (end.tv_nsec - start.tv_nsec)/1000;
   printf("using swap: %lld microsecondsn",time_swap);

}

下面是我在大小为10000的同一个数组上调用两个函数时得到的结果。10000个元素数组的编译和执行。如果仍然不相信,请尝试生成大小为1000-10000的随机数组,并运行上面的代码来观察差异。

您提出的替代方案不完整,您没有发布swap()的代码。在C语言中,swap必须是一个宏,这样的宏很容易出错,而在c++中,它可以是一个通过引用接受两个参数的函数。

此外,在解引用A[j - 1]之前,您应该测试j > 0 。如上所述,代码调用了未定义的行为。

关于你的问题,两个函数都同样慢,时间复杂度为O(N2),但第二个函数可能更慢,因为交换涉及更多的读写,而不是简单地将值移动一个位置,但在排序数组上可能更快,因为第一个版本有冗余存储。

请注意,您可以通过以下方式进一步简化代码:

void insertionSort(int A[], int n) {
    for (int i = 1; i < n; i++) {
        for (int j = i; j > 0 && A[j] < A[j - 1]; j--) {
            swap(A[j], A[j - 1]);
        }
    }
}