排序算法

上一篇博客中写到了关于动态规划中一些常见的问题和解决方案,根据问题总结出来解决动态规划类问题的方法是通过寻找状态,列出状态转移方程,通过遍历即可将问题解决。排序也是一类常见的问题,通过排序的思想,我们也可以来解决很多问题,还有贪心,分治等思想,也会在接下来中不断的更新。排序算法,这里要讲的有7种。

插入排序

选择排序

希尔排序

快速排序

三向切分快速排序

堆排序

归并排序

桶排序

接下来从其实现思路和其复杂度上进行分析来讲解这7种算法。

选择排序

这是排序中最简单的一种方式,当前的位置的数和后续位置的进行比较,小的转移到前面,然后不断的进行比较,由于为了减少每次比较后,位置的交换所带来的消耗,通常会通过一个标志位来进行标记,然后,当一次比较结束后进行位置上的交换。这也就是所谓的冒泡排序,通过排序小的数字不断的向上移动,直到到达最上面。

实现代码

public void sort(int[] array) {

if(array == null || array.length <= 1)

return;

int len = array.length;

for (int i = 0; i < len - 1; i++) {

for (int j = i + 1; j < len; j ++) {

if (array[j] < array[i])

exch(array, i, j);

}

}

}

public void exch(int []array,int a,int b) {

array[a] = array[a] + array[b];

array[b] = array[a] - array[b];

array[a] = array[a] - array[b];

}

时间复杂度: O(n^2) |空间复杂度: O(1) |不稳定

插入排序

选择排序中,如果对于一个给定的已经排好序的数组,我们的复杂度仍然是和一个乱序的数组的复杂度相同,而插入排序则可以解决这个问题,插入排序的思想是在遍历的时候如果当前的数小于前面的数,则将其向前移动直到其合适的位置,这样如果是一个已经排序好的数组,其复杂度为n,乱序则为n^2.

实现代码

public void sort(int[] array) {

if (array == null || array.length <=1)

return;

for (int i = 1; i < array.length; i++) {

for (int j = i; (j > 0) && (array[j] < array[j-1]); j--) {

exch(array, j-1, j);

}

}

}

此处在第二层循环的判断语句,将判断语句放置在for的判断语句中,使得代码变得简洁了很多,如果不这样的话,需要在内部循环外套一层判断,然后循环内部再做一个终止跳转。

时间复杂度:O(n~n^2) |空间复杂度:O(1)| 稳定

希尔排序

希尔排序是在插入排序上进行的改进,在插入排序中,如果一个较小的数字在后面的话,移动起来会很浪费时间,希尔排序的思路是通过设置不为1的间距,将一些较小的数字移动的前面,从而减少移动的次数。比较抽象。

实现代码

public void sort(int[] array) {

if (array == null || array.length <= 1)

return;

int h = 1;

while (h < array.length/3) h = h * 3 + 1;

while (h >= 1) {

for (int i = h; i < array.length; i++) {

for (int j = i; j >= h && array[j] < array[j-h]; j-=h)

exch(array, j, j-h);

}

h = h/3;

}

}

复杂度要比插入排序在某些情况下略好,具体不好计算。

h为我们设置的移动距离,开始为h的计算方法,然后按照插入排序的思路解决。只是移动的距离又原来的1变为现在的不等距离。最终还是回归到1上。

归并排序

归并排序,提到归并,就会有两种方式,一种是从低向上,一种是从上向下,从上向下为递归的方式,而从下向上为迭代的方式来解决。自顶向下,是首先将数组分成两个部分,然后递归这两个部分进行归并排序,最后再将这两部分进行合并。自底向上则是从最小的合并单元开始,不断的增加合并的步长,最后步长和我们的数组的长度相同了,则

实现代码

private int[] aux;

public void sort (int[] array) {

if (array == null || array.length <= 1)

return;

aux = new int[array.length];

sort(a, 0, array.length - 1);

}

public void sort(int[] array, int low, int high) {

if (low >= high)

return;

int mid = low + (high - low)/2;

sort(array, low, mid);

sort(array, mid+1, high);

merge(array, low, mid, high);

}

public void sort(int[] array, int low, int high) {

if (low >= high)

return;

for (int size = 1; size < N ; size += size) {

for (int low = 0; low <= high; low += 2*size)

merge(array, low, low+size-1, Math.min(low + 2*size -1, high -1));

}

}

public void merge (int[] array, int low, int mid, int high) {

int i = low, j = mid+1;

for (int k = low; k < high; k++)

aux[k] = array[k];

int k = low;

while(k < high) {

if (i > mid) {

array[k++] = aux[j++];

continue;

}

if (j > high) {

array[k++] = aux[i++];

continue;

}

if(aux[i] > aux[j]) {

array[k++] = aux[j++];

}else {

array[k++] = aux[i++];

}

}

}

时间复杂度:O(nlgn)|空间复杂度:O(n)|稳定

借助一个中间数组来实现合并操作。

堆排序

