插入排序
1.直接插入排序
一次向数组中插入一个数 ,即每一次操作后的局部数组是有序的 。由于局部有序,插入下一个元素时,只要碰到比这个元素小的值就可以停止继续向前遍历了 ,因为前面的元素更小。
8
1 7 8 2 4 6 9 3
每一步排序结果
17
178
1278
12478
124678
1246789
12346789
1 2 3 4 6 7 8 9
由于是插入排序,是用不到swap代码的 。swap是交换类排序才用到 。
void swap(sqlist* s, int i, int j)
{
int temp = s->arr[i];
s->arr[i] = s->arr[j];
s->arr[j] = temp;
}
#define MAXSIZE 100
struct sqlist {
int arr[MAXSIZE];
int num;
};
void InsertSort(sqlist* s)
{
int i, j;
for ( i= 2; i <= s->num; i++) //控制循环次数,从第二个数开始插入
{
int key = s->arr[i];
for (j = i-1 ; j >=1; j--)
{
if (key < s->arr[j]) //一路找到适合 s->arr[i] 的地方
s->arr[j + 1] = s->arr[j]; //把 s->arr[j] 向后移动一个位置
else break;
}
s->arr[j + 1] =key;
}
}
int main()
{
sqlist *s=new sqlist;
cin >> s->num;
for (int i = 1; i <= s->num; i++)
cin >> s->arr[i];
InsertSort(s);
for (int i = 1; i <=s->num; i++)
cout << s->arr[i] << ' ';
}
核心部分
void InsertSort(sqlist* s) //下标从1到 s->num .
{
int i, j;
for ( i= 2; i <= s->num; i++) //控制循环次数,从第二个数开始插入
{
int key = s->arr[i];
for (j = i-1 ; j >=1; j--)
{
if (key < s->arr[j]) //一路找到适合 s->arr[i] 的地方
s->arr[j + 1] = s->arr[j]; //把 s->arr[j] 向后移动一个位置
else break;
}
s->arr[j + 1] =key;
}
}
易错;
void InsertSort(sqlist* s)
{
int i, j;
for ( i= 2; i <= s->num; i++) //控制循环次数,从第二个数开始插入
{
for (j = i-1 ; j >=1; j--)
{
if (s->arr[i]< s->arr[j]) //一路找到适合 s->arr[i] 的地方
s->arr[j + 1] = s->arr[j]; //把 s->arr[j] 向后移动一个位置
else break;
}
s->arr[j + 1] =s->arr[i];
}
}
// 由于把 s->arr[j] 向后移了一个单位,故会改变 s->arr[i] 的值 ,(在同一个数组中操作时需要注意的地方) ,因此需要设置一个变量存储 。
改进
void InsertSort(sqlist* s)
{
int i, j;
for (i = 2; i <= s->num; i++)
{
if (s->arr[i] < s->arr[i - 1]) // 只有在此情况下才需要进入循环,否则不动即可。
{
s->arr[0] = s->arr[i]; // s->arr[0] 用来存储s->arr[i] 的值 ,且可以作为哨兵,这样下面的for循环条件控制不用控制 j>0 ,因为 j=0 时也不满足 s->arr[j] > s->arr[0]
//将 s->arr[j] > s->arr[0] 作为循环条件的好处是减少了分支的判断 。不满足自动退出循环
for (j = i - 1; s->arr[j] > s->arr[0]; j--)
s->arr[j + 1] = s->arr[j];
s->arr[j + 1] = s->arr[0];
}
}
}
插入排序的改进:希尔排序
1.当记录本身是基本有序的,只需要少量的插入操作,就可以完成整个记录集的排序工作,此时直接插入很高效,
2.当数据比较少时,直接插入有优势
如何让待排序的记录个数较少? 分组
即将原本有大量记录数的记录进行分组 ,此时每个子序列待排序的记录个数就较少了 。然后在这些子序列内分别进行直接插入排序 ,当整个序列基本有序时,再对全体记录进行一次直接插入排序 。
跳跃分割
将相距某个增量的记录组成一个子序列 ,这样才能保证在子序列内分别进行直接插入排序后的结果是基本有序而不是局部有序
ShellSort的 步骤即为
1.确定增量,根据增量分组
2.对每组内的数据进行直接插入排序( 第一个数默认为组内第一个,然后从第二个开始一个一个插入 )
void InsertSort(sqlist* s, int increment)
{
int i, j;
for (i = increment + 1; i <= s->num; i++) //之所以是increment+1 ,是因为从组内第二个开始插入
{
s->arr[0] = s->arr[i];//待插入数据
for (j = i - increment; j > 0 && s->arr[j] > s->arr[0]; j -= increment) //第一次检查是否s->arr[j] > s->arr[0],若不满足则不需要任何调整
s->arr[j + increment] = s->arr[j];
s->arr[j + increment] = s->arr[0];
}
}
void ShellSort(sqlist* s)
{
int increment = s->num;
while (increment != 1)
{
increment = increment / 3 + 1;
InsertSort(s, increment);
}
InsertSort(s, 1);
//也可以用do{}while() ;
}