voidBubbleSort(int* a, int n){
int i = 0;
int j = 0;
int flag = 0;
for(i = 0; i < n - 1; i++) {
flag = 0;
for(j = 0; j < n - 1 - i; j++){
if(a[j] > a[j + ]){
Swap(&a[j], &a[j + ]);
flag = ;
}
}
(!flag) ;
}
}
1
1
1
if
break
3. 代码分析
时间复杂度:O(N^2)。
稳定性:稳定,因为遇到相等的值不会交换位置。
二、选择排序
1. 实现逻辑
每次遍历一边数组,把最小最大的值选出,然后分别置于 start 位置和 end 位置。
start:开始是要排的数组的开头,每选一个最小值 +1;
end:开始是要排的数组的末尾,每选一个最小值 -1;
2. 实现代码
voidSelectSort(int* a, int n){
int i = 0;
int j = 0;
int start = 0;
int end = n - 1;
int min = 0;
int max = 0;
while(start < end){
min = start;
max = start;
for(i = start; i <= end; i++){
if(a[i] > a[max]) max = i;
if(a[i] < a[min]) min = i;
}
/* if (max != start) { Swap(&a[start], &a[min]); Swap(&a[end], &a[max]); } else { Swap(&a[end], &a[max]); Swap(&a[start], &a[min]); }*/if(max == start) max = min;
Swap(&a[start], &a[min]);
Swap(&a[end], &a[max]);
++start;
--end;
}
}
注意 max 是开头元素的情况!
3. 代码分析
时间复杂度:约看作要走 n 遍数组:O(N^2)。
稳定性:不稳定,找到最值并放置最值后相同值的相对顺序可能会变。
三、插入排序
1. 实现逻辑
就像打扑克牌时我们习惯性的给手牌排序一样,先确定一个区间 (开始元素个数为 1),取该区间末尾后一位数 (temp) 与区间末尾的值进行比较:
temp < x:将 x 的值向后一位,end–, 在重复比较;
temp >= x: 将 temp 放在 x 的后面;
2. 实现代码
// 把第 end+1 位置的值插入 [0,end] 里并保持有序voidInsertSort(int* a, int n){
int i = 0;
int end = 0;//[0,end] 为有序区间的范围for(i = 0; i < n - 1; i++){
end = i;
int tmp = a[end + 1];//保存要移动的值while(end >= 0){
if(tmp < a[end]){
a[end + 1] = a[end];
--end;
}else{
break;//存在 tmp<有序区间内所有值以及找到比它小的值两种情况
}
}
a[end + 1] = tmp;
}
}
取一值 gap,将数组分成 gap 组 (想象):
第一组:从下标为 0 开始,每个 gap 个空:0,0+gap,0+gap+gap…为一组;
第二组:从下标为 1 开始,每个 gap 个空:1,1+gap,1+gap+gap…为一组;
……
第 gap 组:下标为 gap-1 开始,每个 gap 个空:gap-1,gap-1+gap…为一组;
然后对每组使用插入排序的思想对每组排序。
以上称为预排序,它会降低插入排序的负担。预排结束后再对整个数组进行插入排序即可。
但是,gap 太小预排的消耗就接近与插入排序了;gap 太大又让后面的插入排序负担变重。两种情况都会让该排序近似于插入排序,可是对于大小不同的数组,最合适的 gap 是不同的,也不好找。因此我们可以让 gap 持续变化。
2. 代码实现
voidShellSort(int* a, int n){
int i = 0;
int end = 0;
int gap = n;
//int gap = 3;//int j = 0;//for (j = 0; j < gap; j++)//{// for (i = j; i < n - gap; i += gap)// {//// end = i;// int tmp = a[end + gap];// while (end >= 0)// {//// if (tmp < a[end])// {//// a[end + gap] = a[end];// end -= gap;// }// else// break;// }// a[end + gap] = tmp;// }////}////for (i = 0; i < n - gap; i++)//{//// end = i;// int tmp = a[end + gap];// while (end >= 0)// {//// if (tmp < a[end])// {//// a[end + gap] = a[end];// end -= gap;// }// else// break;// }//// a[end + gap] = tmp;//}////简化:while(gap >1){ gap = gap /3+1;//gap 太大太小都不好,直接不停改变它;//+1 是为了让 gap 最后一定为 1//研究表明,这个值比较适合//gap 为 1 时就直接是插入排序了while(gap > 1){
gap = gap / 3 + 1;
for(i = 0; i < n - gap; i++){
end = i;
int tmp = a[end + gap];
while(end >= 0){
if(tmp < a[end]){
a[end + gap] = a[end];
end -= gap;
}elsebreak;
}
a[end + gap] = tmp;
}
}
}
一定要让 gap 最后为 1!!
3. 代码分析
时间复杂度:O(N^1.3);
稳定性:不稳定,相同值会处于不同组,相对位置不可控。
六、快排
1. 实现逻辑
可以先看其中一个逻辑再看其他的。
a. 霍尔法
取数组的某一下标为 keyi,通常取末尾或开头,这里取开头。然后定义 left 与 right 两个下标,分别从开头和末尾走,left 找比下标为 keyi 的值大的元素,right 找小的,让 left 找到的大的与 right 找到的小的交换位置,直到 left=right,将 keyi 对应的值放在它们相遇的位置。这样就把 keyi 的值固,其左边为比其小的数,右边为比其大的数。然后以 keyi 为分界点,将数组分成两份,继续该操作。
b. 挖洞法
与霍尔法类似,依旧是 left 找大的,right 找小的,不同的是,我们先存下 keyi 对应的值,走 right,找到小的后自接赋值给 left 所在位置,然后 left 走,找到大的赋值给 right 所在位置;如此往复,直至相遇;最后把 keyi 对应的值赋值给相遇的位置。
c. 快慢指针法
定义 prev(为数组第一个元素下标),cur(数组第二个元素下标)。然后让 cur 先走,如果所到下标的值小于 keyi 对应的值,则 prev 走一步在将 cur 对应的值赋给 prev;如果大于大于 keyi 对应的值,则 prev 不动,cur 继续走,如果遇到小的就重复第一种情况,找到 cur 走出数组。最后交换 keyi 与 prev 对应的值,并将 prev 赋给 keyi。
a. 试想,如果要排序的数组是已经排好序或接近排好序的,你每次取开头做 keyi 的话要这样不断的分 n 次,left 于 right 每一次都得走 n 步 (两者要相遇),这样的话时间复杂度上升到 O(N^2) 了。但是我们又不能改变算法的基本逻辑,我们该怎么办呢?
我们可以取下标为 left,right,(left + right)/2 的数据进行比较选出中间的那个数,然后把它放在 left 位置,这样我们可以把每次分割都看成对半分 (实际不是,粗略估算),这样时间复杂度就来到 O(NlogN) 了。
给出一组数据,找出 max 与 min,设 range = max-min+1,开辟一个可以存 range 个数据的空间 (要初始化为 0);其中 min 对应新数组的中下标为 0 的位置,max 对应末尾下标,即 max-min;令数组中有一值为 x,则 x 对应新数组的下标为:x-min;
然后遍历原数组,每遍历到一个数,将该数在新数组中对应的位置的值 +1;遍历完后,遍历新数组,将新数组中的值当成个数 m,下标为 i,将 m 个 i+min 写入原数组中。
2. 代码实现
voidCountSort(int* a, int n){
int min = a[0];
int max = a[0];
int i = 0;
int range = 0;
int j = 0;
for(i = 1; i < n; i++)//找到最值
{
if(a[i] > max) max = a[i];
if(a[i] < min) min = a[i];
}
range = max - min + 1;
//printf("%d\n", range);int* count = (int*)calloc(range, sizeof(int));//因为要初始化为 0,所以用 callocif(!count){
perror("CountSort:error calloc");
return;
}
for(i = 0; i < n; i++)//记下 a 中各数出现的次数
{
count[a[i]- min]++;
}
for(i = 0; i < range; i++)//这个嵌套的时间复杂度为 O(N)
{
while(count[i]--){
a[j++]= i + min;
}
}
free(count);
}