内部排序之二:交换排序

来源:互联网 发布:模拟城市 mac 破解版 编辑:程序博客网 时间:2024/05/22 10:45

    交换排序


   冒泡排序

   基本思想很简单,如果要求排序后序列中元素按照从小到大的顺序排列,则冒泡排序的步骤如下:

    1、依次比较序列中相邻的两个元素,将较大的放在后面,这样一趟比较后,最大的元素就放在了最后的一个位置;

    2、再依次比较相邻的两个元素,将第二大的元素最终放到倒数第二个位置;

    3、依次循环,直到最小的元素放在了第一个位置,排序完成。

    根据以上思想,很容易写出实现代码

/*冒泡排序后的顺序为从小到大*/void BubbleSort(int[] array){int i,j,temp;for(i=0;i<array.length-1;i++)for(j=0;j<len-i-1;j++)if(arr[j] > arr[j+1]){temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}

冒泡排序最好的时间复杂度为O(n),最坏的时间复杂度为O(n^2),平均时间复杂度为O(n^2)。

我们回过头来再来看上面冒泡排序的思想,无论原始序列的排序是怎样的(哪怕已经是从小到大排好的),它都要进行n-1趟比较,每趟比较又要进行n-i-1次相邻元素间的比较(i为从0开始计的第i趟比较),而实际上,很有可能还没有进行第n-1趟比较,已经完成了排序,这时候后面的几趟比较就显得多余了,基于此,我们可以做如下改进:设置一个标志位,如果某一趟有元素发生交换,则为true,继续下一趟比较,否则,说明排序已经完成,则标志位为false,退出循环。改进后的实现代码如下:

public class BubbleSort {public static void main(String args[]) {int[] arr = { 17, 15, 13, 11, 9, 7, 5, 3, 1 };print(BubbleSort.bubbleSort(arr));}public static int[] bubbleSort(int[] array) {int i, j, len = array.length;int temp;boolean flag = true;// //增设标志位,判断是否已经完成排序for (i = 0; i < len - 1 && flag; i++) {flag = false;for (j = 0; j < len - i - 1; j++) {// /对当前无序区间[0......len-i-1]进行排序(j的范围很关键,这个范围是在逐步缩小的)// 如果本趟比较没有数据发生交换,说明排序已经完成,则flag一直为false,从而退出循环,不再进行下一趟的比较if (array[j] > array[j + 1]) {temp = array[j];array[j] = array[j + 1];array[j + 1] = temp;flag = true;}}}return array;}public static void print(int[] array) {for (int i = 0; i < array.length; i++) {System.out.print(array[i] + " ");}}}


快速排序

快速排序是已知的平均时间复杂度均为O(n*logn)的几种排序算法中效率最高的一个,该算法之所以特别快,主要是由于非常精炼和高度优化的内部循环,它在最坏情况下的时间复杂度为O(n^2),但只要稍加努力(正确选择枢轴元素)就可以避免这种情形。快速排序采用了分治思想,其具体基本思想如下:

    1、从待排序列中任选一个元素作为枢轴;

    2、将序列中比枢轴大的元素全部放在枢轴的右边,比枢轴小的元素全部放在其左边;

    3、以枢轴为分界线,对其两边的两个子序列重复执行步骤1和2中的操作,直到最后每个子序列中只有一个元素。

    一趟快速排序(以排序后从小到大为例)的具体做法如下:

    附设两个元素指针low和high,初值分别为该序列的第一个元素的序号和最后一个元素的序号,设枢轴元素的值为temp,则首先从high所指位置起向前搜索到第一个值小于temp的元素,并将其和temp互换位置,而后从low所指位置起向后搜索到第一个值大于temp的元素,并将其和temp交换位置,如此反复 ,直到low=high为止。

    在内部排序中为了尽量避免比较多的元素交换操作,因此下面的分析和代码的实现中,我们并不是采取交换操作,而是先将枢轴元素保存在val变量中,然后每次遇到需要交换的元素时,先将该元素赋给val所在的位置,而后再将该元素所在位置“挖空”,之后的每一次比较,就用需要交换的元素来填充上次“挖空”的位置,同时将交换过来的元素所在的位置再“挖空”,以等待下次填充。

public class QuickSort {public static void main(String args[]) {int[] arr = { 3, 1, 5, 4, 9, 8, 15, 18, 2 };print(QuickSort.quickSort(arr, 0, arr.length - 1));}public static int[] quickSort(int[] array, int low, int high) {int pos;if (low < high) {pos = findPos(array, low, high);// 通过递归实现排序quickSort(array, low, pos - 1); // 左子序列排序quickSort(array, pos + 1, high); // 右子序列排序}return array;}/** * 该函数返回分割点数值所在的位置  * low刚开始表示排序范围内的第一个元素的位置,逐渐向右移动 * high刚开始表示排序范围内的最后一个位置,逐渐向左移动 */public static int findPos(int[] array, int low, int high) {int temp = array[low];while (low < high) {while (low < high && array[high] >= temp) {high--;}array[low] = array[high];while (low < high && array[low] <= temp) {low++;}array[high] = array[low];}// 最终low=higharray[low] = temp;return low;}public static void print(int[] array) {for (int i = 0; i < array.length; i++) {System.out.print(array[i] + " ");}}}

         PS:

        通常,快速排序被认为在所有同数量级(平均时间复杂度均为O(n*logn))的排序方法中,平均性能最好。但是若初始记录已经基本有序,这样每次如果还选择第一个元素作为枢轴元素,则再通过枢轴划分子序列时,便会出现“一边倒”的情况,此时快速排序就完全成了冒泡排序,这便是最坏的情况,时间复杂度为O(n^2)。所以通常枢轴元素的选择一般基于“三者取中”的原则,即比较首元素、末元素、中间元素的值,取三者中中间大小的那个。经验表明,采取这种方法可以大大改善快速排序在最坏情况下的性能。

       快速排序需要一个栈来实现递归,很明显,如果序列中的元素是杂乱无章的,而且每次分割后的两个子序列的长度相近,则栈的最大深度为O(logn),而如果出现子序列“一边倒”的情况,则栈的最大深度为O(n)。因此就平均情况来看,快速排序的空间复杂度为O(logn)。

0 0
原创粉丝点击