[Algorithm]十大排序算法动图图解及Java代码实现
Posted Spring-_-Bear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Algorithm]十大排序算法动图图解及Java代码实现相关的知识,希望对你有一定的参考价值。
排序算法
- 内部排序:数据记录在内存中进行排序
- 外部排序:数据量很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存
- 排序算法稳定性:排序前后 2 个相等数据的相对位置不发生变化
一、冒泡排序算法
- 冒泡排序算法基本思想:重复走访待排序的序列,依次比较两个元素,如果他们的大小关系不符合排序要求就交换它们的值
- 冒泡排序算法 java 代码实现:(优化思路:如果在某趟排序过程中未发生元素交换则说明数组已有序,可提前结束排序)
/**
* 冒泡排序法:升序
*
* @param array 待排序数组
*/
public void bubbleSort(int[] array)
int times = array.length - 1;
// 外层循坏共执行 len - 1 次
for (int i = 0; i < times; i++)
boolean flag = true;
for (int j = 0; j < times - 1 - i; j++)
// 如果前一个数比后一个数大则交换
if (array[j] > array[j + 1])
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
flag = false;
// 如果在某趟排序过程中未发生元素交换则说明数组已有序,可提前结束排序
if (flag)
break;
二、选择排序算法
- 选择排序算法基本思想:每次都从待排序的序列中选取一个最小(大)的元素放在已排序序列尾部
- 选择排序算法 java 代码实现
/**
* 选择排序法:降序
*
* @param array 待排序的数组
*/
public void selectSort(int[] array)
int len = array.length;
// 外层循环共执行 len - 1 次
for (int i = 0; i < len - 1; i++)
// 假设本轮选择排序中最大的数的下标为 i
int maxIndex = i;
for (int j = i + 1; j < len; j++)
// 从后序的数组元素中查找看是否有比当前 array[maxIndex] 更大的数,有则将其下标赋值给 maxIndex
if (array[j] > array[maxIndex])
maxIndex = j;
if (maxIndex != i)
int temp = array[i];
array[i] = array[maxIndex];
array[maxIndex] = temp;
三、插入排序算法
- 插入排序算法基本思想:把 n 个待排序的数据看作是一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含 n - 1 个元素,排序过程中每次从无序表中取出第一个元素,把它与有序表中的元素依次比较,插入到合适的位置使得有序表依然有序
- 插入排序算法 java 代码实现
/**
* 插入排序法:升序
*
* @param array 待排序数组
*/
public void insertionSort(int[] array)
int len = array.length;
// 从第二个元素开始依次为每个元素寻找插入位置
for (int rightIndex = 1; rightIndex < len; rightIndex++)
int waitInsertValue = array[rightIndex];
int leftIndex = rightIndex - 1;
// 从待插入数据的前一个元素向前查找,当左索引大于等于 0 且元素值大于待插入数据时继续向左查找,过程中元素逐次后移
while (leftIndex >= 0 && array[leftIndex] > waitInsertValue)
// 将大于待插入数据的元素依次后移
array[leftIndex + 1] = array[leftIndex];
--leftIndex;
// 查找结束将 waitInsertValue 插入到空位上
array[leftIndex + 1] = waitInsertValue;
四、希尔排序算法
- 希尔排序算法基本思想:希尔排序算法是对插入排序的一种更高效的改进版本,插入排序算法存在的问题:当需要插入的数据较小且在数组中靠后时,元素后移的次数明显增多,效率较低。希尔排序思想是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录 “基本有序” 时,再对全体记录进行一次直接插入排序。
- 希尔排序交换式算法 java 代码实现
/**
* 希尔排序:交换法
*
* @param array 待排序数组
*/
public void hillSort(int[] array)
int len = array.length;
// 依次将数组按 gap 间隔进行分组
for (int gap = len / 2; gap > 0; gap /= 2)
// 一组一组地进行遍历
for (int i = gap; i < len; i++)
// 遍历各组元素,并进行简单插入排序
for (int j = i - gap; j >= 0; j -= gap)
if (array[j] > array[j + gap])
int temp = array[j];
array[j] = array[j + gap];
array[j + gap] = temp;
- 希尔排序移位式算法 java 代码实现
/**
* 希尔排序:移位法
*
* @param array 待排序数组
*/
public void hillSort(int[] array)
int len = array.length;
// 按 gap 间隔对数组进行分组
for (int gap = len / 2; gap > 0; gap /= 2)
// 依次遍历每一组元素
for (int i = gap; i < len; i++)
int waitInsertValue = array[i];
int curIndex = i;
// 若当前数据比前一个数据小,继续向前寻找插入位置
if (array[curIndex] < array[curIndex - gap])
// 当未找到当前组的最左端元素且当前元素依旧比等待插入的元素值大时,继续寻找插入位置并将元素后移
while (curIndex - gap >= 0 && array[curIndex - gap] > waitInsertValue)
array[curIndex] = array[curIndex - gap];
curIndex -= gap;
// while 循环结束为当前等待插入的数据找到了插入位置
array[curIndex] = waitInsertValue;
五、快速排序算法
- 快速排序算法基本思想:选取一个基准数,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边),“分区” 结束之后该基准就处于数列的中间位置。递归地把小于基准值元素的子数列和大于基准值元素的子数列再次进行快排
- 快速排序算法 java 代码实现
/**
* 快速排序
*
* @param array 待排序数组
* @param begin 左索引,初始化为 0
* @param end 右索引,初始化为 array.length - 1
*/
public void quickSort(int[] array, int begin, int end)
if (begin > end)
return;
// 每次快排选取最左边的数作为基准数
int baseNum = array[begin];
int leftIndex = begin;
int rightIndex = end;
// 在 [begin,end] 区间范围内寻找比基准数小和比基准数大的数进行交换直至左索引与右索引相遇
while (leftIndex != rightIndex)
// 在右半区间寻找比基准数小的数
while (array[rightIndex] >= baseNum && rightIndex > leftIndex)
rightIndex--;
// 在左半区间寻找比基准数大的数
while (array[leftIndex] <= baseNum && leftIndex < rightIndex)
leftIndex++;
// 如果左半区间找到的比基准数大的数比右半区间找到的比基准数小的数还大,则他俩交换其值
if (array[leftIndex] > array[rightIndex])
int temp = array[leftIndex];
array[leftIndex] = array[rightIndex];
array[rightIndex] = temp;
// 将本轮基准数移动到 [begin,end] 区间的中间位置
array[begin] = array[leftIndex];
array[leftIndex] = baseNum;
// 对左半部分数据继续进行快排
quickSort(array, begin, leftIndex - 1);
// 对右半部分数据继续进行快排
quickSort(array, rightIndex + 1, end);
六、归并排序算法
- 归并排序算法基本思想:归并排序是建立在归并操作上的一种有效的排序算法,是分治思想的典型应用。归并排序对待排序序列的元素进行逐层折半分组,然后从最小分组开始进行排序,小分组有序后合并成一个大的分组,逐层进行。最终所有的元素都是有序的
- 归并排序算法 java 代码实现
/**
* 归并排序
*
* @param array 待排序数组
* @param begin 起始下标初始化为 0
* @param end 终止下标初始化为 array.length - 1
* @param temp 临时数组
*/
public void mergeSort(int[] array, int begin, int end, int[] temp)
if (begin < end)
int mid = (begin + end) / 2;
// 向左递归分解左部分
mergeSort(array, begin, mid, temp);
// 向右递归分解右部分
mergeSort(array, mid + 1, end, temp);
// 合并
merge(array, begin, end, temp);
/**
* 将每次分解的结果进行合并
*
* @param array 待排序数组
* @param begin 每次分解后左部分有序表的起始下标
* @param end 每次分结构有部分有序表的终止下标
* @param temp 临时数组
*/
private void merge(int[] array, int begin, int end, int[] temp)
// i 为左部分有序表的左索引
int i = begin;
// mid 为本次分解后的中间下标
int mid = (begin + end) / 2;
// j 为右部分有序表的左索引
int j = mid + 1;
// t 为临时数组 temp 的左索引
int t = 0;
// 按升序合并左、右两部分有序表直至一方合并完成,借助临时数组
while (i <= mid && j <= end)
// 拷贝左部分有序表元素到临时数组
if (array[i] <= array[j])
temp[t] = array[i];
t++;
i++;
else
// 拷贝右部分有序表元素到临时数组
temp[t] = array[j];
t++;
j++;
// 将左部分有序表剩余元素拷贝到临时数组
while (i <= mid)
temp[t] = array[i];
t++;
i++;
// 将右部分有序表剩余元素拷贝到临时数组
while (j <= end)
temp[t] = array[j];
t++;
j++;
// 最后将左、右部分有序表合并结果拷贝到原数组中
t = 0;
for (; begin <= end; begin++, t++)
array[begin] = temp[t];
七、堆排序算法
大顶堆和小顶堆说明
- 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序也是一种选择排序,它的最坏、最好、平均时间复杂度均为
O(nlog~2~n)
,是不稳定算法- 大顶堆:每个父节点的值都大于或等于其左右孩子节点的值(并未要求左右孩子值的大小关系)
- 小顶堆:每个父节点的值都小于或等于其左右孩子节点的值(并未要求左右孩子值的大小关系)
- 对堆中的节点按层从左至右从 0 开始编号映射到数组,有如下规律:
array[i] >= array[2 * i + 1] && array[i] >= array[2 * i + 2]
即父节点大于等于左右子节点- 堆排序时升序一般采用大顶堆,降序采用小顶堆
- 堆排序的基本思想:将待排序序列构建成一个大顶堆,此时整个序列的最大值就是此大顶堆的根节点;再将剩余的 n - 1 个元素重新构建成大顶堆,如此往复,最终可实现对序列的升序排列
- 排序过程:从最后一个非叶子节点开始(第一个非叶子节点在数组中的下标:
array.length / 2 - 1
,从左至右、从下至上进行调整,将最大的元素调整到根节点
- 堆排序的 java 代码实现
/**
* 将数组调整成堆结构
*
* @param array 待排序数组
* @param fatherIndex 非叶子节点在数组中的索引
* @param count 待调整元素的个数
*/
private void adjustToHeap(int[] array, int fatherIndex, int count)
// 取出当前非叶子节点(父节点)的值
int temp = array[fatherIndex];
// k = fatherIndex * 2 + 1 是以 fatherIndex 下标为父节点的左子节点下标
for (int k = fatherIndex * 2 + 1; k < count; k = k * 2 + 1)
// 左子节点比右子节点小
if (k + 1 < count && array[k] < array[k + 1])
k++;
// 如果子节点的值比父节点大,交换子父节点的值
if (array[k] > temp)
array[fatherIndex] = array[k];
array[k] = temp;
// 将子节点的下标赋给父节点以便从当前子节点再进行判断以完整当前子树的调整
fatherIndex = k;
else
break;
/**
* 升序:构建大顶堆
* 降序:构建小顶堆
*
* @param array 待排序数组
*/
public void heapSort(int[] array)
int len = array.length;
// 1. 将无序序列构建成一个堆,升序选择构建大顶堆
// i = len / 2 - 1 为 第一个非叶子节点在数组中的下标
for (int i = len / 2 - 1; i >= 0; i--)
adjustToHeap(array, i, array.length);
// 2. 将堆顶元素与数组末尾元素交换,以将最大元素 “沉” 到数组末端,下轮不再参与构建堆
// 3. 重新调整数组结构使其满足堆的定义,继续下沉
for (int j = len - 1; j > 0; j--)
int temp = array[j];
array[j] = array[0];
array[0] = temp;
adjustToHeap(array, 0, j);
八、计数排序算法
- 计数排序算法基本思想:计数排序不是比较排序,排序的速度快于任何比较排序算法。但计数排序要求输入的数据必须是有确定范围的整数
- 计数排序算法 java 代码实现
/**
* 计数排序
*
* @param array 待排序数组
* @return 有序数组
*/
private int[] countingSort(int[] array)
// 获取数组中最大元素的值,得到桶的个数
int bucketLen = getMaxValue(array) + 1;
int[] bucket = new int[bucketLen];
for (int value : array)
bucket[value]++;
int sortedIndex = 0;
for (int j = 0; j < bucketLen; j++)
while (bucket[j] > 0)
array[sortedIndex++] = j;
bucket[j]--;
return array;
private int getMaxValue(int[] arr)
int maxValue = arr[0];
for (int value : arr)
if (maxValue < value)
maxValue = value;
return maxValue;
九、桶排序算法
- 桶排序算法基本思想:桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到以下两点:在额外空间充足的情况下,尽量增大桶的数量使得映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中;同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要
- 桶排序算法 java 代码实现
/**
* 桶排序算法
*
* @param array 待排序数组
* @param bucketSize 桶的数量
* @return 有序数组
*/
private int[] bucketSort(int[] array, int bucketSize)
if (array.length == 0)
return array;
// 找到待排序数组中的最大值和最小值
int minValue = array[0];
int maxValue = array[0];
for (int value : array)
if (value < minValue)
minValue = value;
else if (value > maxValue)
maxValue = value;
// 向上取整,获得桶的个数
int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;
int[][] buckets = new int[bucketCount][0];
// 利用映射函数将数据分配到各个桶中
for (int j : array)
int index = (int) Math.floor((j - minValue) / bucketSize);
buckets[index] = arrAppend(buckets[index], j);
int arrIndex = 0;
for (int[买什么数据结构与算法,这里有:动态图解十大经典排序算法(含JAVA代码实现)
《数据结构与算法》十大经典排序算法动图演示(2019最新Python代码)