一. 堆排序

  堆排序是利用这种数据结构而设计的一种排序算法。以大堆为例利用堆顶记录的是最大关键字这一特性,每一轮取堆顶元素放入有序区,就类似选择排序每一轮选择一个最大值放入有序区,可以把堆排序看成是选择排序的改进。它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

  堆是一棵完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

对堆中的结点按层进行编号,将这种逻辑结构映射到数组中:

由于它是一颗完全二叉树,所以满足序号

leftchild = parent * 2 + 1;
rightchild = parent * 2 + 2;

这样的特性,利用这一特性,每次将parent与child进行比较然后向下调整元素的位置。

实现堆排序

  1. 将初始待排序关键字序列(R0,R1,R2....Rn)构建成大顶堆,此堆为初始的无序区;初始堆满足大顶堆性质,但是元素无序。
  2. 依次将将堆顶元素R[0]与最后一个元素R[n]交换,此时得到新的无序区(R0,R1,R2,......Rn-1)和新的有序区(Rn);
  3. 交换后进行向下调整无序区,使其满足大顶堆性质。
  4. 循环执行 2.3 步骤 直到遍历完数组。
 1 func HeapSort(arr []int)  {
2 arrLen := len(arr)
3 for i := (arrLen-2)/2; i >= 0; i-- {
4 arrJustDown(arr, i, arrLen)
5 }
6 end := arrLen - 1
7 for end != 0 {
8 arr[0], arr[end] = arr[end], arr[0]
9 arrJustDown(arr, 0, end)
10 end--
11 }
12 fmt.Println(arr)
13 }
14 func arrJustDown(arr []int, root, n int) {
15 parent := root
16 child := parent * 2 + 1
17 for child < n {
18 if child + 1 < n && arr[child + 1] > arr[child] {
19 child++
20 }
21 if arr[child] > arr[parent] {
22 arr[child], arr[parent] = arr[parent], arr[child]
23 parent = child
24 child = parent * 2 + 1
25 } else {
26 break
27 }
28 }
29 }

  建堆和每次向下调整的时间复杂度都是long2N ,所以整个数组处理完后,需要执行Nlong2N遍,调整过程中,最后一个元素和堆顶元素交换后需要向下调整,所以不保证相同大小元素的位置不变,它是不稳定排序。

二. 快速排序

排序思想

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

排序实现

步骤为:(1)从数列中挑出一个元素,称为 "基准"(pivot);

         (2)重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

    (3)递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

  当我们每次划分的时候选择的基准数接近于整组数据的最大值或者最小值时,快速排序就会发生最坏的情况,但是每次选择的基准数都接近于最大数或者最小数的概率随着排序元素的增多就会越来越小,我们完全可以忽略这种情况。但是在数组有序的情况下,它也会发生最坏的情况,为了避免这种情况,我们在选择基准数的时候可以采用三数取中法来选择基准数。 
三数取中法: 选择这组数据的第一个元素、中间的元素、最后一个元素,这三个元素里面值居中的元素作为基准数。

 1 func QuickSort(arr []int)  {
2 arrLen := len(arr)
3 quickSort(arr, 0, arrLen - 1)
4 fmt.Println(arr)
5 }
6 func quickSort(arr []int, left, right int) {
7 if left < right {
8 mid := partSort(arr, left, right)
9 quickSort(arr, left, mid - 1)
10 quickSort(arr, mid + 1, right)
11 }
12 }
13 func partSort(arr []int, left, right int) (ret int) {
14 key := arr[right]
15 for left < right {
16 for left < right && arr[left] <= key {
17 left++
18 }
19 arr[right] = arr[left]
20 for left < right && arr[right] >= key {
21 right--
22 }
23 arr[left] = arr[right]
24 }
25 arr[left] = key
26 ret = left
27 return
28 }

快速排序是一种快速的分而治之的算法,其平均运行时间为O(N*1ogN) 。它的速度主要归功于一个非长紧凑的并且高度优化的内部循环。但是他也是一种不稳定的排序,当基准数选择的不合理的时候他的效率又会编程O(N*N)。快速排序的最好情况: 快速排序的最好情况是每次都划分后左右子序列的大小都相等,其运行的时间就为O(N*1ogN)。快速排序的最坏情况: 快速排序的最坏的情况就是当分组重复生成一个空序列的时候,这时候其运行时间就变为O(N*N)快速排序的平均情况: 平均情况下是O(N*logN)。

