利用Java语言实现七大经典排序算法:冒泡排序、选择排序、插入排序、希尔排序、堆排序、归并排序以及快速排序。

分类
类别 算法
插入排序类 插入排序、希尔排序
选择排序类 选择排序、堆排序
交换排序类 冒泡排序、快速排序
归并排序类 归并排序
复杂度
算法 平均情况 最好情况 最坏情况 辅助空间 稳定性 复杂性
冒泡排序 O(n^2) O(n) O(n^2) O(1) 稳定 简单
选择排序 O(n^2) O(n^2) O(n^2 O(1) 稳定 简单
插入排序 O(n^2) O(n) O(n^2) O(1) 稳定 简单
希尔排序 O(nlogn)~O(n^2) O(n^1.3) O(n^2) O(1) 不稳定 较复杂
堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不稳定 较复杂
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定 较复杂
快速排序 O(nlogn) O(nlogn) O(n^2) O(logn)~O(n) 不稳定 较复杂
算法实现:排序算法类Sort
package com.chiaki.demo;

/**
* 排序算法类
* @author Chiaki
*/
public class Sort {
/**
* 工具函数:打印数组
* @param arr 原数组
*/
public static void printArr(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
} /**
* 工具函数:交换数组两个下标对应的值
* @param arr 原数组
* @param a 下标a
* @param b 下标b
*/
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
} /**
* 冒泡排序
* @param arr 原数组
*/
public static void bubbleSort01(int[] arr) {
// i从第1个元素开始
for (int i = 0; i < arr.length - 1; i++) {
// j从倒数第2个元素开始直到与i相遇
for (int j = arr.length - 2; j >= i; j--) {
// 比较倒数第1个元素与倒数第2个元素
if (arr[j] > arr[j + 1]) {
// 满足条件进行交换
swap(arr, j, j + 1);
}
}
}
} /**
* 改进冒泡排序:设置标志减少比较次数
* @param arr 原数组
*/
public static void bubbleSort02(int[] arr) {
boolean flag = true;
// i从第1个元素开始 flag为假时结束循环
for (int i = 0; (i < arr.length) && flag; i++) {
// flag初始化为假 若后续flag不改变为真则结束循环
flag = false;
// j从倒数第1个元素开始直到与i相遇
for (int j = arr.length - 1; j > i; j--) {
// 比较倒数第1个元素和倒数第2个元素
if (arr[j-1] > arr[j]) {
// 满足条件进行交换
swap(arr, j, j - 1);
// 发生交换则更新flag为真
flag = true;
}
}
}
} /**
* 选择排序
* @param arr 原数组
*/
public static void selectSort(int[] arr) {
for (int i=0; i<arr.length - 1; i++){
// 将当前元素指针设置为最小值指针min
int min = i;
// j从i的下一个元素开始
for (int j = i + 1; j < arr.length; j++) {
// 若arr[j]小于arr[min]则更新min
if (arr[min] > arr[j]) {
min = j;
}
}
// 如果min发生了更新就将数组元素值进行交换
if (i != min) {
swap(arr, i, min);
}
}
} /**
* 插入排序
* @param arr 原数组
*/
public static void insertSort(int[] arr) {
// i从第1个元素开始
for (int i = 1; i < arr.length; i++) {
// 将当前arr[i]保存在临时变量temp
int temp = arr[i];
// 设置与temp进行同组比较的索引j
int j = i - 1;
// 同组比较 若temp较小则进行后移操作
while (j >= 0 && arr[j] > temp) {
// 后移1位
arr[j + 1] = arr[j];
j--;
}
// 将temp插入到后移操作后的元素前面
arr[j + 1] = temp;
}
} /**
* 希尔排序
* @param arr 原数组
*/
public static void shellSort(int[] arr) {
// 希尔变量
int delta = arr.length / 2;
// 满足delta>0进行循环
while (delta > 0) {
for (int i = delta; i < arr.length; i++) {
// 将当前arr[i]保存在临时变量temp
int temp = arr[i];
// 设置与temp进行同组比较的索引j
int j = i - delta;
// 同组比较 若temp较小则进行后移操作
while (j >= 0 && arr[j] > temp) {
// 后移delta位
arr[j + delta] = arr[j];
j -= delta;
}
// 将temp插入到后移操作后的元素前面
arr[j + delta] = temp;
}
// 更新希尔增量
delta /= 2;
}
} /**
* 基于数组实现的大顶堆
* @param arr 数组
* @param size 堆大小
* @param index 堆索引
*/
private static void maxHeap(int[] arr, int size, int index) {
// 根据完全二叉树的性质得到左孩子和右孩子的索引
int left = 2 * index + 1;
int right = 2 * index + 2;
// 将当前索引index设置为最大值索引maxindex
int maxIndex = index;
// 若左孩子结点值比最大值索引结点值大则更新maxindex
if (left < size && arr[left] > arr[maxIndex]) {
maxIndex = left;
}
// 若右孩子结点值比最大值索引结点值大则更新maxindex
if (right < size && arr[right] > arr[maxIndex]) {
maxIndex = right;
}
// 若maxIndex发生了更新则进行交换操作
if (maxIndex != index) {
swap(arr, maxIndex, index);
// 递归
maxHeap(arr, size, maxIndex);
}
} /**
* 基于大顶堆的堆排序
* @param arr 原数组
*/
public static void heapSort(int[] arr) {
// 找到最后一个非叶子结点即最后一个叶子结点的父结点的索引作为开始
int parent = arr.length/2 - 1;
// 从parent开始针对原数组构建大顶堆
for (int i=parent; i>=0; i--) {
maxHeap(arr, arr.length, i);
// 打印大顶堆的调整过程
//printArr(arr);
}
// 基于大顶堆的排序
for (int i=arr.length-1; i>0; i--) {
// 将最后一个元素与根结点值进行交换
swap(arr, 0, i);
// 交换完成后重新调整大顶堆
maxHeap(arr, i, 0);
}
} /**
* 2路归并排序算法
* @param arr 原数组
* @param start 左边界
* @param end 右边界
*/
private static void merge(int[] arr, int start, int end) {
// 满足条件进行递归
if (start < end) {
int mid = (end + start) / 2;
// 对左子数组进行归并排序
merge(arr, start, mid);
// 对右子数组进行归并排序
merge(arr, mid + 1, end);
// 合并
int[] temp = new int[end - start + 1];
int i = start, j = mid + 1, k = 0;
while (i <= mid && j <= end) {
temp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= end) {
temp[k++] = arr[j++];
}
System.arraycopy(temp, 0, arr, start, end - start + 1);
}
} /**
* 调用2路归并排序算法
* @param arr 原数组
*/
public static void mergeSort(int[] arr) {
merge(arr, 0, arr.length - 1);
} /**
* 快速排序
* @param arr 原数组
* @param start 左指针
* @param end 右指针
*/
private static void sort02(int[] arr, int start, int end) {
// 满足条件进行递归
if (start < end) {
// 定义哨兵i和j指向数组头尾
int i = start, j = end;
// 哨兵不相遇则执行循环
while (i < j) {
// 一定是尾部哨兵j先行动
// 若arr[j]>中轴值则进行循环 否则跳出循环
while (i < j && arr[j] > arr[start]) {
j--;
}
// 尾部哨兵j停止行动后头部哨兵i行动
// 若arr[j]<=中轴值则进行循环 否则跳出循环
while (i < j && arr[i] <= arr[start]) {
i++;
}
// 哨兵行动完成后对i和j所在的值进行交换
if (i<j) {
swap(arr, i, j);
}
}
// 将中轴值与i处的值交换
// 保证中轴左边都比它小右边都比它大
swap(arr, start, i);
// 递归对中轴左边的子数组进行快排
sort02(arr, start, i - 1);
// 递归对中轴右边的子数组进行快排
sort02(arr, i + 1, end);
} } /**
* 调用快速排序算法
* @param arr 原数组
*/
public static void quickSort(int[] arr) {
sort02(arr, 0, arr.length-1);
}
}
算法测试:排序测试类SortTest
package com.chiaki.demo;

