1.直接插入排序
直接插入排序是非常简单的一种排序算法,它的基本操作是将一个记录插入到已经排序好的有序表中,从而得到一个新的有序表。
给定关键字序列 5 2 1 3 4
从第一趟排序中,从序列的第一个关键字5开始。在第二趟中,将第二个关键字2插入到上一趟有序序列中,得到(2,5)。在第三趟中,将第三个关键字1插入到上一趟有序序列中,得到(1,2,5)。余下几趟以此类推最终得到(1,2,3,4,5)算法结束。
需要注意的是在把关键字1插入过程中,进行了比较,发现1<5,则把5往后移动一个位置,覆盖原来的关键字1,再将1与2比较,发现1<2,则把2往后移动一个位置,覆盖原来的关键字5。
算法描述:
/*输入参数a:待排序的数据,这里为指向一个整形数组的指针
len:关键字的长度
void InsertSort(int *a,int len)
{
int begin = 1;
int i = 0;
while(begin < len)
{
int key = a[begin];
for(i = begin - 1;i>=0;i++){
if(a[i]<=key){
a[i+1] = key;
break;
}
a[i+1] = a[i];
}
if(i<0)
a[0] = key; //所有关键字都查找后没有找到
begin++;
}
}
- 平均时间复杂度:o(N^2)
- 最好时间复杂度:o(N),如果有序,那么每个元素都已经在在它的待排子序列的合适位置,不用找合适位置
- 最坏时间复杂度:o(N^2)
- 空间复杂度:o(1)
- 稳定性:稳定的
- 复杂性:简单
2.希尔排序
基本思想:先将整个待排序记录序列分割成若干个子序列分别进行直接插入排序,待每个子序列都排序完成,再进行一次整体的直接插入排序。
给定关键序列: 3 4 7 2 1 5 8 6
第一趟每隔3个元素一组,组内进行直接插入排序(1,3)(4,5)(7,8)(2,6)。1 4 7 2 3 5 8 6 第二趟希尔排序,每隔两个元素一组,组内进行直接插入排序1 3 5 2 4 7 8 6。第三趟希尔排序,每隔一个元素一组,组内进行直接插入排序1 2 4 3 5 6 8 7。第四趟希尔排序,每隔0个元素一组,组内进行直接插入排序1 2 3 4 5 6 7 8 。
算法描述:
/*输入参数a:待排序的数据,这里指向为一个整形数组的指针
len:关键字的长度
void ShellSort(int* a,int len)
{
/gap:序列间的间隙
int gap = len;
while(gap > 1){
gap = gap/3 + 1;
for(int i = gap;i < len; i++)
{
int key = a[i];
int start = i-gap;/同序列中后一个元素
while(start >= 0 && key <= a[start])
{
a[start+gap] = a[start];
start -= gap;
}
a[start + gap] = key;
}
}
}
- 平均时间复杂度:o(N^1.3)
- 最好时间复杂度:仍然和增量序列的选取有关
- 最坏时间复杂度:o(N^2)
- 空间复杂度:o(1)
- 稳定性:不稳定,例如待排序列(1,2,4,2),取d=1时,显然就能看出是不稳定的
- 复杂性:复杂
3.交换排序
交换排序我在这里将简单详述下冒泡排序和快速排序。
3.1冒泡排序
基本思路:将第一个记录的关键字和第二个记录的关键字进行比较,若为逆序,则将两记录交换,然后比较第二个记录和第三个记录的关键字,以此类推,直到第n-1个记录和第n个记录的关键字比较完成为止。
给定的关键字序列 3 2 5 6 1 4
第一趟冒泡排序,6个关键字两两比较,必要时进行交换,这样找到了最大的关键字6排在最末尾。第二趟在前5个关键字中继续进行两两比较,找到关键字5,以此类推直到所有记录都排序完成为止。
算法描述:
/*输入参数a:待排序的数据
n:关键字数量*/
void BubbleSort(int a[],int n)
{
int temp;
int flag=1;/*是否存在交换的标志*/
/*n个关键字,最多需要n-1次冒泡处理*,以后每次递减1/
int j=n-1;
while(flag){
flag = 0;/*每次冒泡排序前,标志位清0*/
for(int i=1;i<=j;i++){
if(a[i-1]>a[i]){
temp = a[i];
a[i] = a[i-1];
a[i-1] = temp;
flag = 1;
}
}
j--;
}
}
- 平均时间复杂度:o(N^2),嵌套双循环
- 最好时间复杂度:o(N),若已经有序,那么第一趟就排好了
- 最坏时间复杂度:o(N^2)
- 空间复杂度:o(1)
- 稳定性:稳定的
- 复杂性:简单
3.2快速排序
基本思想:快排是对冒泡的一种改进,首先在序列中任选一个元素ai,将选择的记录作为划分标准,通过交换使ai前的关键字都比ai小,后面的关键字都比ai大。
给定关键字序列 4 3 6 2 8 5 1 7
a[0] a[1] a[2] a[3] a[4] a[5] a[ 6] a[7]
初始序列中含有8个关键字,我们选择第一个元素4作为换份标准。通过4进行划分,序列被分为两部分,1 3 2和6 8 5 7。然后对这两个序列分别用1和6作为划分标准,用同样的方法继续进行划分,直到每个子序列都划分完成为止。
在这里我对第一趟序列进行描述:设定两个指针,指针i指向第一个元素4,指针j指向最后一个元素7。从指针j开始向前检查每个关键字,直到找到小于划分元素4的元素为止,在这里我们找到的是1。将元素1移动到指针i所指的位置即4的位置。并将指针i向后移动一个位置。从此刻i开始向后检查每个关键字直到发现大于4的元素6。将指针6移动到指针j所指的位置及此时a[6]的位置,并将指针j往前移动一个元素。并继续检查小于4的元素,发现元素2,把该元素移动到指针i所指向的位置,同时指针i往后移动一个元素。此时i,j指向同一个元素2,则结束划分并把元素放在a[3]位置下。
算法描述:
/* 输入参数a:待划分的数据,这里指向为一个整形数组的指针
i:带划分序列首元素的下标
j:带划分序列尾元素的下标*/
int partition(int a[],int i,int j)
{
int temp = a[i]; //选择首元素为划分元素
while(i < j) /*当i>=j时,结束划分
{
while(a[j] >=temp && i<j)
j--; //从后往前找到第一个小于划分元素的元素
if(i<j){
a[i++]=[j]; //若找到则移动元素,并将下标i加1
}
while(a[i]<=temp && i<j)
i++;//从前往后找到第一个大于划分元素的元素
if(i<j){
a[j--]=a[i];//若找到则移动元素,并将下标i减1
}
}
a[i] = temp; //划分完毕将划分元素设置为目标位置及上述中4最终位置a[3]
return i; //返回划分元素所在的下标
}
- 平均时间复杂度:o(NlogN)
- 最好时间复杂度:o(NlogN)
- 最坏时间复杂度:o(N^2)
- 空间复杂度:o(logN)
- 稳定性:不稳定的
- 复杂性:复杂