排序算法一(直接选择,堆排序,冒泡排序和快速排序)

来源:互联网 发布:实用 知乎 编辑:程序博客网 时间:2024/06/09 04:31

对于一个排序算法来说,一般从如下3个方面衡量算法的优劣:

1.时间复杂度:它主要是分析关键字的比较次数和记录的移动次数

2.空间复杂度:分析排序算法中需要多少辅助内存

3.稳定性:若两个记录A和B的关键字相等,排序前后二者的先后次序没有发生变化就称之为稳定排序

现有的排序算法分为:内部排序和外部排序

内部排序:所有的操作都是在内存中完成的,而外部排序需要借助外部存储器(如磁盘等)

外部排序最常用的方法是多路归并排序,即将原先的文件分解为多个能够一次性装入内存的部分,

分别把每一部分调入内存完成排序,接下来再对多个有序的子文件进行归并排序。

接下来主要介绍内部排序,分为:

选择排序(直接选择和堆排序),交换排序(冒泡和快速),插入排序(直接插入,折半插入和Shell排序),归并排序,基数排序和桶式排序。

本文介绍将介绍选择排序和交换排序

数组元素类DataWrap:flag用来标记相同元素出现的次数,a 表示第一次出现,b表示第二次出现,以此类推,用来测试算法的稳定性

package sort_lyf;public class DataWrap implements Comparable<DataWrap> {int data;String flag;public DataWrap(int data,String flag){this.data = data;this.flag = flag;}public String toString(){return data+flag;}public static void swap(DataWrap[] data, int i, int j) {DataWrap temp = data[i];data[i] = data[j];data[j] = temp;}@Overridepublic int compareTo(DataWrap datawrap) {return this.data > datawrap.data ? 1:(this.data == datawrap.data? 0:-1);}}

算法的接口类:ISort(采用策略模式)

package sort_lyf;public interface ISort {public void sort(DataWrap[] data);}

算法的实现类:

package sort_lyf;/** * @author liyafang 直接选择排序: * 程序将记录定位在第一个数据上,拿第一个数据和后边的数据依次比较, * 如果第一个数据大于后边的数据,发生交换,这样第一趟下来,就选出 * 最小的一个元素了,它被排在了第一位,然后在定位第二个数据。 */public class DirectSelectSort implements ISort {public  void sort(DataWrap[] data) {System.out.println("开始进入直接选择排序:");int arrayLength = data.length;for (int i = 0; i < arrayLength - 1; i++) {// 外层循环需要遍历到倒数第二个元素for (int j = i + 1; j < arrayLength; j++) {// 内层循环需要遍历到倒数第一元素if (data[i].compareTo(data[j]) > 0) {DataWrap.swap(data, i, j);}}System.out.println(java.util.Arrays.toString(data));}// 存在的问题:在每趟比较过程中,程序一旦发现某个数据比第一// 位的数据小,立即交换他们,这增加了交换次数,导致算法效率低下。}}
排序之前:[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]开始进入直接选择排序:[9a, 30a, 49, 30b, 21a, 16, 9b, 21b][9a, 9b, 49, 30b, 30a, 21a, 16, 21b][9a, 9b, 16, 49, 30a, 30b, 21a, 21b][9a, 9b, 16, 21a, 49, 30b, 30a, 21b][9a, 9b, 16, 21a, 21b, 49, 30a, 30b][9a, 9b, 16, 21a, 21b, 30a, 49, 30b][9a, 9b, 16, 21a, 21b, 30a, 30b, 49]排序之后:[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]

package sort_lyf;/** * @author liyafang 优化后的直接选择排序算法: * 用一个变量记录本趟最小元素的索引,每趟选择 * 只发生一次交换 */public class DirectSelectSortOptimized implements ISort {// public void sort(DataWrap[] data) {System.out.println("开始进入优化版的直接选择排序:");int arrayLength = data.length;for (int i = 0; i < arrayLength - 1; i++) {int minIndex = i;// 永远保留本躺比较中最小值的索引for (int j = i + 1; j < arrayLength; j++) {if (data[minIndex].compareTo(data[j]) > 0) {minIndex = j;}}if (minIndex != i) {DataWrap.swap(data, i, minIndex);}System.out.println(java.util.Arrays.toString(data));}}// 小结:对于直接选择排序,假如有n项数据,数据交换的次数最多有n-1次// 比较次数较多,时间复杂度为O(n*n),空间效率很高为O(1);是不稳定排序。}
排序之前:[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]开始进入优化版的直接选择排序:[9a, 30a, 49, 30b, 16, 21a, 9b, 21b][9a, 9b, 49, 30b, 16, 21a, 30a, 21b][9a, 9b, 16, 30b, 49, 21a, 30a, 21b][9a, 9b, 16, 21a, 49, 30b, 30a, 21b][9a, 9b, 16, 21a, 21b, 30b, 30a, 49][9a, 9b, 16, 21a, 21b, 30b, 30a, 49][9a, 9b, 16, 21a, 21b, 30b, 30a, 49]排序之后:[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]

package sort_lyf;/** * @author liyafang 堆排序: * 对于一棵顺序结构的完全二叉树而言,对于索引为k的节点,它的两个子节点的索引分别为2k+1,2k+2,反过来,对于 * 节点为k的,父节点为(k-1)/2.堆排序的步骤就是重复一下两个步骤:1.建堆   2.拿堆的根节点和最后一个节点交换。 * 对于包含n个元素的数组,堆排序需要n-1次建堆,建堆从最后一个非叶节点开始,因为只要保证每个非叶子节点的值大 * 于等于其左右子节点的值。每次建堆的作用就是选出最大值(注意大堆排出来的是升序),实际上也属于选择排序,只 * 不过堆排序可以通过树形结构保存部分比较结果,可减少比较次数。 */public class HeapSort implements ISort {public void sort(DataWrap[] data) {System.out.println("开始进入堆排序");int arrayLength = data.length;for (int i = 0; i < arrayLength; i++) {buildMaxHeap(data, arrayLength - 1 - i);// 每次建堆完成之后,堆顶元素为第i+1大元素DataWrap.swap(data, 0, arrayLength - 1 - i);// 交换堆顶和arrayLength - 1// - i元素// 交换完成重新再次对其余元素建堆System.out.println(java.util.Arrays.toString(data));}}// 对data数组从0到lastIndex建最大堆,选出0到lastIndex索引范围内的最大元素(堆顶元素)private void buildMaxHeap(DataWrap[] data, int lastIndex) {for (int i = (lastIndex - 1) / 2; i >= 0; i--) {int comparingIndex = i;// 当前正在判断的节点while ((2 * comparingIndex + 1) <= lastIndex) {// 如果当先节点的左子节点还存在int biggerIndex = 2 * comparingIndex + 1;// 左子节点的索引if (biggerIndex < lastIndex) {// 如果右子节点存在if (data[biggerIndex].compareTo(data[biggerIndex + 1]) < 0) {biggerIndex++;}}if (data[comparingIndex].compareTo(data[biggerIndex]) < 0) {DataWrap.swap(data, comparingIndex, biggerIndex);// 继续判断发生交换的子节点(biggerIndex)是否大于其左右子节点comparingIndex = biggerIndex;} else {break;}}}}// 小结:对于堆排序算法而言,需要进行n-1次建堆,每次建堆耗时logN,// 则时间效率为O(N*logN),空间效率很高为O(1),不稳定排序。}
排序之前:[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]开始进入堆排序[21b, 30a, 21a, 30b, 16, 9a, 9b, 49][9b, 30b, 21a, 21b, 16, 9a, 30a, 49][9a, 21b, 21a, 9b, 16, 30b, 30a, 49][9a, 16, 21a, 9b, 21b, 30b, 30a, 49][9b, 16, 9a, 21a, 21b, 30b, 30a, 49][9a, 9b, 16, 21a, 21b, 30b, 30a, 49][9b, 9a, 16, 21a, 21b, 30b, 30a, 49][9b, 9a, 16, 21a, 21b, 30b, 30a, 49]排序之后:[9b, 9a, 16, 21a, 21b, 30b, 30a, 49]
package sort_lyf;/** * @author liyafang 优化版的冒泡法:  * 依次比较0和1,1和2,2和3,n-2和n-1的值,如果前者大,就交换 * 经过第一趟,最大的元素排到了最后,然后进行第二趟... 一旦一 * 趟冒泡没有发生交换,即可提前结束排序。 */public class BubbleSortOptimized implements ISort {public void sort(DataWrap[] data) {System.out.println("开始进入改进版冒泡排序:");int arrayLength = data.length;int count = 0;boolean IsSwap = false;for (int i = 0; i < arrayLength; i++) {for (int j = 0; j < arrayLength - 1 - i; j++) {if (data[j].compareTo(data[j + 1]) > 0) {DataWrap.swap(data, j, j + 1);IsSwap = true;count++;}}if (!IsSwap) {// 如果某趟没有发生交换,则表明已处于有序状态break;}System.out.println(java.util.Arrays.toString(data));}System.out.println("总的交换次数:" + count);}/** * 冒泡排序而言的时间效率是不确定的:最好的情况下,初始数据序列已经处于有序的状态 * 执行一趟冒泡即可,做n-1次比较,无需进行任何交换;但在最坏情况下,初始数据序列 * 处于完全逆序,算法执行n-1躺冒泡,第i趟做了n-i次比较,执行n-i-1次对象交换, * 此时交换总次数为n(n-1)/2;算法空间效率很高,为O(1); 属于稳定排序; */}
排序之前:[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]开始进入改进版冒泡排序:[21a, 30a, 30b, 16, 9a, 9b, 21b, 49][21a, 30a, 16, 9a, 9b, 21b, 30b, 49][21a, 16, 9a, 9b, 21b, 30a, 30b, 49][16, 9a, 9b, 21a, 21b, 30a, 30b, 49][9a, 9b, 16, 21a, 21b, 30a, 30b, 49][9a, 9b, 16, 21a, 21b, 30a, 30b, 49][9a, 9b, 16, 21a, 21b, 30a, 30b, 49][9a, 9b, 16, 21a, 21b, 30a, 30b, 49]总的交换次数:18排序之后:[9a, 9b, 16, 21a, 21b, 30a, 30b, 49]

package sort_lyf;/** * @author liyafang */public class QuickSort implements ISort{public  void sort(DataWrap[] data) {System.out.println("开始进入快速排序:");subSort(data, 0, data.length - 1);System.out.println(java.util.Arrays.toString(data));}public  void subSort(DataWrap[] data, int start, int end) {if (start < end) {DataWrap base = data[start];// 定一个变量leftIndex,从左边第一个索引开始int leftIndex = start;// 定一个变量rightIndex,从右边第一个索引开始int rightIndex = end + 1;// 此处三个while循环用的非常秒,注意体会!while (true) {// 找出大于分界值的元素的索引,并用leftIndex记录它while (leftIndex < end&& (data[++leftIndex].compareTo(base) <= 0));// 找出小于分界值的元素的索引,并用rightIndex记录它while (rightIndex > start&& (data[--rightIndex].compareTo(base) >= 0));// 如果i<j,交换i、j两处索引的元素if (leftIndex < rightIndex) {DataWrap.swap(data, leftIndex, rightIndex);} else {break;// 重复执行以上步骤,直到leftIndex>=rightIndex,}}// 最后将分界值和rightIndex索引处的元素交换即可DataWrap.swap(data, start, rightIndex);// 递归左子序列subSort(data, start, rightIndex - 1);// 递归右子序列subSort(data, rightIndex + 1, end);}}/** * 快速排序的时间效率很好,因为它每趟能确定的元素呈指数增长。  * 快速排序需要使用递归,而递归使用栈,因此它的空间效率为O(logN)。 * 快速排序包含跳跃式交换,因此是不稳定的排序。 */}
排序之前:[21a, 30a, 49, 30b, 16, 9a, 9b, 21b]开始进入快速排序:[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]排序之后:[9a, 9b, 16, 21a, 21b, 30b, 30a, 49]

Main函数:

package sort_lyf;public class Test {public static void main(String args[]) {//a 代表该元素第一次出现  代表该元素第二次出现,主要是为了测试排序是否稳定DataWrap[] data = { new DataWrap(21, "a"), new DataWrap(30, "a"),new DataWrap(49, ""), new DataWrap(30, "b"),new DataWrap(16, ""), new DataWrap(9, "a"),new DataWrap(9, "b"), new DataWrap(21, "b"), };/* * DataWrap[] data = { new DataWrap(10, "a"), new DataWrap(9, "a"), new * DataWrap(8, ""), new DataWrap(7, "b"), new DataWrap(6, ""), new * DataWrap(5, "a"), new DataWrap(4, "b"), new DataWrap(3, "b"), new * DataWrap(2, "b"), new DataWrap(1, "b")}; *//* * DataWrap[] data = { new DataWrap(1, "a"), new DataWrap(2, "a"), new * DataWrap(3, ""), new DataWrap(4, "b"), new DataWrap(5, ""), new * DataWrap(6, "a"), new DataWrap(7, "b"), new DataWrap(8, "b"), new * DataWrap(9, "b"), new DataWrap(10, "b")}; */System.out.println("排序之前:\n" + java.util.Arrays.toString(data));/*ISort lyf = new DirectSelectSort();lyf.sort(data);ISort lyf = new DirectSelectSortOptimized();lyf.sort(data);ISort lyf = new HeapSort();lyf.sort(data);ISort lyf = new BubbleSortOptimized();lyf.sort(data);*/ISort lyf = new QuickSort();lyf.sort(data);System.out.println("排序之后:\n" + java.util.Arrays.toString(data));}}