import org.junit.After;
import org.junit.Before;
import org.junit.Test; import static com.chiaki.demo.Sort.*; /**
* 排序测试类
* @author Chiaki
*/
public class SortTest {
private int[] arr; @Before
public void init() {
arr = new int[]{49, 38, 65, 97, 76, 13, 27, 50, 88};
System.out.print("Before: ");
printArr(arr);
} @After
public void show() {
System.out.print("After : ");
printArr(arr);
} /**
* 冒泡排序01测试结果
* Before: 49 38 65 97 76 13 27 50 88
* After : 13 27 38 49 50 65 76 88 97
*/
@Test
public void bubbleSortTest01() {
bubbleSort01(arr);
} /**
* 冒泡排序02测试结果
* Before: 49 38 65 97 76 13 27 50 88
* After : 13 27 38 49 50 65 76 88 97
*/
@Test
public void bubbleSortTest02() {
bubbleSort02(arr);
} /**
* 选择排序测试结果
* Before: 49 38 65 97 76 13 27 50 88
* After : 13 27 38 49 50 65 76 88 97
*/
@Test
public void selectSortTest() {
selectSort(arr);
} /**
* 插入排序测试结果
* Before: 49 38 65 97 76 13 27 50 88
* After : 13 27 38 49 50 65 76 88 97
*/
@Test
public void insertSortTest() {
insertSort(arr);
} /**
* 希尔排序测试结果
* Before: 49 38 65 97 76 13 27 50 88
* After : 13 27 38 49 50 65 76 88 97
*/
@Test
public void shellSortTest() {
shellSort(arr);
} /**
* 堆排序测试结果
* Before: 49 38 65 97 76 13 27 50 88
* After : 13 27 38 49 50 65 76 88 97
*/
@Test
public void heapSortTest() {
heapSort(arr);
} /**
* 归并排序测试结果
* Before: 49 38 65 97 76 13 27 50 88
* After : 13 27 38 49 50 65 76 88 97
*/
@Test
public void mergeSortTest() {
mergeSort(arr);
} /**
* 快速排序测试结果
* Before: 49 38 65 97 76 13 27 50 88
* After : 13 27 38 49 50 65 76 88 97
*/
@Test
public void quickSortTest() {
quickSort(arr);
}
}

