1. 基本思想
* 递归比较相邻的两个数,若前面的数比后面的数大,则进行交换。这样,最大的数就被交换到了最后。
* 重复执行上面的过程,直到没有需要交换(用一个flag记录)为止。
2. 简单实现
void bubble_sort(int array[], int len) { int i, j, temp, flag; for(i = 0; i < len-1; i++){ flag = 0; for(j = 0; j < (len-1-i); j++){ if(array[j] > array[j+1]){ temp = array[j]; array[j] = array[j+1]; array[j+1] = temp; flag = 1; } } if(flag == 0){ break; } } }
3. 冒泡排序算法改进
对于冒泡排序来说,元素的位置对性能影响很大。若值较大的数在数列的开头附件位置,这样的结点我们称之为兔子,因为这些结点不会对性能产生较大的影响,它们可以很快地被交换到最后。而对于值较小且位置靠近数列结尾,这样的结点我们称之为乌龟,因为这些结点被移动到开头的过程是非常缓慢的,因此,对性能有极大的影响。
有一些方法可以用来消除乌龟结点,以改进冒泡排序的性能。
方法一、Cocktail排序
Cocktail排序算法又叫双向冒泡排序,基本思想是不断递归操作子数列,将最大值交换到数列末尾后,再将最小值交换到数列开头,如此反复,直到不需要交换为止。
void cocktail_sort(int array[], int len) { int i, temp, flag; int head, tail; for(head = 0, tail = len-1; head < tail; ){ //From head to tail, move the large number to the tail flag = 0; for(i = head; i < tail; i++){ if(array[i] > array[i+1]){ temp = array[i]; array[i] = array[i+1]; array[i+1] = temp; flag = 1; } } if(flag == 0)break; tail--; //From tail to head, move the small number to the head flag = 0; for(i = tail; i > head; i--){ if(array[i] < array[i-1]){ temp = array[i]; array[i] = array[i-1]; array[i-1] = temp; flag = 1; } } if(flag == 0)break; head++; } }
方法二、Comb排序
与Cocktail排序不同,Comb排序算法是从交换结点间隔角度来消除乌龟结点的。对于普通的冒泡排序来说,都是对相邻结点进行比较交换的。Comb排序则对一定间隔的数列结点进行比较交换。结点间隔一开始为数列的长度,每次执行后,则收缩结点间隔,直到结点间隔为1。收缩因子一般为1.247330950103979。
void comb_sort(int array[], int len) { float shrink_factor = 1.247330950103979; int gap = len; int flag = 1, i, tmp; while(gap > 1 || flag){ if(gap > 1){ gap = gap/shrink_factor; } flag = 0; i = 0; while((gap+i) < len){ if(array[i] > array[i+gap]){ tmp = array[i]; array[i] = array[i+gap]; array[i+gap] = tmp; flag = 1; } i++; } } }
待续...
4. 应用场景
冒泡排序的效率相对于其他好的排序算法来说是比较差的。不过,在某些场景下,比如,带时间的日志记录整体上是按时间顺序排列的,只有很少一些日志记录在很小的范围内顺序是乱的,这种情况下,用冒泡排序会更好些。