经典排序算法----直接插入排序算法及其改进(稳定)

插入排序算法(稳定

插入排序

直接插入,平均情况O(n2),最好情况O(n),最坏情况O(n2),辅助空间O(1),稳定。


插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。


直接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。


解题思路:

  • 设数组为a[0…n-1]。
  • 初始时,a[0]自成1个有序区,无序区为a[1..n-1]。令i=1
  • 将a[i]并入当前的有序区a[0…i-1]中形成a[0…i]的有序区间。
  • i++并重复第二步直到i==n-1。排序完成

插入排序法
0123456789
12348967100

分析:
如上图所示,若想将元素6(位序i=6)插入到前边已经排好顺序的数组中,需要先找到元素6所归属的位置i,因为元素6的下标为6,即i=6,所以应该从下标为5(即i-1)的位置开始向前寻找,找到比元素6小的位置j,( 注意:找到元素6位置i之后的位置是无用的,因为j本来就在i之前的第一个位置上),并将该位置之后(j+1)的元素,6之前(即i-1)的元素的位置依次向后移动,并将元素6插入到j之后的位置,即(j+1)的位置;

根据上述分析,很容易写出代码(由小到大排序):
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

void Show(int *arr, int n)
{
	for (int i = 0; i < n; i++)
		printf("%-5d", arr[i]);
	printf("\n");
}

void InsertSort(int *arr, int n)
{
	int i, j, k;
	for (i = 1; i < n; i++)
	{
		//为a[i]找到要插入的位置
		for (j = i - 1; j >= 0; j--)
			if (arr[i] > arr[j])
				break;

		//如果找到了一个合适的位置,注意,找到a[i]之后的位置是没有意义的
		if (j != i - 1)
		{
			//将比a[i]大的所有元素后移
			int temp = arr[i];
			for (k = i - 1; k > j; k--)
				arr[k + 1] = arr[k];
			arr[j+1] = temp;
		}
	}
}

void main()
{
	int arr[100] = { 0 };
	time_t time1;
	srand((unsigned int)time(&time1));
	for (int i = 0; i < 100; i++)
		arr[i] = rand() % 100;
	printf("排序之前的数据:\n");
	Show(arr, 100);
	printf("排序之后的数据:\n");
	InsertSort(arr, 100);
	Show(arr, 100);

	system("pause");
}

运行结果:
排序之前的数据:
29   64   22   30   56   16   21   34   57   34   83   99   65   91   4    7
15   78   45   24   93   49   74   91   59   0    19   86   94   84   14   6
15   74   54   39   9    25   54   92   4    66   90   17   27   54   25   22
51   15   14   41   4    99   64   4    1    4    16   57   55   64   98   13
91   27   30   34   93   75   73   29   23   94   54   88   73   27   58   48
52   26   16   9    67   93   9    16   67   7    70   77   61   39   90   7
25   10   79   85
排序之后的数据:
0    1    4    4    4    4    4    6    7    7    7    9    9    9    10   13
14   14   15   15   15   16   16   16   16   17   19   21   22   22   23   24
25   25   25   26   27   27   27   29   29   30   30   34   34   34   39   39
41   45   48   49   51   52   54   54   54   54   55   56   57   57   58   59
61   64   64   64   65   66   67   67   70   73   73   74   74   75   77   78
79   83   84   85   86   88   90   90   91   91   91   92   93   93   93   94
94   98   99   99
请按任意键继续. . .


优化一


这样的代码太长了,不够清晰。现在进行一下改写,将搜索和数据后移这二个步骤合并。即每次a[i]先和前面一个数据a[i-1]比较,如果a[i] > a[i-1]说明a[0…i]也是有序的,无须调整。否则就令j=i-1,temp=a[i]。然后一边将数据a[j]向后移动一边向前搜索,当有数据a[j]<a[i]时停止并将temp放到a[j + 1]处。

代码:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

void Show(int *arr, int n)
{
	for (int i = 0; i < n; i++)
		printf("%-5d", arr[i]);
	printf("\n");
}

void InsertSort(int *arr, int n)
{
	int i, j;
	for (i = 1; i < n; i++)
	{
		if (arr[i] < arr[i - 1])
		{
			int temp = arr[i];
			for (j = i - 1; j >= 0 && arr[j] > temp; j--)
				arr[j + 1] = arr[j];
			arr[j + 1] = temp;
		}
	}
}

void main()
{
	int arr[100] = { 0 };
	time_t time1;
	srand((unsigned int)time(&time1));
	for (int i = 0; i < 100; i++)
		arr[i] = rand() % 100;
	printf("排序之前的数据:\n");
	Show(arr, 100);
	printf("排序之后的数据:\n");
	InsertSort(arr, 100);
	Show(arr, 100);

	system("pause");
}

运行结果:
排序之前的数据:
0    44   4    22   28   54   8    2    72   96   70   43   28   43   49   40
99   65   62   28   77   95   77   75   20   16   74   76   57   71   60   19
10   72   26   44   87   80   60   77   65   65   1    65   44   40   94   46
36   18   45   92   7    81   27   46   70   16   10   52   62   56   91   14
92   12   36   84   99   39   45   90   41   1    16   2    57   31   10   4
19   67   80   41   64   44   17   91   43   93   76   7    84   32   82   64
88   4    17   76
排序之后的数据:
0    1    1    2    2    4    4    4    7    7    8    10   10   10   12   14
16   16   16   17   17   18   19   19   20   22   26   27   28   28   28   31
32   36   36   39   40   40   41   41   43   43   43   44   44   44   44   45
45   46   46   49   52   54   56   57   57   60   60   62   62   64   64   65
65   65   65   67   70   70   71   72   72   74   75   76   76   76   77   77
77   80   80   81   82   84   84   87   88   90   91   91   92   92   93   94
95   96   99   99
请按任意键继续. . .

优化二


再对将a[j]插入到前面a[0…j-1]的有序区间所用的方法进行改写,用数据交换代替数据后移。如果a[j]前一个数据a[j-1] > a[j],就交换a[j]和a[j-1],再j--直到a[j-1] <= a[j]。这样也可以实现将一个新数据新并入到有序区间。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

void Show(int *arr, int n)
{
	for (int i = 0; i < n; i++)
		printf("%-5d", arr[i]);
	printf("\n");
}

void Swap(int *a, int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

void InsertSort(int *arr, int n)
{
	int i, j;
	for (i = 1; i < n; i++)
	{
		for (j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--)
			Swap(&arr[j], &arr[j + 1]);
	}
}

void main()
{
	int arr[100] = { 0 };
	time_t time1;
	srand((unsigned int)time(&time1));
	for (int i = 0; i < 100; i++)
		arr[i] = rand() % 100;
	printf("排序之前的数据:\n");
	Show(arr, 100);
	printf("排序之后的数据:\n");
	InsertSort(arr, 100);
	Show(arr, 100);

	system("pause");
}
运算结果:
排序之前的数据:
33   95   26   23   40   39   69   57   31   54   21   98   82   2    78   34
84   69   18   51   97   16   18   98   43   15   48   50   3    55   5    27
42   0    38   67   85   72   8    9    84   99   44   87   9    97   4    2
9    81   39   63   11   60   23   37   50   97   77   52   31   13   61   35
86   30   99   25   73   84   56   68   13   88   57   0    90   84   17   71
73   66   81   73   64   85   1    18   4    2    61   13   26   27   97   67
0    97   0    13
排序之后的数据:
0    0    0    0    1    2    2    2    3    4    4    5    8    9    9    9
11   13   13   13   13   15   16   17   18   18   18   21   23   23   25   26
26   27   27   30   31   31   33   34   35   37   38   39   39   40   42   43
44   48   50   50   51   52   54   55   56   57   57   60   61   61   63   64
66   67   67   68   69   69   71   72   73   73   73   77   78   81   81   82
84   84   84   84   85   85   86   87   88   90   95   97   97   97   97   97
98   98   99   99

注意:最后一种算法的思想和冒泡排序算法的思想差不多!

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值