结果显示上述算法均通过测试。

Java实现经典七大经典排序算法的更多相关文章

  1. Java基础复习笔记基本排序算法

    Java基础复习笔记基本排序算法 1. 排序 排序是一个历来都是很多算法家热衷的领域,到现在还有很多数学家兼计算机专家还在研究.而排序是计算机程序开发中常用的一种操作.为何需要排序呢.我们在所有的系统 ...

  2. 程序兵法:Java String 源码的排序算法(一)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 <程序兵法:Java Str ...

  3. Java中的数据结构及排序算法

    (明天补充) 主要是3种接口:List Set Map List:ArrayList,LinkedList:顺序表ArrayList,链表LinkedList,堆栈和队列可以使用LinkedList模 ...

  4. Java数据结构(七)—— 排序算法

    排序算法(Sort Algorithm) 排序算法介绍和分类 将一组数据,依指定顺序进行排列 排序的分类 内部排序 指将需要处理的所有数据都加载到内部存储器中进行排序 外部排序 数据量过大,无法全部加 ...

  5. Java常用的7大排序算法汇总

    1.插入排序算法 插入排序的基本思想是在遍历数组的过程中,假设在序号 i 之前的元素即 [0..i-1] 都已经排好序,本趟需要找到 i 对应的元素 x 的正确位置 k ,并且在寻找这个位置 k 的过 ...

  6. Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等

    本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排 ...

  7. java小程序整理及排序算法

    1. 利用循环打印如下图形 ***** **** *** ** * public class Main { public static void main(String[] args) { // TO ...

  8. 学习Java 以及对几大基本排序算法(对算法笔记书的研究)的一些学习总结(Java对算法的实现持续更新中)

    Java排序一,冒泡排序! 刚刚开始学习Java,但是比较有兴趣研究算法.最近看了一本算法笔记,刚开始只是打算随便看看,但是发现这本书非常不错,尤其是对排序算法,以及哈希函数的一些解释,让我非常的感兴 ...

  9. java泛型中使用的排序算法——归并排序及分析

    一.引言 我们知道,java中泛型排序使用归并排序或TimSort.归并排序以O(NlogN)最坏时间运行,下面我们分析归并排序过程及分析证明时间复杂度:也会简述为什么java选择归并排序作为泛型的排 ...

随机推荐

  1. SQL : 把特定的数据排前面 & 分别查询几组数据的最大值

    把特定的数据排前面 : 比如说,把没有审核身份证的人排最前面,然后再按userId正序排. select case when idcardverified = 1 then 0 else 1 end ...

  2. shell脚本带参数启动项目

    用maven工程打包时,会将数据库连接一并打进去,如果需要经常修改数据库连接,则需要打开jar包然后修改配置,这样很麻烦耗时并且容易出错. 因此需要将数据库配置放入项目外,这样修改数据库时去固定的配置 ...

  3. es6 Proxy简单使用

    es6的Proxy是什么? 可以理解为,是在访问对象前的一层拦截.只要访问的该对象,就要通过这个一层拦截.这一层的拦截,可以进行数据的过滤和更改 比如下面这个 var p = new Proxy({} ...

  4. 使用Faker库生成模拟数据

    一.相关文档 该库在laravel框架中默认已经存在,无需手动进行安装.使用参考文档: https://packagist.org/packages/fzaninotto/faker 二.简单示例 & ...

  5. 12天,这本《重学Java设计模式》PDF书籍下载量9k,新增粉丝1400人,Github上全球推荐榜!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言

  6. Ionic 警告框

    <html ng-app="mySuperApp"> <head> <meta charset="utf-8"> <m ...

  7. PHP fileatime() 函数

    定义和用法 fileatime() 函数返回指定文件的上次访问时间. 如果成功,该函数将以 Unix 时间戳形式返回文件的上次访问时间.如果失败,则返回 FALSE. 语法 fileatime(fil ...

  8. PHP fwrite() 函数

    定义和用法 fwrite() 函数将内容写入一个打开的文件中. 函数会在到达指定长度或读到文件末尾(EOF)时(以先到者为准),停止运行. 如果函数成功执行,则返回写入的字节数.如果失败,则返回 FA ...

  9. PHP tempnam() 函数

    定义和用法 tempnam() 函数在指定的目录中创建一个具有唯一文件名的临时文件. 该函数返回新的临时文件名,如果失败则返回 FALSE. 语法 tempnam(dir,prefix) 参数 描述 ...

  10. 4.15 省选模拟赛 编码 trie树 前缀和优化建图 2-sat

    好题 np. 对于20分 显然可以爆搜. 对于50分 可以发现每个字符串上的问号要么是0,要么是1.考虑枚举一个字符串当前是0还是1 这会和其他字符串产生矛盾. 所以容易 发现这是一个2-sat问题. ...