三. 桶排序

介绍

  基本原理是将数组分到有限数量的桶里。每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后依次把各个桶中的记录列出来记得到有序序列。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是比较排序,他不受到O(n log n)下限的影响。

排序思想

  桶排序的思想近乎彻底的分治思想。假设待排序的一组数均匀独立的分布在一个范围中,并将这一范围划分成几个子范围(桶)。然后基于某种映射函数f ,将待排序列的关键字 k 映射到第i个桶中 (即桶数组B 的下标i) ,那么该关键字k 就作为 B[i]中的元素 (每个桶B[i]都是一组大小为N/M 的序列 )。接着将各个桶中的数据有序的合并起来 : 对每个桶B[i] 中的所有元素进行比较排序 (可以使用快排)。然后依次枚举输出 B[0]….B[M] 中的全部内容即是一个有序序列。

  为了使桶排序更加高效,我们需要做到这两点:

  1. 在额外空间充足的情况下,尽量增大桶的数量
  2. 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中

实现逻辑

  • 设置一个定量的数组当作空桶子。
  • 寻访序列,并且把项目一个一个放到对应的桶子去。
  • 对每个不是空的桶子进行排序。
  • 从不是空的桶子里把项目再放回原来的序列中。

动图演示排序过程:

设有数组 array = [63, 157, 189, 51, 101, 47, 141, 121, 157, 156, 194, 117, 98, 139, 67, 133, 181, 13, 28, 109]

对其进行桶排序:

复杂度

  • 平均时间复杂度:O(n + k)
  • 最佳时间复杂度:O(n + k)
  • 最差时间复杂度:O(n ^ 2)
  • 空间复杂度:O(n * k)
  • 稳定性:稳定

  桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。

go代码实现

 1 func bin_sort(li []int, bin_num int) {
2 min_num, max_num := li[0], li[0]
3 for i := 0; i < len(li); i++ {
4 if min_num > li[i] {
5 min_num = li[i]
6 }
7 if max_num < li[i] {
8 max_num = li[i]
9 }
10 }
11 bin := make([][]int, bin_num)
12 for j := 0; j < len(li); j++ {
13 n := (li[j] - min_num) / ((max_num - min_num + 1) / bin_num)
14 bin[n] = append(bin[n], li[j])
15 k := len(bin[n]) - 2
16 for k >= 0 && li[j] < bin[n][k] {
17 bin[n][k+1] = bin[n][k]
18 k--
19 }
20 bin[n][k+1] = li[j]
21 }
22 o := 0
23 for p, q := range bin {
24 for t := 0; t < len(q); t++ {
25 li[o] = bin[p][t]
26 o++
27 }
28 }
29 }

  桶排序是计数排序的变种,它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。把计数排序中相邻的m个”小桶”放到一个”大桶”中,在分完桶后,对每个桶进行排序(一般用快排),然后合并成最后的结果。

  算法思想和散列中的开散列法差不多,当冲突时放入同一个桶中;可应用于数据量分布比较均匀,或比较侧重于区间数量时。

  桶排序最关键的建桶,如果桶设计得不好的话桶排序是几乎没有作用的。通常情况下,上下界有两种取法,第一种是取一个10n或者是2n的数,方便实现。另一种是取数列的最大值和最小值然后均分作桶。

