go实现堆排序、快速排序、桶排序算法
一. 堆排序
堆排序是利用堆这种数据结构而设计的一种排序算法。以大堆为例利用堆顶记录的是最大关键字这一特性,每一轮取堆顶元素放入有序区,就类似选择排序每一轮选择一个最大值放入有序区,可以把堆排序看成是选择排序的改进。它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。
堆
堆是一棵完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
对堆中的结点按层进行编号,将这种逻辑结构映射到数组中:
由于它是一颗完全二叉树,所以满足序号
leftchild = parent * 2 + 1;
rightchild = parent * 2 + 2;
这样的特性,利用这一特性,每次将parent与child进行比较然后向下调整元素的位置。
实现堆排序
- 将初始待排序关键字序列(R0,R1,R2....Rn)构建成大顶堆,此堆为初始的无序区;初始堆满足大顶堆性质,但是元素无序。
- 依次将将堆顶元素R[0]与最后一个元素R[n]交换,此时得到新的无序区(R0,R1,R2,......Rn-1)和新的有序区(Rn);
- 交换后进行向下调整无序区,使其满足大顶堆性质。
- 循环执行 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] 中的全部内容即是一个有序序列。
为了使桶排序更加高效,我们需要做到这两点:
- 在额外空间充足的情况下,尽量增大桶的数量
- 使用的映射函数能够将输入的 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实现堆排序、快速排序、桶排序算法的更多相关文章
- python实现桶排序算法
桶排序算法也是一种可以以线性期望时间运行的算法,该算法的原理是将数组分到有限数量的桶里,每个桶再分别排序. 它的算法流程如下所示: 设置一个定量的数组当作空桶子. 寻访序列,并且把项目一个一个放到对应 ...
- 洛谷 P1177 【模板】快速排序(排序算法整理)
P1177 [模板]快速排序 题目描述 利用快速排序算法将读入的N个数从小到大排序后输出. 快速排序是信息学竞赛的必备算法之一.对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成.( ...
- 简单桶排序算法-python实现
#-*- coding: UTF-8 -*- import numpy as np def BucketSort(a, n): barrel = np.zeros((1, n), dtype = 'i ...
- Python实现八大排序算法(转载)+ 桶排序(原创)
插入排序 核心思想 代码实现 希尔排序 核心思想 代码实现 冒泡排序 核心思想 代码实现 快速排序 核心思想 代码实现 直接选择排序 核心思想 代码实现 堆排序 核心思想 代码实现 归并排序 核心思想 ...
- 排序算法<No.3>【桶排序】
算法,是永恒的技能,今天继续算法篇,将研究桶排序. 算法思想: 桶排序,其思想非常简单易懂,就是是将一个数据表分割成许多小数据集,每个数据集对应于一个新的集合(也就是所谓的桶bucket),然后每个b ...
- 八大排序算法的python实现(五)堆排序
代码 #coding:utf-8 #author:徐卜灵 # 堆排序适用于记录数很多的情况 #与快速排序,归并排序 时间复杂一样都是n*log(n) ######################### ...
- 计数排序和桶排序(Java实现)
目录 比较和非比较的区别 计数排序 计数排序适用数据范围 过程分析 桶排序 网络流传桶排序算法勘误 桶排序适用数据范围 过程分析 比较和非比较的区别 常见的快速排序.归并排序.堆排序.冒泡排序等属于比 ...
- 常用排序算法的python实现和性能分析
常用排序算法的python实现和性能分析 一年一度的换工作高峰又到了,HR大概每天都塞几份简历过来,基本上一天安排两个面试的话,当天就只能加班干活了.趁着面试别人的机会,自己也把一些基础算法和一些面试 ...
- 十大经典排序算法最强总结(含JAVA代码实现)
最近几天在研究排序算法,看了很多博客,发现网上有的文章中对排序算法解释的并不是很透彻,而且有很多代码都是错误的,例如有的文章中在“桶排序”算法中对每个桶进行排序直接使用了Collection.sort ...
随机推荐
- csaw2013reversing2 writeup
csaw2013reversing2 writeup 1.程序分析 题目是一个exe文件,提示运行即可拿到flag,但是窗口弹出之后会出现一堆乱码.这时候,我们把文件丢入IDA之中,看到程序大致流程如 ...
- TCP实现聊天
TCP实现聊天 IO流关闭是简写的,正常写要判断是否为null 客户端:(最好捕获异常) 1.连接服务器Socket 2.发送消息 package net.TCPChat; import java.i ...
- 华为应用市场更新APP多次被拒
最近公司的APP发布了新版本,只进行了线上bug的修复,基本没改什么主体业务功能.各大应用市场都顺利更新上架,但是国货之光华为,被闷了几次.拒来拒去,就是那些反复的内容.内容一般如下: 经检测发现,您 ...
- 基于SpringBoot的在线问卷调查管理系统
注意:该项目只展示部分功能,如需了解,评论区咨询即可. 1.开发环境 开发语言:Java 后台框架:SpringBoot 前端框架:vue 数据库:MySQL 设计模式:MVC 架构:B/S 源码类型 ...
- Golang语言系列-14-单元测试
单元测试 字符串切割函数 package split_string import ( "fmt" "strings" ) // Split:切割字符串 // e ...
- 【笔记】二分类算法解决多分类问题之OvO与OvR
OvO与OvR 前文书道,逻辑回归只能解决二分类问题,不过,可以对其进行改进,使其同样可以用于多分类问题,其改造方式可以对多种算法(几乎全部二分类算法)进行改造,其有两种,简写为OvO与OvR OvR ...
- 常见web中间件漏洞(二)Apache漏洞
Apache(总联想到武直那个)是最常见,使用人数最多的一款web服务器软件.跨平台,多扩展,开源,用过的人都说好 Apache的漏洞主要集中在解析漏洞这一块 1.未知扩展名解析漏洞 Apache的一 ...
- 题解 Merchant
传送门 可以发现如果我们最终选择的物品集合已经确定,就很好求了 \(\sum k*t+\sum b \geqslant s\) ,二分即可 但现在我们无法确定该选哪些物品 因此我们只需要check一下 ...
- 题解 y
传送门 考场上写的记忆化不够快--和暴力一个分 如果题面里有提到类似「从点1出发」的字样,特别注意点1根本就没有连边的情况 这题写记忆化的时候是想搜出所有可能的组合, 那么对于一个点u,剩余深度为d时 ...
- 题解 u
传送门 这里AC解法因为手残 tag2[min(r+l, n+1)][min(c+l+1, n+1)]+=s; 写成 tag2[min(r+l, n+1)][c+l+1]+=s; 惨遭RE,以后注意查 ...