一直想理解一下基本的排序算法,最近正好在搞java所以就一并了(为了便于理解,这儿都是以从小到大排序为目的)

冒泡排序

也就是比较连续的两个值,如果前面一个值大于后面一个值,则交换。

时间复杂度为O(n*n),是稳定排序(稳定性意思:当两个值相同时,排序前后的这两个值的相对位置是否有交换)

注意点:第二重循环参数

    // 冒泡排序,从小到大
private static void BubbleSort(int[] arr, int n) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
if (arr[j] > arr[j + 1])
Swap(arr, j, j + 1);
}
}
}

选择排序

就是从头到尾选择每个位置放这个位置到最后一个位置的最小值,其中我们可以标记,减少交换次数

时间复杂度为O(n*n),不稳定的排序

注意点:判断后再进行异或的交换

    // 选择排序
private static void SelectSort(int[] arr, int n) {
int temp;
for (int i = 0; i < n; ++i) {
temp = i;
for (int j = i + 1; j < n; ++j) {
if (arr[temp] > arr[j]) {
temp = j;
}
}
// 异或交换时必须判断
if (i != temp)
Swap(arr, i, temp);
}
}

直接插入排序

就是从头到尾遍历每个点,每个点再从尾到头找这个值放在数组的位置

时间复杂度为O(n*n),稳定的排序

注意点:temp与谁比较

    // 直接插入排序
private static void InsertSort(int[] arr, int n) {
// 哨兵
int temp;
for (int i = 1; i < n; ++i) {
temp = arr[i];
int k = i - 1;
// 注意从后向前
for (int j = k; j >= 0 && temp < arr[j]; --j, --k) {
arr[j + 1] = arr[j];
}
arr[k + 1] = temp;
}
}

快速排序

其实就类似分治的方法,取一个中枢元素,然后找到一个位置放这个中枢元素,保证这个位置前面的值不大于它,后面的值不小于它,接着分别递归

最坏时间复杂度为O(n*n),平均时间复杂度为O(n*log2n),所以可以先打乱顺序。是不稳定的排序

注意点:中间两个while的顺序

    // 模拟洗牌打乱顺序
private static void Shuffle(int[] arr, int n) {
Random random = new Random();
for (int i = 0; i < n; ++i) {
int x = random.nextInt(n - 1);
int y = random.nextInt(n - 1);
if (x != y)
Swap(arr, x, y);
}
Print(arr, n);
}
// 快速排序(最坏时间是n*n,所以我们首先可以打乱顺序)
private static void quickSort(int[] arr, int left, int right) {
// 哨兵
int temp = arr[left];
int i = left, j = right; // 出口
if (i >= j)
return; // 保证左边一定不比temp大,右边一定不比temp小
while (i < j) {
while (i < j && arr[j] >= temp) {
--j;
}
if (i < j) {
arr[i] = arr[j];
++i;
}
while (i < j && arr[i] <= temp) {
++i;
}
if (i < j) {
arr[j] = arr[i];
--j;
}
}
arr[i] = temp; // 左右递归
quickSort(arr, left, i - 1);
quickSort(arr, i + 1, right);
}

二路归并排序

其实很简单,就是从中间分成左右两个数组分别递归,在最后返回时我们保证两个数组都是有序数组再合并,可以知道使用双指针的方法可以在O(n)的

时间内合并两个有序数组,然后知道这样分下去其实是几乎完全的二叉树,高度为log2n

时间复杂度为O(n*log2n),是稳定的排序

注意点:指针向后移动

    // 二路归并排序
private static void Marge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right - left + 1];
int leftpos = left, rightpos = mid + 1;
// 双指针方法
for (int i = 0; i < right - left + 1; ++i) {
if (rightpos > right || leftpos <= mid && arr[leftpos] <= arr[rightpos]) {
temp[i] = arr[leftpos++];
} else {
temp[i] = arr[rightpos++];
}
}
for (int i = 0; i < right - left + 1; ++i) {
arr[i + left] = temp[i];
}
}
private static void MargeSort(int[] arr, int left, int right) {
if (left >= right)
return;
// 分成两段递归
int mid = (left + right >> 1);
MargeSort(arr, left, mid);
MargeSort(arr, mid + 1, right);
// 关键:合并两端有序数组,只需用O(n)时间做到
Marge(arr, left, mid, right);
}

希尔排序

极其简单的算法,简答的方法是是设置步长并每次减一半,接着使用插入排序,排的是相隔步长的元素

时间复杂度很麻烦,但是最坏也是O(n*n),平均大概是O(n^3/2),是不稳定排序

    // 希尔排序
