直接选择排序
选择排序的原理:将数组分为有序区,无序区。初始有序区元素为0,整个数组为无序区。每次遍历从无序区中选出一个最小(或最大)的元素,放在有序区的最后,每一次遍历排序过程都是有序区元素个数增加,无序区元素个数减少的过程,直到无序区元素个数位0。
直接选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的数组进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
/** * 直接选择排序 * * @param ts */ public static <T extends Comparable<? super T>> void selectSort(T[] ts) { int length = ts.length; int min; // 无序区最小元素位置 T tem; // 辅助空间 for (int i = 0; i < length - 1; i++) { min = i;// 默认取无序区第一个元素为最小值 // 循环遍历无序区,查找到无序区最小元素 for (int y = i + 1; y < length; y++) if (ts[min].compareTo(ts[y]) > 0) min = y; if (i == min) continue; // 把无序区最小元素,插入到有序区末尾 tem = ts[i]; ts[i] = ts[min]; ts[min] = tem; } }
1. 时间复杂度:O(n^2)
直接选择排序耗时的操作有:比较 + 交换赋值。时间复杂度如下:
1) 最好情况:序列是升序排列,在这种情况下,需要进行的比较操作需n(n-1)/2次。交换赋
值操作为0次。即O(n^2)
2) 最坏情况:序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。交换赋值n-1
次(交换次数比冒泡排序少多了),选择排序的效率比较稳定,最好情况和最坏情况差
不多。即O(n^2)
3) 渐进时间复杂度(平均时间复杂度):O(n^2)
2. 空间复杂度:O(1)
3. 稳定性
直接选择排序是不稳定的。
因为每次遍历比较完后会使用本次遍历选择的最小元素和无序区的第一个元素交换位置,所以如果无序区第一个元素后面有相同元素的,则可能会改变相同元素的相对顺序。
堆排序
堆是一颗被完全填满的二叉树,有可能的例外是在底层,底层上的元素从左到右填入。这样的树称为完全二叉树。
堆的性质:
比如我们想找出最大元,因为最大元在根上,而且任意子树也是一个堆,那么任意节点就应该大于它的所有后裔。
一个重要的观察发现,因为完全二叉树这么有规律,所以可以用一个数组表表示而不需要使用链, 对于数组中任意位置的i上元素,其左儿子在位置[2i+1]上,右儿子在左儿子后的[(2i+1)+1]位置,它的父亲则在[i-1/2] 上,注意[0]位置是根。如下图
当新增一个元素24时,我们在下一个可用位置插入元素24。因为24>10,所以破坏了堆的性质,解决:把24朝着根的方向上移,直到24放入正确的位置。 如下图,这里称之为上虑。(堆排序,不会用到)
当删除最大元时。由于现在堆少了一个元素,因此堆中最后一个元素必须移动到堆中某个地方。我们的做法是将最后一个元素放在删除的最大元位置。然后向下插入到最大儿子路径的一个正确的位置。这里称之为下虑。堆排中会递归删除最大元。
根据上面堆的性质,每次删除最大元后,堆缩小1,因此,堆中最后的单元可以用来存放刚刚删去的元素。
下图中先进行,先把数组初始化成最大堆,然后递归删除最大元(最左面那根),每次删除的最大元放在数组的length-1,length-2,length-3......
在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义以下几种操作:
- 最大堆调整:将堆的末端子节点作调整,使得子节点永远小于父节点
- 创建最大堆:将堆所有数据重新排序
- 堆排序:移除位在第一个数据的根节点,并做最大堆调整的递归运算
/** * 堆排序 * * @param ts */ public static <T extends Comparable<? super T>> void heapSort(T[] ts) { // 通过下虑,将数组初始化成一个堆。 for (int length = ts.length, i = length / 2 - 1; i >= 0; i--) percDown(ts, i, length); // 对具有堆性质的数组排序 for (int len = ts.length - 1; len >= 0; len--) { // 将最大元[0]删除,即放到堆尾,堆尾元素放到最大元位置 swap(ts, len); // 对最大元位置元素 下虑 percDown(ts, 0, len); } } /** * 下虑 找出最大元 * * @param ts * @param index * @param length */ private static <T extends Comparable<? super T>> void percDown(T[] ts, int i, int length) { T temp = ts[i];// 待调整最大元位置元素 for (int child = leftChild(i); child < length; i = child, child = leftChild(i)) { // 判断有右儿子&&右儿子>左儿子 if (child + 1 != length && ts[child + 1].compareTo(ts[child]) > 0) child++; // 最大儿子跟父比较 if (temp.compareTo(ts[child]) < 0) ts[i] = ts[child]; else break; } ts[i] = temp;// 放到正确位置 } /** * 堆尾、堆首互换 * * @param ts * @param index */ private static <T extends Comparable<? super T>> void swap(T[] ts, int index) { T temp = ts[index]; ts[index] = ts[0]; ts[0] = temp; } /** * 左儿子位置 * * @param i * @return */ private static int leftChild(int i) { return i * 2 + 1; }
1. 时间复杂度:O(nlog2n)
首先把数组,初始化成一个堆。这个阶段花费O(N)的时间,可以忽略。
然后执行N次的删除最大元,每次最大元删除重排花费的时间为O(logn),因此
最差时间复杂:O(nlogn)
最优时间复杂:O(nlogn)
平均时间复杂:O(nlogn)
2. 空间复杂度:O(1)
3. 稳定性
堆排序是不稳定的
因为在初始化堆时,相同元素可能被分配到不同的父节点下,所以在反复调整堆过程中,可能会改变相同元素的相对顺序
简单性能测试
/** * 简单的性能测试 * * @param args */ public static void main(String[] args) { int num = 100000; Integer[] inters = new Integer[num]; Random inter = new Random(); for (int i = 0; i < num; i++) { inters[i] = inter.nextInt(num); } long tims = System.currentTimeMillis(); // heapSort(inters); selectSort(inters); tims -= System.currentTimeMillis(); System.err.println(tims * -1); }
排序方法\数组大小 | 1000 | 5000 | 10000 | 50000 | 10w | 100w | 1000w |
选择排序 | 29 | 45 | 104 | 2.3s | 10s | 太长 | 太长 |
堆排序 | 3 | 15 | 20 | 28 | 40 | 600 | 10s |
相关推荐
堆排序7.java 使用java实现的堆排序堆排序7.java 使用java实现的堆排序堆排序7.java 使用java实现的堆排序堆排序7.java 使用java实现的堆排序堆排序7.java 使用java实现的堆排序堆排序7.java 使用java实现的堆排序堆...
Java实现堆排序不是C,Java实现堆排序不是C,Java实现堆排序不是C,Java实现堆排序不是C
堆排序12.java 使用java代码实现堆排序12.java 使用java代码实现堆排序12.java 使用java代码实现堆排序12.java 使用java代码实现堆排序12.java 使用java代码实现堆排序12.java 使用java代码实现堆排序12.java 使用...
堆排序10.java 使用java来实现堆排序10.java 使用java来实现堆排序10.java 使用java来实现堆排序10.java 使用java来实现堆排序10.java 使用java来实现堆排序10.java 使用java来实现堆排序10.java 使用java来实现堆...
堆排序:应用Java和Python分别实现堆排序算法; 堆排序:应用Java和Python分别实现堆排序算法; 堆排序:应用Java和Python分别实现堆排序算法; 堆排序:应用Java和Python分别实现堆排序算法; 堆排序:应用Java和...
本文将深入探讨Java编程语言中实现的七种主要排序算法:直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序以及归并排序。每种算法都有其独特性,适用于不同的场景和数据特性。 1. **直接插入排序**:...
堆排序 java实现
本文件“Java实现堆排序.rar”可能包含了用Java语言编写的堆排序算法的源代码示例。下面我们将深入探讨堆排序的基本原理、步骤以及如何在Java中实现。 堆排序的核心是构建一个大顶堆或小顶堆。在大顶堆中,每个节点...
在实际应用中,Java的`PriorityQueue`可以提供更简洁的实现方式,但手动实现有助于理解堆排序的工作原理。 总结起来,堆排序是一种高效的排序算法,时间复杂度为O(n log n),空间复杂度为O(1)。在Java中,我们可以...
用Java实现的堆排序算法,二叉树转换为堆,然后排序
本篇将详细介绍标题所提及的四种排序算法:Shell排序、快速排序、归并排序以及堆排序。 1. **Shell排序**: Shell排序是一种改进的插入排序,由Donald Shell于1959年提出。它通过设置一个间隔序列,使得数组中的...
在编程领域,选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一...在实际开发中,由于其效率问题,通常会被更高效的排序算法,如快速排序、归并排序或堆排序所替代。
在实际应用中,二叉排序树常用于快速查找和排序,而堆则常用于实现优先级队列、求解最大/最小元素问题,如堆排序算法。结合二叉排序树和堆的概念,可以设计出更高效的数据结构和算法,例如二叉堆(一种同时满足二叉...
选择排序是一种简单直观的排序算法,它的工作原理是每一次从待排序的数据元素中选出...在实际编程中,我们通常会使用更高效的排序算法,如快速排序、归并排序或堆排序,但理解基础排序算法对提升编程技能仍然非常重要。
java的堆排序算法实现程序,含测试,可直接运行。java的堆排序算法实现程序,含测试,可直接运行。
Java代码-排序-直接插入排序、希尔排序、直接选择排序、冒泡排序、堆排序、快速排序、归并排序中部分排序以及算法-贪心法,大家可以将源码下载下来进行学习,附带着注释和解释,有不理解的可以找博主一起探讨,共同...
Java中可以使用`PriorityQueue`类实现堆排序。 4. **快速排序(Quick Sort)**: 快速排序是效率较高的排序算法,基于“分而治之”的策略。选择一个基准值,将数组分为两部分,一部分所有元素都小于基准,另一部分...