插⼊排序算法是所有排序⽅法中最简单的⼀种算法,其主要的实现思想是将数据按照⼀定的顺序⼀ 个⼀个的插⼊到有序的表中,最终得到的序列就是已经排序好的数据。
1. 直接插⼊排序
直接插⼊排序是插⼊排序算法中的⼀种,采⽤的⽅法是:在添加新的记录时,使⽤顺序查找的⽅式 找到其要插⼊的位置,然后将新记录插⼊。
直接插⼊排序(Straight Insertion Sort)的基本思想是:
- 把n个待排序的元素看成为⼀个有序表和⼀个⽆序表。
- · 开始时有序表中只包含1个元素,⽆序表中包含有n-1个元素,排序过程中每次从⽆序表中取出第⼀ 个元素,将它插⼊到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。
插⼊排序算法还包括折半插⼊排序、2-路插⼊排序和希尔排序等
例如采⽤直接插⼊排序算法将⽆序表{3,1,7,5,2,4,9,6}进⾏升序排序的过程为:
- ⾸先考虑记录 3 ,由于插⼊排序刚开始,有序表中没有任何记录,所以 3 可以直接添加到有序表 中,则有序表和⽆序表可以如图 1所示:
- 向有序表中插⼊记录 1 时,同有序表中记录 3 进⾏⽐较,1<3,所以插⼊到记录 3 的左侧,如图所示:
- 向有序表插⼊记录 7 时,同有序表中记录 3 进⾏⽐较,3<7,所以插⼊到记录 3 的右侧,如图 所示:
- 向有序表插⼊记录 2 时,同有序表中记录 7进⾏⽐较,2<7,再同 5,3,1分别进⾏⽐较,最终确 定 2 位于 1 和 3 中间,如图所示:
- 照此规律,依次将⽆序表中的记录 4,9 和 6插⼊到有序表中,如图所示:
直接插⼊排序:将待排序的数组,分成两个序列,前⾯的序列保持有序,依次选择后⾯的元素,往 前⾯插⼊。(就地排序、稳定排序)
2. 折半插⼊排序
上述算法在查找插⼊位置时,采⽤的是顺序查找的⽅式,⽽在查找表中数据本身有序的前提下,可 以使⽤折半查找来代替顺序查找,这种排序的算法就是折半插⼊排序算法。
3. 2-路插⼊排序
2-路插⼊排序算法是在折半插⼊排序的基础上对其进⾏改进,减少其在排序过程中移动记录的次数 从⽽提⾼效率。
具体实现思路为:另外设置⼀个同存储记录的数组⼤⼩相同的数组 d,将⽆序表中第⼀个记录添加 进 d[0] 的位置上,然后从⽆序表中第⼆个记录开始,同 d[0] 作⽐较:如果该值⽐ d[0] ⼤,则添加到其 右侧;反之添加到其左侧。
在这⾥的数组 d 可以理解成⼀个环状数组。
3. 希尔排序
希尔排序,⼜称“缩⼩增量排序”,也是插⼊排序的⼀种,但是同前⾯⼏种排序算法⽐较来看,希尔 排序在时间效率上有很⼤的改进。
在使⽤直接插⼊排序算法时,如果表中的记录只有个别的是⽆序的,多数保持有序,这种情况下算 法的效率也会⽐较⾼;除此之外,如果需要排序的记录总量很少,该算法的效率同样会很⾼。希尔排序 就是从这两点出发对算法进⾏改进得到的排序算法。
希尔排序的具体实现思路是:先将整个记录表分割成若⼲部分,分别进⾏直接插⼊排序,然后再对整个 记录表进⾏⼀次直接插⼊排序。
例如⽆序表{49,38,65,97,76,13,27,49,55,4}进⾏希尔排序的过程为:
- ⾸先对 {49,13},{38,27},{65,49},{97,55},{76,4} 分别进⾏直接插⼊排序(如果需要调 换位置也只是互换存储位置),如下图所示:
上图中两两进⾏⽐较,例如 49 和 13 进⾏⽐较,13<49,所以交换存储位置。
- 通过⼀次排序,⽆序表中的记录已基本有序,此时还可以再进⾏⼀次分割,如下图所示:
- 经过两次分割,⽆序表中已基本有序,此时对整张表进⾏⼀次直接插⼊排序(只需要做少量的⽐较 和插⼊操作即可),最终希尔排序的结果为:
希尔排序的过程中,对于分割的每个⼦表,其各⾃包含的记录在原表中并不是相互挨着的,⽽是相 互之间相隔着某个固定的常数。例如上例中第⼀次排序时⼦表中的记录分割的常量为 5,第⼆次排序时 为 3。
通过此种⽅式,对于关键字的值较⼩的记录,其前移的过程不是⼀步⼀步的,⽽是跳跃性的前移, 并且在最后⼀次对整表进⾏插⼊排序时减少了⽐较和排序的次数。
//insertSort.h
#ifndef INSERT_SORT_H
#define INSERT_SORT_H
#include "../sortHelper.h"
// 直接插入排序(找位置插入)
void insertSort(SortTable *table);
// 直接插入法(扑克牌的交换)
void insertSortV1(SortTable *table);
// shell排序
void shellSort(SortTable *table);
// shell排序
void shellSort1(SortTable *table);
#endif
#include "insertSort.h"
void insertSortV1(SortTable*table)
{
for(int i=1;i<table->length;i++)
{
Element e=table->data[i];
int j;
for(j=i;j>0&&table->data[j-1].key>e.key;j--)
{
swapElement(&table->data[j-1],&table->data[j]);
}
table->data[j]=e;
}
}
/* 1. 默认第一个元素就是有序,那么从第二个元素开始和前面有序的区域进行比较
* 2. 待插入的元素i,和有序区域从后往前依次查找
* 若待插入元素 < 以有序区域的值,有序的区域的值后移一位
* 插入查找的位置[0...i-1]的位置
* */
void insertSort(SortTable *table)
{
for(int i=1;i<table->length;++i)
{
int j = i - 1;
Element tmp = table->data[i];
while (j > -1 && tmp.key < table->data[j].key) {
table->data[j + 1] = table->data[j];
j--;
}
table->data[j + 1] = tmp;
}
}
void shellSort(SortTable *table)
{
int gap;
for(gap=table->length/2;gap>0;gap/=2)
{
for(int i=gap;i<table->length;++i)
{
Element temp=table->data[i];
int j;
for(j=i;j>=gap&&table->data[j-gap].key>temp.key;j-=gap ){
swapElement(&table->data[j],&table->data[j-gap]);
}
table->data[j]=temp;
}
}
}
void shellSort1(SortTable *table)
{
int gap;
for(gap=table->length/2;gap>0;gap/=2)
{
for(int i=gap;i<table->length;++i)
{
Element temp=table->data[i];
int j;
for(j=i;j>=gap&&table->data[j-gap].key>temp.key;j-=gap ){
swapElement(&table->data[j],&table->data[j-gap]);
}
table->data[j]=temp;
}
}
}
#include "insertSort.h"
int main()
{
int n = 100000; // 增大数字
SortTable *table1 = generateRandomArray(n, 0, n);
SortTable *table2 = copySortTable(table1);
SortTable *table3 = copySortTable(table1);
SortTable *table4 = generateLinearArray(n, 10);
testSort("random insertSort: ", insertSortV1, table1);
testSort("random insertSort: ", insertSort, table2);
testSort("shell insertSort: ", shellSort, table3);
testSort("linear insertSort: ", insertSort, table4);
releaseSortTable(table1);
releaseSortTable(table2);
releaseSortTable(table3);
releaseSortTable(table4);
return 0;
}
//sortHelp.h
#ifndef SORT_HELPER_H
#define SORT_HELPER_H
#include <stdio.h>
#include <stdlib.h>
typedef int keyType;
typedef struct {
keyType key; // 查找表中每个数据元素的关键值
void *data; // 数据的其他区域
}Element;
typedef struct {
Element *data; // 存放查找表中数据元素的首地址
int length; // 查找表的元素个数
}SortTable;
enum sortStatus{success, failed};
void swapElement(Element *a, Element *b); // 交换元素a和元素b
SortTable *generateRandomArray(int n, int low, int high); // 产生随机数范围[low,high]
SortTable *generateLinearArray(int n, int swapTimes); // 参数顺序空间,随机交换swapTimes次
SortTable *copySortTable(SortTable *old); // 拷贝和old一样值的排序表
void releaseSortTable(SortTable *table);
// 排序算法函数的别名
typedef void (*sortHandler)(SortTable *);
// 测试sortName的排序算法
void testSort(const char *sortName, sortHandler sort, SortTable *table);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "sortHelper.h"
/* 交换a和b的元素值 */
void swapElement(Element *a, Element *b) {
Element tmp;
memcpy(&tmp, a, sizeof(Element));
memcpy(a, b, sizeof(Element));
memcpy(b, &tmp, sizeof(Element));
}
/* 产生n个随机数的排序表,值的范围是[low, high] */
SortTable *generateRandomArray(int n, int low, int high) {
SortTable *table = (SortTable *) malloc(sizeof(SortTable));
if (table == NULL) {
fprintf(stderr, "sort table malloc failed!\n");
return NULL;
}
table->length = n;
table->data = (Element *) malloc(sizeof(Element) * n);
if (table->data == NULL) {
fprintf(stderr, "element malloc failed!\n");
free(table);
return NULL;
}
srand(time(NULL) + 1);
for (int i = 0; i < n; ++i) {
table->data[i].key = (rand() % (high - low + 1)) + low;
table->data[i].data = NULL;
}
return table;
}
/* 产生n个随机交换swapTimes次的有序顺序表 */
SortTable *generateLinearArray(int n, int swapTimes) {
SortTable *table = (SortTable *)malloc(sizeof(SortTable));
if (table == NULL) {
fprintf(stderr, "sort table malloc failed!\n");
return NULL;
}
table->data = (Element *)malloc(sizeof(Element) * n);
if (table->data == NULL) {
fprintf(stderr, "data malloc failed!\n");
free(table);
return NULL;
}
table->length = n;
for (int i = 0; i < n; ++i) {
table->data[i].key = i;
table->data[i].data = NULL;
}
// 在已经有序的排序表中,交换swapTimes次
srand(time(NULL) + 2);
for (int i = 0; i < swapTimes; ++i) {
int pos1 = rand() % n;
int pos2 = rand() % n;
swapElement(&table->data[pos1], &table->data[pos2]);
}
return table;
}
/* 拷贝一个排序表,使用同样的数据进行不同排序算法的测试 */
SortTable *copySortTable(SortTable *old) {
SortTable *table = (SortTable *) malloc(sizeof(SortTable));
table->length = old->length;
table->data = malloc(sizeof(Element) * old->length);
for (int i = 0; i < old->length; ++i) {
table->data[i].key = old->data[i].key;
table->data[i].data = old->data[i].data;
}
return table;
}
/* 释放table */
void releaseSortTable(SortTable *table) {
if (table) {
if (table->data) {
free(table->data);
}
free(table);
}
}
// 检查排序表里的数据,是否是从小到大排序
static enum sortStatus checkData(SortTable *table) {
for (int i = 0; i < table->length - 1; ++i) {
if (table->data[i].key > table->data[i + 1].key) {
printf("Check Sort Data Failed: %d : %d\n", table->data[i].key, table->data[i + 1].key);
return failed;
}
}
return success;
}
/* 测试sortName的排序算法,算法通过sort传递函数名,数据以table传入 */
void testSort(const char *sortName, sortHandler sort, SortTable *table) {
clock_t start = clock();
sort(table);
clock_t end = clock();
if (checkData(table) == failed) {
printf("%s failed!\n", sortName);
return;
}
printf("%s cost time: %fs.\n", sortName, (double) (end - start) / CLOCKS_PER_SEC);
}