七、冒泡排序
1.简介
冒泡排序可以说是我们的老朋友了,是一种很简单的排序方法。冒泡就是泡泡在水中向上漂,很形象的名字和贴合它的思路,通过一趟趟的冒泡每一次将最大的元素冒到最后的位置处,这样就完成了数据的排序。
2.思路与代码
冒泡排序无需多言,就是比较相邻元素大小,将较大者不断后移。我们在以前的博客中介绍过冒泡排序,http://t.csdnimg.cn/sB0Yp,有需要可以去瞧瞧哈
void BubbleSort(int* a, int n)
{for (int j = 0; j < n; j++){bool flag = true;for (int i = 0; i < n - 1 - j; i++){if (a[i] > a[i + 1]){swap(&a[i], &a[i + 1]);flag = false;}}if (flag){break;}}
}
3.复杂度与稳定性分析
(1)时间复杂度
冒泡排序时间复杂度不难计算,由于我们加入了flag作为判断,所以当为顺序的情况时只需要遍历一遍,其时间复杂度是。而逆序是最差情况,是个标准的等差数列,时间复杂度为。
一般认为冒泡排序的时间复杂度是。
(2)空间复杂度
冒泡排序没有使用额外的空间,其空间复杂度为。
(3)稳定性
冒泡排序是稳定的。
冒泡排序比较相邻元素并进行交换,这就意味着相对位置在前的元素想要换到相对位置在后的元素后面,必须要越过这个靠后的元素,即二者一定会存在比较。只需要限定一下当一次比较时二者相等不进行交换,这样就可以保证稳定性。
八、计数排序
1.简介
计数排序给我的感觉就是众多排序算法中的民间高手。虽然方法不入流,适用范围不广,但是架不住它真的快。排序算法简单说就是统计所有数据出现的次数,然后根据统计的次数从小到大再将数据依次输出。说它不入流是因为我感觉计数排序并非真正的排序,这种统计次数再输出的方式并不是对于原数组进行数据的处理,这也就注定了它的适用范围并不会很广。计数排序只能用于数据跨度较小的整数排序上。
2.思路与代码
计数排序使用一个计数数组来记录数组过程中各个元素出现的次数,计数数组的下标代表着各种值,下标位置存储的数据则是出现次数。
为了节约空间与拓展统计区间,我们一般会先遍历数组找到最大值和最小值。根据这个最大值和最小值得到所需要统计数据的区间,并以此为依据进行数组开辟。同时可以将最小值设置为偏移量,根据最小值的大小确定数组0下标位置存储的数据是谁的。
之后再遍历数组进行计数,然后再遍历计数数组进行数据写出即可。
void CountSort(int* a, int n)
{//找到偏移量int min = a[0], max = a[0];for (int i = 0; i < n; i++){if (a[i] < min){min = a[i];}if (a[i] > max){max = a[i];}}int range = max - min + 1;int* cou = (int*)calloc(range, sizeof(int));if (cou == NULL){perror("calloc fail");return;}//计数for (int i = 0; i < n; i++){cou[a[i] - min]++;}//排序int k = 0;for (int i = 0; i < range; i++){for (int j = 0; j < cou[i]; j++){a[k++] = i + min;}}
}
3.复杂度与稳定性分析
(1)时间复杂度
对于计数排序,假设其跨度为m,它存在着三次遍历,前两次是对原数组的遍历(n),最后一次是对计数数组与其值的遍历(n+m)。因此其时间复杂度与数据规模n和数据跨度m都有关系,时间复杂度为。
(2)空间复杂度
计数排序使用了计数数组,计数数组的规模是根据待排序数组的跨度m而定的,所以其空间复杂度为。
(3)稳定性
计数排序是稳定的。
对于计数排序而言,因为我们是遍历其原数组进行对计数数组的写入再输出,根据遍历访问的先后顺序我们是有办法控制其稳定的,如利用队列等控制同一个值的先入先出。指的一提的是稳定性的意义更多用于复杂对象,我们根据其某一个指标排序而维持此前的相对位置,所以如果需要稳定的计数排序,以我现在的水平来说,我会选择使用队列来存储每个计数背后的复杂对象来实现先进先出。