堆排序的实现方式是通过建立一个大堆或者是小堆,也就是所谓的优先队列,然后通过优先队列来实现数值的出队列来实现排序。这里通过维护一个最大堆,然后通过这个堆来进行排序,对于堆的操作,有下沉和上浮,首先,我们通过一个数组来表示这个结构,为了方便进行上浮和下沉,我们将数组的起始位置标记为1,方便我们在向上升和向下沉的操作。

实现代码

private int []array;

private int N = 0;

public MaxPQ(int maxN) {

array = new int[maxN+1];

}

public boolean isEmpty() {

return N == 0;

}

public int delMax() {

int max = -1;

if (N <= 0)

return;

max = array[1];

array[1] = array[N];

array[N] = 0;

N--;

sink(1);

return array[1];

}

public void insert(int num) {

array[++N] = num;

swim(N);

}

public void swim(int n){

while(n > 1 && array[n] > array[n/2]){

exch(n, n/2);

n = n/2;

}

}

public void sink(int n) {

while(2*n < N) {

if(array[n] >= array[2*n] && array[n] >= array[2*n+1])

break;

int tmp = array[2*n] > array[2*n+1] ? 2*n : 2*n+1;

exch(n, tmp);

n = tmp;

}

}

public void exch(int i, int j) {

int tmp = array[i];

array[i] = array[j];

array[j] = tmp;

}

public void sort() {

if(N <= 0)

return;

for (int k = N/2; k > 0; k--){

sink(k);

}

while(N > 1){

exch(1, N--);

sink(1);

}

}

时间复杂度:O(nlgn)|空间复杂度:N|不稳定

快速排序

快速排序采用的方式通过切分的方式,每次切分,将数组划分为两部分,一部分大于切分点,一部分小于切分点。然后通过不断地切分来实现排序。

实现代码

public void sort(int[] array, int low, int high) {

if (low >= high)

return;

int i = partition(array, low, high);

sort(array, low, i-1);

sort(array, j+1, high);

}

private int partition(int[] array, int low, int high) {

int lowFlag = low;

int highFlag = high+1;

while(true){

while(array[++lowFlag] <= array[low]) if(lowFlag == high) break;

while(array[--highFlag] >= array[low]) if(highFlag == low) break;

if(lowFlag >= highFlag) break;

exch(array, lowFlag, highFlag);

}

exch(array, low, highFlag);

return highFlag;

}

private void exch(int[] array, int i, int j){

int tmp = array[i];

array[i] = array[j];

array[j] = tmp;

}

时间复杂度:O(nlgn)|空间复杂度:O(1)|不稳定

核心代码在于切分部分,如何将我们的数组切分为两个部分,得到了第一个位置,之后,将其和数组的前部开始部分和后部进行比较,比较完成之后,前面为小于它的数字,后面为大于它的数字,如果不符合进行交换,最后返回切分位置。

三向切分快速排序

三向切分快速排序是在其快速排序的基础上来解决对于数组中的重复数字的问题,如果按照第一种方式,及时是相同的数字,也要被重新切分一次,因此想到通过这种方式,对于相同的数字只会被切分一次,因此需要增加一个标志位来控制相同的部分。

实现代码

public void sort(int[] array, int low, int high){

if(high <= low)

return;

int lowFlag = low;

int highFlag = high;

int i = low+1;

while(i < highFlag){

int tmp = array[low];

if(array[i] < tmp) exch(array, i++, lowFlag++);

else if(array[i] > tmp) exch(array, i, highFlag--);

else i++;

}

sort(array, low, lowFlag-1);

sort(array, highFlag+1, high);

}

如果遇到比第一位,也就是要作为切分点的数小的数,则将其与之进行交换,同时记录下来其位置,如果遇到比它大的数,则和后面的数进行交换,最后就可以将相同的数集中在一起。在上面的快速排序中,对于每一次判断没有进行一个交换,而是等到了最后的时候,才进行一个交换。

时间复杂度:O(nlgn)|空间复杂度:O(1)|不稳定

Java中的sort方法采用的是三向切分快速排序,但是该种算法有一个不好的地方是,在对于一个倒序的数组,这个时候,复杂度会比较高,因此通常会将其进行乱序操作,将数组打乱,然后再进行排序。

桶排序

桶排序相比上述方法来说是非常简单的,其复杂度是在n,但是空间开销是非常大的,特比是当数据集分布的不是很集中的时候,将会浪费很大的空间,属于典型的用空间来换取时间的一种做法。

实现代码

public void bucketSort(int[] array) {

if(array == null)

return;

int[] bucket = new int[11];

for (int i = 0; i < array.length; i++) {

bucket[array[i]]++;

}

int k = 0;

for(int j = 0; j < bucket.length; j++) {

while(bucket[j]-- > 0) {

array[k++] = j;

}

}

}

这里的桶做的是只能容纳