go实现堆排序、快速排序、桶排序算法的更多相关文章

  1. python实现桶排序算法

    桶排序算法也是一种可以以线性期望时间运行的算法,该算法的原理是将数组分到有限数量的桶里,每个桶再分别排序. 它的算法流程如下所示: 设置一个定量的数组当作空桶子. 寻访序列,并且把项目一个一个放到对应 ...

  2. 洛谷 P1177 【模板】快速排序(排序算法整理)

    P1177 [模板]快速排序 题目描述 利用快速排序算法将读入的N个数从小到大排序后输出. 快速排序是信息学竞赛的必备算法之一.对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成.( ...

  3. 简单桶排序算法-python实现

    #-*- coding: UTF-8 -*- import numpy as np def BucketSort(a, n): barrel = np.zeros((1, n), dtype = 'i ...

  4. Python实现八大排序算法(转载)+ 桶排序(原创)

    插入排序 核心思想 代码实现 希尔排序 核心思想 代码实现 冒泡排序 核心思想 代码实现 快速排序 核心思想 代码实现 直接选择排序 核心思想 代码实现 堆排序 核心思想 代码实现 归并排序 核心思想 ...

  5. 排序算法<No.3>【桶排序】

    算法,是永恒的技能,今天继续算法篇,将研究桶排序. 算法思想: 桶排序,其思想非常简单易懂,就是是将一个数据表分割成许多小数据集,每个数据集对应于一个新的集合(也就是所谓的桶bucket),然后每个b ...

  6. 八大排序算法的python实现(五)堆排序

    代码 #coding:utf-8 #author:徐卜灵 # 堆排序适用于记录数很多的情况 #与快速排序,归并排序 时间复杂一样都是n*log(n) ######################### ...

  7. 计数排序和桶排序(Java实现)

    目录 比较和非比较的区别 计数排序 计数排序适用数据范围 过程分析 桶排序 网络流传桶排序算法勘误 桶排序适用数据范围 过程分析 比较和非比较的区别 常见的快速排序.归并排序.堆排序.冒泡排序等属于比 ...

  8. 常用排序算法的python实现和性能分析

    常用排序算法的python实现和性能分析 一年一度的换工作高峰又到了,HR大概每天都塞几份简历过来,基本上一天安排两个面试的话,当天就只能加班干活了.趁着面试别人的机会,自己也把一些基础算法和一些面试 ...

  9. 十大经典排序算法最强总结(含JAVA代码实现)

    最近几天在研究排序算法,看了很多博客,发现网上有的文章中对排序算法解释的并不是很透彻,而且有很多代码都是错误的,例如有的文章中在“桶排序”算法中对每个桶进行排序直接使用了Collection.sort ...

随机推荐

  1. SVG和Canvas的区别?

    什么是SVG? SVG(可缩放矢量图形)编辑可缩放矢量图形是基于可扩展标记语言(标准通用标记语言的子集),用于描述二维矢量图形的一种图形格式.它由万维网联盟制定,是一个开放标准. 什么是 Canvas ...

  2. Python从零开始编写控制程序(一)

    Python之从零开始编写控制程序(一) 在此声明:本博客仅供学习参考,任何产生相关违法犯罪行为与本人无关. 另外如果有师傅有好的思路和想法,可以和我一起沟通交流. 最近在一直尝试做Powershel ...

  3. 阿里饿死了么Android面试凉经,两轮面完被虐哭了,怒清购物车。。。卸载饿死了么

    大家应该看过很多分享面试成功的经验,但根据幸存者偏差的理论,也许多看看别人面试失败在哪里,对自己才更有帮助. 最近跟一个朋友聊天,他漫不经心地复习了几个月,就去参加了饿了么面试,第二面结束后,嗯,挂了 ...

  4. 【LeetCode】169. 多数元素

    169. 多数元素 知识点:数组:排序:消消乐:分治: 题目描述 给定一个大小为 n 的数组,找到其中的多数元素.多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是非空的, ...

  5. mac 软件意外退出

    大概率的原因是软件签名问题. 先安装 xcode xcode-select --install 然后签名 sudo codesign --force --deep --sign - 文件位置(直接将应 ...

  6. 【LeetCode】80. 删除有序数组中的重复项 II

    80. 删除有序数组中的重复项 II 知识点:数组:排序:双指针: 题目描述 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度. 不要使 ...

  7. spring cloud alibaba版本选择

    https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明 Spring Cloud Version Spring Cloud Version ...

  8. 【HMC Core 6.0全球上线】图形计算服务新插件,助力高画质3D手游创新

    HMS Core 6.0已于7月15日全球上线,本次新版本向广大开发者开放了众多全新能力与技术.其中华为图形计算服务(CG Kit)开放了体积雾插件和流体插件,为3D手游画面的提升提供了坚实的技术基础 ...

  9. Java-线程池专题 (美团)

    实现多线程的三种方式,继承Thread,实现Runnable 和 实现 Executor接口 ,具体参考:Java 多线程 三种实现方式 去美团,问到了什么是线程池,如何使用,为什么要用,以下做个总结 ...

  10. 小白5分钟创建WPF

    创建WPF应用程序 基于生产这里选择.Net Framework进行开发 添加控件 由于不熟悉 高效点 我们这里直接拖拽控件 如果你有一点前端基础 你可以在控件对应Code 根据属性 对控件进行设置 ...