之前在学校学习数据结构的时候,学不太懂也没太认真学,最近找了个时间总结归纳后自己写了一下十种排序算法,用的是Java写的,暂时只适用于非负整数的排序,在关键代码上加了注释讲解,代码可能会存在bug,如有发现希望各位指教!
1.冒泡排序
最基本的排序算法,本质是依次遍历数组找到最小的(或最大)数交换到数组首位。但时间复杂度很高因为数组一次遍历只能确定一个最小的数。
图解
代码
package com.DataStructure;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{11, 32, 42, 53, 76, 89, 90, 100, 30, 83})));
System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 5, 4, 61, 35, 74, 65, 3, 45})));
System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})));
System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18})));
}
public static int[] sort(int[] array) {
if (array == null || array.length <= 1) {
return array;
}
int local;
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] > array[j]) {
local = array[i];
array[i] = array[j];
array[j] = local;
}
}
}
return array;
}
}
2.选择排序
顾名思义关键在于选择,本质是从数组中找到最小的元素将他位置记录下来,遍历完与数组首位元素交换。过程和冒泡排序很相似,都是找最小元素交换的过程,
图解
代码
package com.DataStructure;
import java.util.Arrays;
public class SelectionSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{41, 63, 45, 5, 61, 121, 90, 35})));
System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 45, 45, 61, 35, 74, 65, 3, 45})));
System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})));
System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18})));
}
public static int[] sort(int[] array) {
if (array == null || array.length <= 1) {
return array;
}
for (int i = 0; i < array.length; i++) {
int minIndex = i;
for (int j = i + 1; j < array.length; j++) {
if (array[j] < array[minIndex]) {
minIndex = j;
}
}
swap(array, i, minIndex);
}
return array;
}
private static void swap(int[] array, int first, int last) {
int swap = array[first];
array[first] = array[last];
array[last] = swap;
}
}
3. 插入排序
插入排序是从数组第二个元素将该元素和前面的元素进行比较排序,使未排序元素和前面已排序序列每个元素两两比较,找到插入的位置构建排序序列的过程,但因为已排序序列是倒序排好,所以当判断已排序序列元素比该元素小时就可以停止遍历插入。
图解
代码
package com.DataStructure;
import java.util.Arrays;
public class InsertionSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{41, 63, 45, 5, 61, 121, 90, 35})));
System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 45, 45, 61, 35, 74, 65, 3, 45})));
System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})));
System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18})));
}
public static int[] sort(int[] a) {
int temp;
for (int i = 1; i < a.length; i++) {
for (int j = i; j > 0; j--) {
if (a[j] < a[j - 1]) {
temp = a[j];
a[j] = a[j - 1];
a[j - 1] = temp;
} else {
break;
}
}
}
return a;
}
}
4.希尔排序
希尔排序是一种特殊的插入排序,对数组元素通过不同间隔分组排序,最后需以间隔为1进行排序,来达到有序序列。
图解
代码
package com.DataStructure;
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 5, 4, 61, 35, 74, 65, 3, 45})));
System.out.println(Arrays.toString(sort(new int[]{11, 32, 42, 53, 76, 89, 90, 100, 30, 83})));
System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})));
System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18})));
}
public static int[] sort(int[] array) {
if (array == null || array.length <= 1) {
return array;
}
for (int distance = array.length / 2; distance > 0; distance /= 2) {
for (int i = distance; i < array.length ; i++) {
for (int j = i; j >= distance && array[j] < array[j-distance]; j -= distance) {
swap(array, j, j-distance);
}
}
}
return array;
}
private static void swap(int[] array, int first, int last) {
int swap = array[first];
array[first] = array[last];
array[last] = swap;
}
}
5.归并排序
归并排序本质是一种分而治之的思想,先对数组进行对半拆分直到数组长度为1不可拆分后,对两个不可拆分数组进行排序合并然后和另外一组合并的数组继续合并直到排序完成的过程。先拆后合的思想。
图解
代码
package com.DataStructure;
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{11, 57, 42, 53, 21, 89, 90, 100, 30, 83})));
System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 45, 45, 61, 45, 74, 65, 3})));
System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})));
System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18})));
}
public static int[] sort(int[] array) {
int[] temp = new int[array.length];
return sort(array, 0, array.length - 1, temp);
}
public static int[] sort(int[] array, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) >> 1;
sort(array, left, mid, temp);
sort(array, mid + 1, right, temp);
merge(array, left, mid, right, temp);
}
return array;
}
private static void merge(int[] array, int left, int mid, int right, int[] temp) {
int localLeft = left;
int localMid = mid + 1;
int localRight = right;
int index = 0;
while (localLeft <= mid && localMid <= localRight) {
if (array[localLeft] <= array[localMid]) {
temp[index++] = array[localLeft++];
} else {
temp[index++] = array[localMid++];
}
}
while (localLeft <= mid) {
temp[index++] = array[localLeft++];
}
while (localMid <= right) {
temp[index++] = array[localMid++];
}
index = 0;
while (left <= right){
array[left++] = temp[index++];
}
}
}
6.快速排序
快速排序和归并排序有点相似,也是将数组进行拆分,但归并是拆分后对子数组进行排序后合并,而快速则是将基准元素进行交换达到左侧为小于基准元素和大于基准元素分列两边。
图解
代码
写了两种思路: 1?? 左右两指针根据依次移动,目标元素target为数组第一个元素,右指针找到比target小的,左指针找到比target大的两者交换,直到左右指针相遇,将target元素和相遇位置元素交换,然后左右两边数组继续递归。 2?? 先右指针移动,目标元素target为数组第一个元素,遇到比target小的就将target和右指针对应元素交换,然后左指针移动找到比target元素大的数,将target和该数进行交换。直到左右指针相遇,继续上述操作递归。
package com.DataStructure;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{60, 32, 42, 53, 76, 89, 90, 100, 30, 83})));
System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 45, 45, 61, 35, 74, 65, 3, 45})));
System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})));
System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18})));
}
public static int[] sort(int[] array, int left, int right) {
if (left < 0 || right > array.length - 1) {
throw new IndexOutOfBoundsException("排序数组范围越界");
}
if (array == null || array.length <= 1) {
return array;
}
partition(array, left, right);
return array;
}
private static void partition(int[] array, int left, int right) {
int leftIndex = left;
int currentIndex = left;
int rightIndex = right;
if (left >= right) {
return;
}
while (left < right) {
while (left < right && array[currentIndex] <= array[right]) {
right--;
}
while (left < right && array[currentIndex] >= array[left]) {
left++;
}
if (left < right) {
swap(array, left, right);
}
}
swap(array, currentIndex, left);
partition(array, leftIndex, left - 1);
partition(array, right + 1, rightIndex);
}
public static void partition2(int[] array, int left, int right) {
if (left >= right) {
return;
}
int leftIndex = left;
int currentIndex = left;
int rightIndex = right;
while (left < right) {
while (left < right && array[right] >= array[currentIndex]) {
right--;
}
if (left < right) {
swap(array, right, currentIndex);
currentIndex = right;
}
while (left < right && array[left] <= array[currentIndex]) {
left++;
}
if (left < right) {
swap(array, left, currentIndex);
currentIndex = left;
}
}
partition2(array, leftIndex, left - 1);
partition2(array, right + 1, rightIndex);
}
public static int[] sort(int[] array) {
return sort(array, 0, array.length - 1);
}
private static void swap(int[] array, int first, int last) {
int swap = array[first];
array[first] = array[last];
array[last] = swap;
}
}
7.堆排序
堆排序是一种很巧妙的排序,将数组看作树结构,对数组依次进行大顶堆操作找出最大的元素,移到末尾,直到所有元素排序完成。
图解
代码
package com.DataStructure;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 5, 4, 61, 35, 74, 65, 3})));
System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})));
System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18})));
}
public static int[] sort(int[] array) {
if (array == null || array.length <= 1) {
return array;
}
for (int i = array.length - 1; i > 0; i--) {
bigHeapSort(i, array);
swap(array, 0, i);
}
return array;
}
private static void bigHeapSort(int last, int[] array) {
int root = last / 2;
for (int i = root; i >= 0; i--) {
int leftChild = 2 * i + 1;
int rightChild = 2 * i + 2;
if (rightChild < last + 1 && array[rightChild] > array[i]) {
swap(array, i, rightChild);
}
if (leftChild < last + 1 && array[leftChild] > array[i]) {
swap(array, i, leftChild);
}
}
}
private static void swap(int[] array, int first, int last) {
int swap = array[first];
array[first] = array[last];
array[last] = swap;
}
}
8.计数排序
计数排序本质不是一种排序算法,他统计每个元素出现的次数,然后将元素依次输出到数组中,所需要额外的数组长度取决于最大和最小数的差,如果数组中分布不集中,将会浪费大量空间。
图解
代码
package com.DataStructure;
import java.util.Arrays;
/**
-
计数排序 -
@author endwas -
@date Created in 2021/8/19 11:22 */ public class CountingSort { public static void main(String[] args) { System.out.println(Arrays.toString(sort(new int[]{11, 25, 42, 53, 76, 89, 21, 70, 33, 83}))); System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 5, 4, 61, 35, 74, 65, 3}))); System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}))); System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18}))); } public static int[] sort(int[] array) { if (array == null || array.length <= 1) { return array; } int maxValue = BucketSort.getMaxValue(array); int minValue = BucketSort.getMinValue(array); // System.out.println("max: " + maxValue + " min: " + minValue); // 创建数组 大小为 最大数-最小数 20-12+1 = 长度9 // 12 13 14 15 16 17 18 19 20 int[] countingArray = new int[maxValue - minValue + 1]; // 给每个数计数 最小的数在0的位置 // value-minValue 15-12即在3的位置 for (int value : array) { countingArray[value - minValue]++; } // 根据计数数组得到当前排序位置 int i = array.length; // 倒序遍历计数数组 从最后一位 index = length-1 for (int j = countingArray.length - 1; j >= 0; j–) { // 拿到当前的值 即个数 遍历次数 // 修改array数组 for (int k = countingArray[j]; k > 0; k–) { array[–i] = minValue + j; } } return array; } }
9.桶排序
桶排序本质是计数排序的一种优化,在计数排序中一个数将占用一个额外的储存空间,桶排序将一个桶存储一个范围的数,节省了空间。
图解
代码
package com.DataStructure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class BucketSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{11, 32, 42, 53, 76, 89, 90, 100, 30, 83})));
}
public static int[] sort(int[] array) {
if (array == null || array.length <= 1) {
return array;
}
int maxValue = getMaxValue(array);
int minValue = getMinValue(array);
System.out.println("max: " + maxValue + " min: " + minValue);
int difference = maxValue - minValue;
int bucketNum = difference / array.length + 1;
List<List<Integer>> lists = new ArrayList<>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
lists.add(new ArrayList<>());
}
for (int value : array) {
lists.get(bucketNum - 1 - ((maxValue - value) / array.length)).add(value);
}
int count = 0;
System.out.println("buckets are: " + lists);
for (List<Integer> list : lists) {
list.sort(Comparator.comparingInt(a -> a));
if (!list.isEmpty()) {
for (int integer : list) {
array[count++] = integer;
}
}
}
return array;
}
public static int getMaxValue(int[] array) {
int maxValue = Integer.MIN_VALUE;
for (int value : array) {
maxValue = Math.max(value, maxValue);
}
return maxValue;
}
public static int getMinValue(int[] array) {
int minValue = Integer.MAX_VALUE;
for (int value : array) {
minValue = Math.min(value, minValue);
}
return minValue;
}
}
10.基数排序
基数排序是桶排序的优化,根据从个位数到最高位依次来分配桶,所以一共只会有10个桶(对应位数为0-9)但相对于桶排序,基数排序在桶内的元素是排序好顺序的。
图解
代码
package com.DataStructure;
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
System.out.println(Arrays.toString(sort(new int[]{41, 63, 45, 5, 61, 121, 90, 35})));
System.out.println(Arrays.toString(sort(new int[]{121, 63, 45, 45, 45, 61, 35, 74, 65, 3, 45})));
System.out.println(Arrays.toString(sort(new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})));
System.out.println(Arrays.toString(sort(new int[]{10, 94, 128, 32, 61, 0, 456, 312, 2021, 18})));
}
public static int[] sort(int[] array){
if (array == null || array.length <= 1) {
return array;
}
int maxLength = getMaxLength(array);
int mod = 1;
for (int i = 0; i < maxLength; i++) {
int[][] results = new int[10][array.length];
int[] count = new int[10];
for (int value : array) {
int charNum = (value / mod) % 10;
results[charNum][count[charNum]++] = value;
}
int s = 0;
for (int k = 0; k < count.length; k++) {
for (int t = 0; t < count[k]; t++) {
array[s++] = results[k][t];
}
}
mod *= 10;
}
return array;
}
private static int getNumLength(int num){
int count = 0;
while (num > 0){
num /= 10;
count++;
}
return count;
}
private static int getMaxLength(int[] array){
int k = 0;
for (int value : array) {
k = Math.max(getNumLength(value), k);
}
return k;
}
}
总结
十种排序算法各有各的特色,有常规的遍历排序冒泡排序、选择排序;有很巧妙的排序算法堆排序;也有通过递归完成的快速排序、归并排序;也有计数归纳排序的计数排序、桶排序、基数排序; 排序算法是算法的基础,之前一直没认真学,最近就抽空边学边总结十种排序算法,收获还是很多的,代码还是很多地方可以优化的,感觉并没有达到每种算法的最好时间、空间复杂度。
|