java shell排序算法_【算法】8种排序算法(Java)相关推荐

  1. 希尔排序是一种稳定的排序算法_十大经典排序算法——希尔排序

    vs code ppt c++/java 目录 1.1.排序分类 1.2.排序的定义: 对一序列对象根据某个关键字进行排序. 1.3.术语说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的 ...

  2. java数字排序方法_常见的数据排序方法

    排序,顾名思义,就是将一组无序的数据按照指定的顺序(一般是从大到小或从小到大)进行排列的过程.不管是在Java还是在JavaScript.PHP.C/C++等编程语言中,对数组(或集合)进行排序都是程 ...

  3. 6种java垃圾回收算法_被说烂了的Java垃圾回收算法,我带来了最“清新脱俗”的详细图解...

    一.概况 理解Java虚拟机垃圾回收机制的底层原理,是系统调优与线上问题排查的基础,也是一个高级Java程序员的基本功,本文就针对Java垃圾回收这一主题做一些整理与记录.Java垃圾回收器的种类繁多 ...

  4. java 线性回归算法_线性搜索或顺序搜索算法在Java中如何工作? 示例教程

    java 线性回归算法 大家好,之前,我讨论了二进制搜索算法的工作原理,并分享了在Java中实现二进制搜索的代码. 在那篇文章中,有人问我是否还有其他搜索算法? 如果数组中的元素未排序,又该如何使用它 ...

  5. java常见的hash算法_常见的哈希算法和用途

    写在前面 哈希算法经常会被用到,比如我们Go里面的map,Java的HashMap,目前最流行的缓存Redis都大量用到了哈希算法.它们支持把很多类型的数据进行哈希计算,我们实际使用的时候并不用考虑哈 ...

  6. java 寻路算法_游戏中的寻路算法解析

    游戏角色的自动寻路,已经是游戏中一个历史比较悠久的领域,较为成熟也有很多种实现.这里摘录一句后面所提的参考资料中的描述:"业内AI开发者中有一句话:"寻路已不是问题."我 ...

  7. java动态分区分配_操作系统动态分区分配算法课程设计java版解析.doc

    湖 南 文 理 学 院 实 验 报 告 课程名称 操作系统课程设计 实验名称 存储管理--动态分区分配算法的模拟 成绩 学生姓名 曹乐 专业 计算机 班级.学号 13101 18 同组者姓名 实验日期 ...

  8. akaze特征匹配怎么去掉不合适的点_图像匹配几种常见算法与实践

    奇技 · 指南 本文主要内容 1.模版匹配 2.特征匹配 3.深度学习去找目标 图像匹配的应用及背景 图像匹配是指通过一定的匹配算法在两幅或多幅图像之间识别同名点. 应用:遥感(制图更新),计算机视觉 ...

  9. 【算法篇】八种内排序算法

    常用的八种内排序算法分别是: 交换排序:冒泡排序.快速排序 选择排序:简单选择排序.堆排序 插入排序:直接插入排序.希尔排序 归并排序 基数排序 内排序巧记:选(选择)舰(简单选择)队(堆)的时候脚( ...

  10. k均值算法 二分k均值算法_如何获得K均值算法面试问题

    k均值算法 二分k均值算法 数据科学访谈 (Data Science Interviews) KMeans is one of the most common and important cluste ...

最新文章

  1. objective-C 中使用@Class和 #import区别
  2. java 内部类 返回值_Java基础第10天+形式参数和返回值的问题、包、导包、权限修饰符、常见的修饰符、内部类(局部内部类,成员内部类,匿名内部类)...
  3. 小猿圈之python的输入和输出
  4. 网络拓扑故障诊断讲解总结
  5. ZK Framework(一、HelloWorld)
  6. winfrom中DataGridView使用笔记
  7. leetcode 链表1
  8. c#开发大全、系列文章、精品教程
  9. oracle 卸载(手动,无universal installer)
  10. 如何快捷修改eclipse黑色背景和字体颜色设置?
  11. VR MultiPass\SinglePass(Instanced)\MultiView 浅析和区分总结
  12. method call expected
  13. Java web 几种实现在网页页面里播放视频的 插件及方法
  14. 蓝牙电话/耳机和蓝牙音乐profile
  15. 谷歌网盘国内下载API
  16. XV6实验-Lab1 Syscalls
  17. 统计字符串中字幕出现的数量(Map案例)
  18. The server time zone value ‘�й���׼ʱ��‘ is unrecognized or represents more than one time zone
  19. 虚拟机系列之-ubuntu系统克隆相同ip调整办法
  20. 用户 'sa' 登录失败的解决方案

热门文章

  1. SNMP OID是什么?
  2. 王者荣耀服务器维护5月22,5月22日王者荣耀更新内容一览
  3. mysql 环形复制_mysql复制(Replication)
  4. 光模块价格由带宽还是距离决定_光纤视频收发产品的光模块选型
  5. 中兴c600olt数据配置_2698元起中兴天机Axon 11发布 轻薄设计视频双防抖
  6. 清除浏览器缓存之后为什么还是显示旧的html页面_H5缓存机制浅析-移动端Web加载性能优化...
  7. Selenium3自动化测试——21.数据驱动应用
  8. 路径搜索算法 python实现_A*算法在栅格地图上的路径搜索(python实现)
  9. plus 什么是mybais_MyBatis和MyBatisPlus的区别是什么?
  10. 网站做好后不能用手机浏览吗_企业几年前制作的网站大部分都应该被淘汰掉