# 八大排序算法 **Repository Path**: javanoteany/Sort ## Basic Information - **Project Name**: 八大排序算法 - **Description**: 八大排序算法 原理讲解 图文并茂 Java实现 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2021-11-09 - **Last Updated**: 2023-11-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: Java, 算法, 排序 ## README ### 八大排序算法 ### 简介 八大排序算法 原理讲解 图文并茂 Java实现 ##排序算法大体可分为两种: 1、比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等。 2、非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等。 ### (一)冒泡排序 1、比较相邻的元素。如果第一个比第二个大,就交换它们两个; 2、对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数; 3、针对所有的元素重复以上的步骤,除了最后一个; 4、重复步骤1~3,直到排序完成。 ``` import java.util.Arrays; /** * 冒泡排序的规则 * 1、从第一个数开始,将这个数 与它相邻的数比较,如果这个数大于相邻的数 * 则两个数交换位置 * 2、依次从第二个数开始比较,与相邻的数比较 * 3、以此类推 到倒数第二数截至 * 4、重复以上步骤 */ public class BubbleSort { public static void main(String[] args) { int[] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48}; //用于临时交换的变量 int temp = 0; for (int j = 0; j < array.length - 1; j++) { for (int i = 0; i < array.length - j - 1; i++) { if (array[i] > array[i + 1]) { //相邻的数比较 temp = array[i]; array[i] = array[i + 1]; array[i + 1] = temp; } } System.out.println("比较"+(j+1)+"轮之后:" + Arrays.toString(array)); } } } ``` ​ ### (二)选择排序 1、在未排序序列中找到最小(大)元素,存放到排序序列的起始位置; 2、再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾; 3、以此类推,直到所有元素均排序完毕。 ``` import java.util.Arrays; /** * 选择排序: 从一堆数中选择一个最小数 放在第一个位置,再从一堆数中选择一个 * 最小数放在第二个位置, 依次 将一堆数的最小数按顺序排放。 * 步骤: * 1、假设第一个数是最小数,需要定义最小数的下标minIndex=0 * 将这个数与后面的每一个数比较,找到最小数的下标即可 * 2、将第一个数与最小数的下标交换 ,得出最小数在第一位。 * 3、依次类推, 将已比较的数 忽略,继续从生下的元素中找足最小数,放入已比较的数的下一位 * 直到整个数列比较结束 */ public class SelectionSort { public static void main(String[] args) { int [] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48}; for(int j = 0;j <= array.length-1;j++){ int minIndex = j; // 假设第一个数是最小数 for (int i = 1+j; i < array.length; i++) { // 为什么i =j+1 因为初始值要略过已比较的下标 if (array[minIndex] > array[i]) { minIndex = i; int temp = 0; temp = array[j]; // 将这个最小数放在 第一位 array[j] = array[minIndex]; array[minIndex] = temp; } } System.out.println("第" + (j + 1) +"次完成后:" + Arrays.toString(array)); } System.out.println("最后的排序:" + Arrays.toString(array)); } } ``` ### (三)插入排序 1、从第一个元素开始,该元素可以认为已经被排序; 2、取出下一个元素,在已经排序的元素序列中从后向前扫描; 3、如果该元素(已排序)大于新元素,将该元素移到下一位置; 4、重复步骤3,直到找到已排序的元素小于或者等于新元素的位置; 5、将新元素插入到该位置后; 6、重复步骤2~5。 ``` import java.util.Arrays; /** * 插入排序 * 1、从第一个元素开始,假设第一个元素是已排好序的 * 2、从下一个元素开始,依次比较它前面的所有元素(从后向前扫描) * 3、 如果这个元素 小于它前面的元素 则两两交换 , * 如果这个元素 大于它前面的元素,则不交换 * 4、依次重复2,3步骤 ,直到将所有数 比较完成 */ public class InsertSort { public static void main(String[] args) { int [] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48}; for(int i = 1;i < array.length; i++){ int key = array[i]; int j = i - 1; while((j >= 0) && (key < array[j])){ array[j + 1]=array[j]; j--; } array[j + 1]=key; System.out.println("第" + i +"次完成后:"+ Arrays.toString(array)); } System.out.println("最后一次完成后的结果:"+Arrays.toString(array)); } } ​ ``` ### (四)归并排序 该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。 1、把长度为n的输入序列分成两个长度为n/2的子序列; 2、对这两个子序列分别采用归并排序; 3、将两个排序好的子序列合并成一个最终的排序序列。 ``` import java.util.Arrays; ​ public class MergeSort { public static void main(String[] args) { int[] array = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 }; mergeSort(array , 0 , array.length - 1 ); System.out.println(Arrays.toString(array)); ​ } ​ public static int[] mergeSort(int[] a,int low,int high){ int mid = (low+high)/2; if(low= rightIndex) { return; } int left = leftIndex; int right = rightIndex; //待排序的第一个元素作为基准值 int key = arr[left]; //从左右两边交替扫描,直到left = right while (left < right) { while (right > left && arr[right] >= key) { //从右往左扫描,找到第一个比基准值小的元素 right--; } //找到这种元素将arr[right]放入arr[left]中 arr[left] = arr[right]; while (left < right && arr[left] <= key) { //从左往右扫描,找到第一个比基准值大的元素 left++; } //找到这种元素将arr[left]放入arr[right]中 arr[right] = arr[left]; } //基准值归位 arr[left] = key; //对基准值左边的元素进行递归排序 quickSort(arr, leftIndex, left - 1); //对基准值右边的元素进行递归排序。 quickSort(arr, right + 1, rightIndex); } } ``` ### (六)堆排序 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 最大堆要求节点的元素都要不小于其孩子,最小堆要求节点元素都不大于其左右孩子 那么处于最大堆的根节点的元素一定是这个堆中的最大值. ``` import java.util.Arrays; ​ public class HeapSort { public static void main(String[] args) { int [] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48}; HeapSort(array, array.length); System.out.println(Arrays.toString(array)); } ​ public static void Swap(int array[], int i, int j) { int temp = array[i]; ​ array[i] = array[j]; ​ array[j] = temp; ​ } ​ public static void Heapify(int A[], int i, int size){ // 从A[i]向下进行堆调整 int left_child = 2 * i + 1; // 左孩子索引 int right_child = 2 * i + 2; // 右孩子索引 int max = i; // 选出当前结点与其左右孩子三者之中的最大值 if (left_child < size && A[left_child] > A[max]) max = left_child; if (right_child < size && A[right_child] > A[max]) max = right_child; if (max != i) { Swap(A, i, max); // 把当前结点和它的最大(直接)子节点进行交换 Heapify(A, max, size); // 递归调用,继续从当前结点向下进行堆调整 } } ​ public static int BuildHeap(int A[], int n){ // 建堆,时间复杂度O(n) int heap_size = n; for (int i = heap_size / 2 - 1; i >= 0; i--) // 从每一个非叶结点开始向下进行堆调整 Heapify(A, i, heap_size); return heap_size; } public static void HeapSort(int A[], int n) { int heap_size = BuildHeap(A, n); // 建立一个最大堆 while (heap_size > 1) {// 堆(无序区)元素个数大于1,未完成排序 // 将堆顶元素与堆的最后一个元素互换,并从堆中去掉最后一个元素 // 此处交换操作很有可能把后面元素的稳定性打乱,所以堆排序是不稳定的排序算法 Swap(A, 0, --heap_size); Heapify(A, 0, heap_size); // 从新的堆顶元素开始向下进行堆调整,时间复杂度O(logn) } } } ``` ### (七)希尔排序 1、选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1; 2、按增量序列个数k,对序列进行k 趟排序; 3、每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度 常用的h序列由Knuth提出,该序列从1开始,通过如下公式产生: h = 3 * h +1 反过来程序需要反向计算h序列,应该使用 h = ( h - 1 ) / 3 ``` public class ShellSort { public static void main(String[] args) { int array[] = {1,2,4,3,9,7,8,6}; int h = 0; int length = array.length; while( h <= length ){ //计算首次步长 h = 3 * h + 1; } while( h >= 1 ){ for( int i = h;i < length;i++ ){ int j = i - h; //左边的一个元素 int get = array[i]; //当前元素 while( j >= 0 && array[j] > get ){ //左边的比当前大,则左边的往右边挪动 array[j+h] = array[j]; j = j - h; } array[j + h] = get; //挪动完了之后把当前元素放进去 } h = ( h - 1 ) / 3; } for( int i = 0 ; i < array.length ; i++ ){ System.out.print(array[i]+" "); } } } ​ ``` ### (八)基数排序 1、通过序列中各个元素的值,对排序的N个元素进行若干趟的“分配”与“收集”来实现排序。 2、分配:我们将L[i]中的元素取出,首先确定其个位上的数字,根据该数字分配到与之序号相同的桶中 3、收集:当序列中所有的元素都分配到对应的桶中,再按照顺序依次将桶中的元素收集形成新的一个待排序列L[ ] 4、对新形成的序列L[]重复执行分配和收集元素中的十位、百位...直到分配完该序列中的最高位,则排序结束 ``` /** * 基数排序:Java */ ​ public class RadixSort { /* * 获取数组a中最大值 * * 参数说明: * a -- 数组 * n -- 数组长度 */ private static int getMax(int[] a) { int max; max = a[0]; for (int i = 1; i < a.length; i++) if (a[i] > max) max = a[i]; return max; } /* * 对数组按照"某个位数"进行排序(桶排序) * * 参数说明: * a -- 数组 * exp -- 指数。对数组a按照该指数进行排序。 * * 例如,对于数组a={50, 3, 542, 745, 2014, 154, 63, 616}; * (01) 当exp=1表示按照"个位"对数组a进行排序 * (02) 当exp=10表示按照"十位"对数组a进行排序 * (03) 当exp=100表示按照"百位"对数组a进行排序 * ... */ private static void countSort(int[] a, int exp) { //int output[a.length]; // 存储"被排序数据"的临时数组 int[] output = new int[a.length]; // 存储"被排序数据"的临时数组 int[] buckets = new int[10]; ​ // 将数据出现的次数存储在buckets[]中 for (int i = 0; i < a.length; i++) buckets[ (a[i]/exp)%10 ]++; ​ // 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。 for (int i = 1; i < 10; i++) buckets[i] += buckets[i - 1]; ​ // 将数据存储到临时数组output[]中 for (int i = a.length - 1; i >= 0; i--) { output[buckets[ (a[i]/exp)%10 ] - 1] = a[i]; buckets[ (a[i]/exp)%10 ]--; } ​ // 将排序好的数据赋值给a[] for (int i = 0; i < a.length; i++) a[i] = output[i]; ​ output = null; buckets = null; } ​ /* * 基数排序 * * 参数说明: * a -- 数组 */ public static void radixSort(int[] a) { int exp; // 指数。当对数组按各位进行排序时,exp=1;按十位进行排序时,exp=10;... int max = getMax(a); // 数组a中的最大值 ​ // 从个位开始,对数组a按"指数"进行排序 for (exp = 1; max/exp > 0; exp *= 10) countSort(a, exp); } ​ public static void main(String[] args) { int i; int a[] = {53, 3, 542, 748, 14, 214, 154, 63, 616}; ​ System.out.printf("before sort:"); for (i=0; i