private static void HillSort(int[] arr, int n) {
// 设置步长,即保证相隔步长个元素为有序数组
for (int step = n / 2; step > 0; step /= 2)
// 修改的插入排序
for (int i = step; i < n; ++i)
for (int j = i; j - step >= 0; j -= step)
if (arr[j - step] > arr[j])
Swap(arr, j - step, j);
}

堆排序

为了让额外的空间复杂度为O(1),可以首先倒着使用原数组下沉的思想(就是保证了此点的子树一定是堆)建立大顶堆,

接着依次将头放到新空出来的位置,再更新堆

时间复杂度是O(n*log2n),是不稳定排序

注意点:边界值得大小

    // 堆的下沉,保证此树为堆
private static void DeleteEmp(int[] arr, int fat, int n) {
int lefchild;
int temp = arr[fat];
for (; (fat << 1 | 1) < n; fat = lefchild) {
lefchild = fat << 1 | 1;
if (lefchild < n - 1 && arr[lefchild] < arr[lefchild + 1])
lefchild++;
if (arr[lefchild] > temp) {
arr[fat] = arr[lefchild];
} else {
break;
}
}
arr[fat] = temp;
}
// 堆排序(额外的辅助空间为O(1)),聪明的方法是利用原来的空间直接建树,
// 但是从小到大时我们需要建立大顶堆,接着删除第一个点放到数组最后一个位置,倒着插入
private static void HeapSort(int[] arr, int n) {
// 建堆(注意普通建堆时间复杂度为O(n),因为上浮的平均时间复杂度为O(1))
// 保证以个点为树的都是堆,倒着来就可以保证整棵树
for (int i = n / 2; i >= 0; --i) {
DeleteEmp(arr, i, n);
}
// 堆顶放入后面,再调整堆
for (int i = n - 1; i > 0; --i) {
Swap(arr, 0, i);
DeleteEmp(arr, 0, i);
}
}

以上都是基于比较的排序方法,接下来介绍一些非基于比较的排序算法,而且都是线性的时间复杂度

计数排序

就是将数字变成数组下标,这种排序主要是保证数据范围比较小。

时间复杂度为O(k),是不稳定的排序

基数排序

首先从低到高枚举的是数字的每一位,每一位根据0-9的顺序放入桶中储存,再放回原数组,注意负数,还有就是非常耗费空间

时间复杂度为O(n),是稳定的排序

    // 基数排序,时间复杂度为O(n)的排序方式,m为最大数字的位数+1
private static void BaseSort(int[] arr, int n, int m) {
// 假设数字范围为(0,1000000000)
int[] order = new int[10];
int[][] temp = new int[10][n];
int mm = 0, k = 1;
while (mm < m) {
// 从最低位开始放入10个桶中
for (int i = 0; i < n; ++i) {
int last = arr[i] / k % 10;
temp[last][order[last]++] = arr[i];
}
// 10个桶中按顺序放回数组
int coun = 0;
for (int i = 0; i < 10; ++i) {
if (order[i] > 0) {
for (int j = 0; j < order[i]; ++j)
arr[coun++] = temp[i][j];
order[i] = 0;
}
}
k *= 10;
++mm;
}
}

桶排序

将数组分到有限数量的桶子里。每个桶子再个别排序

时间复杂度为O(n),是稳定的排序

接着还有一些比较奇特的排序方式:

鸡尾酒排序:双向冒泡排序

梳排序:在冒泡排序下让步长不为1,而且每次步长除以1.3

奇偶排序:多核处理有优势;先将待排序数组的所有奇数位与自己身后相邻的偶数位相比较,如果前者大于后者,则进行交换,直到这一趟结束。然后将偶数位与自己身后相邻的奇数位相比较,如果前者大于后者,则进行交换,直到这一趟结束。重复

外排序:以上都是在内存中排序的算法,即可以在内存中直接寻址,而外排序则可能是输入数据在磁带上,它使用的是归并排序,但是可以是多路归并排序

树形选择排序(锦标赛排序) :对N个数字,选出最小(大)的n/2个数,再进行新一轮的比较,直到选出最小(大)的。

1.把N个数放到完全二叉树的叶子节点,两两比较,选出最小的作为根节点,且保存到数组中

2.把最小的原始值设为无穷大,从那个地方开始新一轮比较

第一次需要比较n-1次,之后都是log2(n)次

小结

对于一般的内部排序,基本使用的是插入排序,希尔排序或者快速排序,主要是根据输入数据的大小来确定。

注意希尔排序可以使用Sedgewick增量运行,则预计运行时间就为O(N^7/6),而快速排序找枢纽可以使用三数中值分割法。

堆排序比希尔排序要慢,但是也可以根据Floyd提出的改进算法移动一次数据来优化。

