选择排序:直接选择排序&堆排序
上一篇中, 介绍了交换排序中的冒泡排序和快速排序, 那么这一篇就来介绍一下 选择排序和堆排序, 以及他们与快速排序的比较.
一、直接选择排序
1. 思想
在描述直接选择排序思想之前, 先来一个假设吧.(先不管这个假设是什么思想的排序啊)
假设我有两个集合, 一个是待排序集合, 一个是空集合. 现在通过这个空的集合来完成待排序集合的排序.
第一步: 我可以遍历listA集合, 找到其中最大的数num, 然后把这个num插入到listB中.
listB.Insert(num);
listA.Remove(num);
第二步: 重复第一步的操作, 一直到消耗完 listA 集合中的数.
这样的话, 数据就逐渐从listA有序的流向了listB. 最后来看listB, 就是一个有序的集合.
代码如下:
static List<int> InsertSort(List<int> listA)
{
if (listA == null)
return null;
List<int> listB = new List<int>();
int maxNum = ;
int ptr = ;
int i = ;
while (listA.Count > )
{
for (i = , ptr = , maxNum = listA[]; i < listA.Count; i++)
{
if (listA[i] < maxNum)
{
maxNum = listA[i];
ptr = i;
}
}
listB.Add(maxNum);
listA.RemoveAt(ptr);
}
listA = listB;
return listA;
}
直接选择排序的思想, 与这里的思想是一样的, 只不过, 不需要使用中间集合来排序, 直接就在自己集合里面做交换就可以了.
接下来, 就来简化上面的过程, 可以得到直接选择排序版本:
static List<int> SelectionSort(List<int> list)
{
int count = list.Count;
//要遍历的次数
for (int i = ; i < count - ; i++)
{
//假设tempIndex的下标的值最小
int tempIndex = i; for (int j = i + ; j < count; j++)
{
//如果tempIndex下标的值大于j下标的值,则记录较小值下标j
if (list[tempIndex] > list[j])
tempIndex = j;
} //最后将假想最小值跟真的最小值进行交换
var tempData = list[tempIndex];
list[tempIndex] = list[i];
list[i] = tempData;
}
return list;
}
2. 复杂度
选择排序的时间复杂度是O(n2), 乍一看, 怎么和冒泡排序的复杂度是一样的?
从上面的代码来看, 选择排序的循环次数是这样的:
(n-1) + (n-2) + (n-3) + ... + 2 + 1
结果为 : n2/2
所以, 选择排序的时间复杂度也是O(n2)
不过, 由于选择排序需要进行的交换操作很少, 最多的时候, 只会发生n-1次交换,
冒泡排序则是两两交换, 最多的时候, 会发生 n2/2次交换. 所以, 选择排序的性能还是要由于冒泡排序的.
这也是上一篇的, 我会问, 为什么每次都要交换, 而不能找到最大/最小的值, 才进行交换操作.
二、堆排序
1. 思想
1). 前奏
相信很多人都喜欢看篮球比赛, 尤其是nba季候赛, 那么先来看一下, 季后赛是怎么晋级的.
nba季后赛就是这种逐级递进的方式, 来决定最后的胜者. 在排序中, 我能否借鉴这种形式来排序呢?
1. 看左下角的三个方块, 有两个是4级的, 一个是3级的, 假设每个方块里面都放有一个数, 大小各不同, 那么我是否可以比较这三个数, 然后把其中最大的数, 放到第3级中呢?
2. 如果每三个方块都可以这样比较, 那么是否会决出一个最大的值, 放入第1级的这个唯一的一个方块中呢?
这里面还有一个问题, 如果只有两个方块怎么办呢? 那就只能一个在上, 一个在下, 而不能是两个在下, 因为需要有一个晋级.
2) 二叉堆
以上的这张图, 看起来是不是有点熟悉, 感觉有点像二叉树结构. 在这里, 是二叉堆.
既然要把一组数据映射成为一个二叉堆, 那么就要明白, 这组数据是怎么存储和映射的.
从上到下, 从左到右的一个顺序来映射. 从图上看, 每一级对应的下标范围为 : 2n-1 ~ 2(2n-1)
接下来就对他进行一轮堆排序: (只需要对父节点进行判断就可以了)
这里的比较顺序: 父节点的比较顺序是从下往上, 从有右往左. 且与父节点交换的这个节点还需要与后代节点比较. 以确定他的位置
Demo:
一个父节点的比较, 代码如下:
static void HeapAdjust(List<int> list, int parent, int length)
{
//temp保存当前父节点
int temp = list[parent]; //得到左孩子的下标(这可是二叉树的定义,大家看图也可知道)
int child = * parent + ; while (child < length)
{
//如果parent有右孩子,则要判断左孩子是否小于右孩子
if (child + < length && list[child] < list[child + ])
child++; //此时的child指向右孩子 //父亲节点大于子节点,就不用做交换
if (temp >= list[child])
//这里能这么写, 就是因为temp节点下的所有节点, 都已经拍过序了, 保证了父节点是子节点中最大的
break; //将较大子节点的值赋给父亲节点
//这里相当于向前覆盖, 因为temp已经取出来了, 所以相当于有一个坑可以填数
list[parent] = list[child]; //下面就是为下一个循环做准备
//也就是说, 让parent指向temp的其中一个大的子节点
//然后将该子节点做为下一个父节点
parent = child; //找到该新父节点的左孩子节点
child = * parent + ;
}
//最后将temp值赋给较大的子节点,以形成两值交换
list[parent] = temp;
}
接下来就是堆排序的代码了
///<summary>
/// 堆排序
///</summary>
///<param name="list"></param>
public static void HeapSort(List<int> list)
{
//list.Count/2-1 就是最后一个父节点的下标, 所有的父节点都是往前放的
for (int i = list.Count / - ; i >= ; i--)
{
HeapAdjust(list, i, list.Count);
} //最后输出堆元素
for (int i = list.Count - ; i > ; i--)
{
//堆顶与当前堆的最后一个元素进行值对调
int temp = list[];
list[] = list[i];
list[i] = temp; //重新塑造堆, 因为堆的最后一个元素会被剔除出去
//剔除出去的数据, 就是有序的数据
HeapAdjust(list, , i);
}
}
这里的思想, 就是不断地构建堆, 得到最大的值, list[0], 然后将最大的值和堆的最后一个值交换, 剔除这个最大值, 重新塑造堆, 此时, 堆已经少了一个值了.
也就是说, 刚开始有10个数进行堆排序, 一轮之后, 最大的值剔除出去, 再进行堆排序的时候, 就只有9个值了.
2. 复杂度
堆排序的时间复杂度为O(nlog2n), 从复杂度上来看, 好像和快速排序是一样的, 那么事实上是不是这样呢.
其证明过程见下面参考链接
3. 堆排序 vs 快速排序
从上面来看, 在2000数据量下, 堆排序还是要慢与快速排序的, 难怪微软会使用快速排序呢.
那么堆排序是否一无是处呢?
快速排序并不能在排序没有完成的情况下, 取出其中最大/最小的一些数, 但是选择排序和堆排序擅长这个, 可以再排序还没有完成的情况下, 取出其中最大/最小的一些数据.
参考:
选择排序:直接选择排序&堆排序的更多相关文章
- 插入排序、冒泡排序、选择排序、希尔排序、高速排序、归并排序、堆排序和LST基数排序——C++实现
首先是算法实现文件Sort.h.代码例如以下: <pre name="code" class="java">/* * 实现了八个经常使用的排序算法: ...
- 9, java数据结构和算法: 直接插入排序, 希尔排序, 简单选择排序, 堆排序, 冒泡排序,快速排序, 归并排序, 基数排序的分析和代码实现
内部排序: 就是使用内存空间来排序 外部排序: 就是数据量很大,需要借助外部存储(文件)来排序. 直接上代码: package com.lvcai; public class Sort { publi ...
- 算法与数据结构(十三) 冒泡排序、插入排序、希尔排序、选择排序(Swift3.0版)
本篇博客中的代码实现依然采用Swift3.0来实现.在前几篇博客连续的介绍了关于查找的相关内容, 大约包括线性数据结构的顺序查找.折半查找.插值查找.Fibonacci查找,还包括数结构的二叉排序树以 ...
- 数据结构和算法(Golang实现)(20)排序算法-选择排序
选择排序 选择排序,一般我们指的是简单选择排序,也可以叫直接选择排序,它不像冒泡排序一样相邻地交换元素,而是通过选择最小的元素,每轮迭代只需交换一次.虽然交换次数比冒泡少很多,但效率和冒泡排序一样的糟 ...
- 冒泡排序 & 选择排序 & 插入排序 & 希尔排序 JavaScript 实现
之前用 JavaScript 写过 快速排序 和 归并排序,本文聊聊四个基础排序算法.(本文默认排序结果都是从小到大) 冒泡排序 冒泡排序每次循环结束会将最大的元素 "冒泡" 到最 ...
- [jQuery编程挑战]004 针对选择框词典式排序
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="utf-8&quo ...
- 排序算法——选择排序(js语言实现)
选择排序:顾名思义选择,选择排序属于O(N^2)的排序算法,意思就是选择数组中的最小的拿出来放在第一位,然后再剩下的数里面,选择最小的,排在第二位,以此类推. 例如:8 3 4 5 6 2 ...
- 选择排序—简单选择排序(Simple Selection Sort)
基本思想: 在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换:然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素 ...
- JS快速排序 希尔排序 归并排序 选择排序
/* 快速排序 1.1 算法描述 快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用.快速排序是一种既不浪费空间又可以快一 ...
随机推荐
- (记忆化搜索 )The Triangle--hdu --1163
http://poj.org/problem?id=1163 Description 73 88 1 02 7 4 44 5 2 6 5 (Figure 1) Figure 1 shows a ...
- CentOS FTP服务器系统套件全面讲解
对大家推荐很好使用的CentOS FTP系统,像让大家对CentOS FTP系统有所了解,然后对CentOS FTP系统全面讲解介绍,希望对大家有用. 1.vsFTPd,目前常用CentOS FTP服 ...
- spring的bean在什么时候被实例化
Spring什么时候实例化bean,首先要分2种情况 第一:如果你使用BeanFactory作为Spring Bean的工厂类,则所有的bean都是在第一次使用该Bean的时候实例化 第二:如 ...
- STL容器(C11)--unordered_map用法
http://www.cplusplus.com/reference/unordered_map/unordered_map/
- hdu 4975 最大流快版
http://acm.hdu.edu.cn/showproblem.php?pid=4975 给出每行每列的和,问是否存在这样的表格:每个小格放的数字只能是0--9. 直接用第八场最大流模板. #in ...
- JavaScript中的工厂函数
所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,实际上是创建了一个类实例. 在学习jQuery的时候,我们经常会看到“工厂函数”这个概念,那么究竟什么是“工厂函数”呢?我们来看看概念,“所 ...
- SSD 相关基础知识
SDD 基础知识 SSD(Solid State Drives)是固态硬盘,使用闪存颗粒来存储数据,闪存又可分为NAND Flash和NOR Flash,通常所说的SSD硬盘都使用NAND Flash ...
- C#项目 学生选课系统 C#窗口 Winform项目 项目源码及使用说明
这是一个学生选课信息管理系统,使用VS2010+SQL2008编写,VS2017正常使用. 项目源码下载地址 https://gitee.com/whuanle/xkgl 笔者录了两个视频,打开项目源 ...
- WPF用户控件库 嵌入外部(VLC)exe
综合网上资源完成的自己的第一篇博客 ------------------------------------------------------------------------ 网上类似的贴子挺多 ...
- 介绍 Scratch 3.0:扩展编码创造力
在过去十年中,全世界数百万儿童使用Scratch编写自己的互动游戏,故事,动画等. 这种磅礴的创造力激励我们继续扩展和改进Scratch,让世界各地的孩子都有新的机会用新技术创造性地表达自己. 今天, ...