【8.2插入类、8.3交换类、8.4选择类、8.5归并类、8.6分配类 都属于内部排序。 】
8.2 插入排序
8.2.1 直接插人排序
【算法特点】
(1)稳定排序。
(2)算法简便,且容易实现。
(3)也适用于链式存储结构,只是在单链表上无需移动记录,只需修改相应的指针。
(4)更适合于初始记录基本有序(正序)的情况。
当初始记录无序,n较大时,此算法时间复杂度较高,不宜采用。
c
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 20
typedef int KeyType;
typedef char InfoType;
typedef struct
{
KeyType key;
InfoType otherinfo;
}RedType;
typedef struct
{
RedType r[MAXSIZE + 1]; //r[O]闲置或用做哨兵单元
int length;
}SqList;
void CreateSqList(SqList& L);
void InsertSort(SqList& L);
void printSqList(SqList L);
int main()
{
SqList L = { {0},0 };
CreateSqList(L);
InsertSort(L);
printSqList(L);
return 0;
}
void CreateSqList(SqList& L)
{
int i = 0;
printf("请输入顺序表的元素个数:");
scanf_s(" %d", &L.length);
for (i = 1; i <= L.length; i++)
{
printf("请输入第%d个关键字:", i);
scanf_s(" %d", &L.r[i].key);
}
}
//算法8.1 直接插入排序
void InsertSort(SqList& L)
{
int i = 0;
int j = 0;
for (i = 2; i <= L.length; i++)
{
if (L.r[i].key < L.r[i - 1].key)
{
L.r[0] = L.r[i];
L.r[i] = L.r[i - 1]; //L.r[i-1]后移
for (j = i - 2; L.r[0].key < L.r[j].key; --j)
{
L.r[j + 1] = L.r[j];
}
L.r[j + 1] = L.r[0];
}
}
}
void printSqList(SqList L)
{
int i = 0;
printf("\n\n排序后的序列为:");
for (i = 1; i <= L.length; i++)
{
printf("\nr[%d].key = %d", i, L.r[i].key);
}
}
8.2.2 折半插人排序
【算法特点】
(1)稳定排序。
(2)因为要进行折半查找 , 所以只能用于顺序结构,不能用于链式结构。
(3)适合初始记录无序、n较大时的情况。
c
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 20
typedef int KeyType;
typedef char InfoType;
typedef struct
{
KeyType key;
InfoType otherinfo;
}RedType;
typedef struct
{
RedType r[MAXSIZE + 1]; //r[O]闲置或用做哨兵单元
int length;
}SqList;
void CreateSqList(SqList& L);
void BInsertSort(SqList& L);
void printSqList(SqList L);
int main()
{
SqList L = { {0},0 };
CreateSqList(L);
BInsertSort(L);
printSqList(L);
return 0;
}
void CreateSqList(SqList& L)
{
int i = 0;
printf("请输入顺序表的元素个数:");
scanf_s(" %d", &L.length);
for (i = 1; i <= L.length; i++)
{
printf("请输入第%d个关键字:", i);
scanf_s(" %d", &L.r[i].key);
}
}
//算法8.2 折半插入排序
void BInsertSort(SqList& L)
{
int i = 0;
int j = 0;
int low = 1;
int high = 1;
int mid = 0;
for (i = 2; i <= L.length; ++i)
{
L.r[0] = L.r[i]; //无序序列的第一个下标/位置数i,暂存到监视哨中
low = 1; //(非递减)有序序列的最小下标/位置数low
high = i - 1; //(非递减)有序序列的最大下标/位置数high
//折半查找的非递归算法
while (low <= high)
{
mid = (low + high) / 2;
if (L.r[0].key < L.r[mid].key)
{
high = mid - 1;
}
else //if(L.r[0].key >= L.r[mid].key). 当L.r[0].key == L.r[mid].key时,也是low=mid+1,保证了本折半插入排序的稳定性
{
low = mid + 1;
}
}
/*在折半查找中,当low > high时,跳出while循环。
跳出后,L.r[0]即原L.r[i]的插入点在L.r[high+1]处,也就是最后的L.r[mid]处(此时mid = low = high+1. 本来三者相等,high由于L.r[0].key < L.r[mid].key,等于mid-1而小于了low);
或者是在L.r[low]处,也就是最后的L.r[mid+1]处(此时mid = high = low-1. 本来三者相等,low由于L.r[0].key >= L.r[mid].key,等于mid+1而大于了high)*/
//有序序列中,从L.r[high+1](包括L.r[high+1],j取的最小值为high+1)到L.r[i-1](j取得最大值为i-1),每个记录都往后移
for (j = i - 1; j >= high + 1; --j)
{
L.r[j + 1] = L.r[j];
}
L.r[high + 1] = L.r[0];
}
}
void printSqList(SqList L)
{
int i = 0;
printf("\n\n排序后的序列为:");
for (i = 1; i <= L.length; i++)
{
printf("\nr[%d].key = %d", i, L.r[i].key);
}
}
8.2.3 希尔排序
【算法特点】
(1)记录跳跃式地移动导致排序方法是不稳定的。
(2)只能用于顺序结构,不能用于链式结构。
(3)增量序列可以有各种取法,但应该使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1。
(4)记录总的比较次数和移动次数都比直接插入排序要少,n越大时,效果越明显。所以同折半插入排序一样,适合初始记录无序、n较大时的情况。
c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAXSIZE 26
typedef int KeyType;
typedef char InfoType;
typedef struct
{
KeyType key;
InfoType otherinfo;
}RedType;
typedef struct
{
RedType r[MAXSIZE + 1]; //r[O]闲置或用做哨兵单元
int length;
}SqList;
void CreateSqList(SqList& L);
void ShellInsert(SqList& L, int dk);
void ShellSort(SqList& L, int dt[]);
int checkPrimeNumber(int n);
void PrimeNumber(SqList L, int dt[], int& len);
void printSqList(SqList L);
int main()
{
SqList L = { {0},0 };
int dt[MAXSIZE] = {0};
CreateSqList(L);
ShellSort(L,dt);
printSqList(L);
return 0;
}
void CreateSqList(SqList& L)
{
int i = 0;
printf("请输入顺序表的元素个数:");
scanf_s(" %d", &L.length);
for (i = 1; i <= L.length; i++)
{
printf("请输入第%d个关键字:", i);
scanf_s(" %d", &L.r[i].key);
}
}
//算法8.3 希尔(插入)排序
//对顺序表L做一趟增量是dk的希尔插入排序
void ShellInsert(SqList& L, int dk)
{
int i = 0;
int j = 0;
//分组之后,第k组的元素下标/位置数为:k, dk+k, 2dk+k, 3dk+k, ...(k = 1,..,dk)
for (i = dk + 1; i <= L.length; ++i)
{
if (L.r[i].key < L.r[i - dk].key)
{
L.r[0] = L.r[i]; //暂存在L.r[O]中,L.r[O]不是哨兵
for (j = i - dk; j>0 && L.r[0].key < L.r[j].key; j-=dk) //j = j-dk
{
L.r[j+dk] = L.r[j]; //比较的同时,记录后移
}
L.r[j + dk] = L.r[0];
}
}
}
//按增量序列 dt[O...len-1]对顺序表 L作len 趟希尔排序
//对顺序表L完成一次完成的希尔插入排序
void ShellSort(SqList &L,int dt[])
{
int i = 0;
int len = 0;
PrimeNumber(L, dt, len);
for (i = 0; i < len; i++)
{
ShellInsert(L, dt[i]);
}
}
//应使增量序列dt中的值没有除 l 之外的公因子
//取[1,L.length]范围内所有的质数,从大到小存入dt数组中
void PrimeNumber(SqList L,int dt[],int &len)
{
int i = 0;
int status = checkPrimeNumber(0);
int k = 0;
for (i = L.length; i >= 1; i--)
{
status = checkPrimeNumber(i);
if (status)
{
dt[k] = i;
k++;
}
}
dt[k] = 1; //增量序列最后一个增量值必须等于1
len = k+1;
printf("\n\n该序列的增量序列为:");
for (i = 0; i < len; i++)
{
printf("%d ", dt[i]);
}
}
//判断一个数是否是质数
int checkPrimeNumber(int n)
{
int i = 0;
int sq = floor(sqrt(n));
if (n <= 1)
{
return 0;
}
if (n == 2 || n == 3)
{
return 1;
}
//只有6x-1和6x+1的数才有可能是质数(但不一定就是,如n=35,还需要继续判断)
if (n % 6 != 1 && n % 6 != 5) //n=4和n=6时,n%6满足该if条件,返回false,正好符合情况
{
return false;
}
for (i = 5; i <= sq; i += 6)
{
if (n % i == 0 || n % (i + 2) == 0)
{
return 0;
}
}
//n = 5 和 n=7 时,不会进入if判断语句,也不会进入for循环,而是直接返回true,此时也判断正确
return true;
}
void printSqList(SqList L)
{
int i = 0;
printf("\n\n排序后的序列为:");
for (i = 1; i <= L.length; i++)
{
printf("\nr[%d].key = %d", i, L.r[i].key);
}
}