排序算法(java版)的更多相关文章

  1. 排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

    常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结 ...

  2. 排序算法系列:选择排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)

    在网上搜索算法的博客,发现一个比较悲剧的现象非常普遍: 原理讲不清,混乱 啰嗦 图和文对不上 不可用,甚至代码还出错 我总结一个清晰不罗嗦版: 原理: 从数组头元素索引i开始,寻找后面最小的值(比i位 ...

  3. 常用排序算法--java版

    package com.whw.sortPractice; import java.util.Arrays; public class Sort { /** * 遍历一个数组 * @param sor ...

  4. 排序算法Java版

    选择排序: public static void selectSort(int[]a) { int minIndex=0; int temp=0; if((a==null)||(a.length==0 ...

  5. 八大排序算法Java实现

    本文对常见的排序算法进行了总结. 常见排序算法如下: 直接插入排序 希尔排序 简单选择排序 堆排序 冒泡排序 快速排序 归并排序 基数排序 它们都属于内部排序,也就是只考虑数据量较小仅需要使用内存的排 ...

  6. 6种基础排序算法java源码+图文解析[面试宝典]

    一.概述 作为一个合格的程序员,算法是必备技能,特此总结6大基础算法.java版强烈推荐<算法第四版>非常适合入手,所有算法网上可以找到源码下载. PS:本文讲解算法分三步:1.思想2.图 ...

  7. 十大基础排序算法[java源码+动静双图解析+性能分析]

    一.概述 作为一个合格的程序员,算法是必备技能,特此总结十大基础排序算法.java版源码实现,强烈推荐<算法第四版>非常适合入手,所有算法网上可以找到源码下载. PS:本文讲解算法分三步: ...

  8. 八大排序算法Java

    目录(?)[-] 概述 插入排序直接插入排序Straight Insertion Sort 插入排序希尔排序Shells Sort 选择排序简单选择排序Simple Selection Sort 选择 ...

  9. 排序算法(Java实现)

    这几天一直在看严蔚敏老师的那本<数据结构>那本书.之前第一次学懵懵逼逼,当再次看的时候,发觉写的是非常详细,非常的好. 那就把相关的排序算法用我熟悉的Java语言记录下来了.以下排序算法是 ...

  10. 九大排序算法Java实现

    之前学习数据结构与算法时花了三天时间整理九大排序算法,并采用Java语言来实现,今天第一次写博客,刚好可以把这些东西从总结的文档中拿出来与大家分享一下,同时作为自己以后的备忘录. 1.排序算法时间复杂 ...

随机推荐

  1. encodeURI() 函数概述

    encodeURI() 函数的作用是将URI中的某些特定的字符以一位到四位的转义序列来替代,这些转义序列就是这些字符的UTF-8编码(如果说某些字符是由两个代替字符构成的,该字符也只会是四位的转义序列 ...

  2. [转]Linux Socket编程 Socket抓取网页源码

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

  3. 1076: [SCOI2008]奖励关

    1076: [SCOI2008]奖励关 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2078  Solved: 1118[Submit][Statu ...

  4. IIPP迷你项目(三)“Stopwatch: The Game”

    0 本周项目说明 这一次博客是Coursera的IIPP课程第三周迷你项目的实现,基础要求是做一个秒表,能start能stop能reset,更高的要求是在此秒表的基础上完成两个小游戏,但是鉴于第二个小 ...

  5. 企业实施DevOPS的七大挑战(转)

    从别人的演讲视频中摘抄,做笔记. 什么是DevOPS 如何衡量DevOPS 企业实施DevOPS的七大挑战 自动化测试投入不足 单元测试 API测试 界面测试 功能测试 高度集中的IT服务 标准化 脚 ...

  6. coursera 《现代操作系统》 -- 第十一周 IO系统

    本周要求 错题 下列I/O控制方式中,哪一个不需要硬件支持? 中断方式 轮询方式 DMA方式 I/O处理机方式 中断方式:中断控制器 轮询方式:CPU不断查询设备以了解其是否就绪 DMA:使用到了   ...

  7. Token bucket

    w https://en.wikipedia.org/wiki/Token_bucket

  8. Linux学习拾遗

    一.安装iso文件 首先建立一个目录作为挂载点:# mkdir /mnt/iso 获得root权限然后使用下面的参数挂载ISO映像文件:# mount -t iso9660 /path/image.i ...

  9. JS续

    JS中的事件 [JS中的事件分类] * 1.鼠标事件: * click/dbclick/mouseover/mouseout/mousemove/mousedown/mouseup * * 2.键盘事 ...

  10. xx云网络实施方案案例

    由于xx云在我公司进行试用,对接我方存储,于是乎就负责网络实施方案,下面是具体方案介绍 ip分配 具体网络拓扑如下: 下面是两台交换机配置 IPMI交换机配置如下: [Quidway]di cu !S ...