排序算法一(直接选择,堆排序,冒泡排序和快速排序)
2011-10-26 21:12
447 查看
对于一个排序算法来说,一般从如下3个方面衡量算法的优劣:
1.时间复杂度:它主要是分析关键字的比较次数和记录的移动次数
2.空间复杂度:分析排序算法中需要多少辅助内存
3.稳定性:若两个记录A和B的关键字相等,排序前后二者的先后次序没有发生变化就称之为稳定排序
现有的排序算法分为:内部排序和外部排序
内部排序:所有的操作都是在内存中完成的,而外部排序需要借助外部存储器(如磁盘等)
外部排序最常用的方法是多路归并排序,即将原先的文件分解为多个能够一次性装入内存的部分,
分别把每一部分调入内存完成排序,接下来再对多个有序的子文件进行归并排序。
接下来主要介绍内部排序,分为:
选择排序(直接选择和堆排序),交换排序(冒泡和快速),插入排序(直接插入,折半插入和Shell排序),归并排序,基数排序和桶式排序。
本文介绍将介绍选择排序和交换排序
数组元素类DataWrap:flag用来标记相同元素出现的次数,a 表示第一次出现,b表示第二次出现,以此类推,用来测试算法的稳定性
算法的接口类:ISort(采用策略模式)
算法的实现类:
[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][/code]
Main函数:
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; } @Override public 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][/code]
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)); } }
相关文章推荐
- 数据结构与算法:七种排序算法总结(冒泡排序、选择排序、直接插入排序、希尔排序、堆排序、归并排序、快速排序)
- 排序算法: 冒泡排序, 快速排序,希尔排序,直接插入排序 ,直接选择排序,归并排序,堆排序
- java中各种常用排序实现(直接插入排序、直接选择排序、堆排序、冒泡排序、快速排序和归并排序)
- 排序算法java版,速度排行:冒泡排序、简单选择排序、直接插入排序、折半插入排序、希尔排序、堆排序、归并排序、快速排序
- java版排序算法简介及冒泡排序以及优化,选择排序,直接插入排序,希尔排序,堆排序,快速排序及其优化前言 2 分类 2 稳定性 3 时间复杂度 4 Java实现版本 5 1、冒泡排序 6 2、选择排序
- python排序算法-冒泡排序,选择排序,直接插入排序,希尔排序,归并排序,快速排序,堆排序
- 排序算法汇总(选择排序 ,直接插入排序,冒泡排序,希尔排序,快速排序,堆排序)
- 排序算法java版,速度排行:冒泡排序、简单选择排序、直接插入排序、折半插入排序、希尔排序、堆排序、归并排序、快速排序
- 程序员必知的8大排序(①直接插入排序②希尔排序③简单选择排序④堆排序⑤冒泡排序⑥快速排序⑦归并排序⑧基数排序)
- 排序算法(快速排序、直接插入排序、直接选择、冒泡排序)
- C#实现所有经典排序算法(选择排序 冒泡排序 快速排序)
- 易混的排序算法:冒泡排序、选择排序、快速排序
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 排序算法:冒泡排序、插入排序、选择排序、快速排序对比
- C语言8种排序算法及其实现 1.希尔排序 2.二分插入法 3.直接插入法 4.带哨兵的直接排序法 5.冒泡排序 6.选择排序 7.快速排序 8.堆排序
- 排序算法(插入排序、shell排序、冒泡排序、选择排序、合并排序、堆排序、快速排序、计数排序、基数排序、桶排序)
- 初级版、正宗版、升级版冒泡排序;简单选择排序;直接插入排序;希尔排序;堆排序;递归法 、非递归法归并排序;快速排序; 快速排序优化算法
- 排序算法之冒泡排序、选择排序、直接插入排序(java实现)
- 牛客网Java刷题知识点之插入排序(直接插入排序和希尔排序)、选择排序(直接选择排序和堆排序)、冒泡排序、快速排序、归并排序和基数排序(博主推荐)
- 几种常用的排序算法(快速排序,希尔排序,堆排序,选择排序,冒泡排序)