八种基本排序(选择排序selectSort,冒泡排序bubbleSort,插入排序insertSort,shell希尔排序shellSort,快速排序quickSort,归并排序mergeSort,堆排序heapSort,基数排序radixSort )
摘要:选择排序selectSort,冒泡排序bubbleSort,插入排序insertSort,shell希尔排序shellSort,快速排序quickSort,归并排序mergeSort,堆排序heapSort,基数排序radixSort
下述方法都是置于class里的静态方法,由main来调用的
public class XXXX { public static void main(String[] args) { int[] arr = {34, 5, 7, 12, 5, 34, 22, 11, 17, 76, 3, 5, 2, 87, 33, 51}; xxxSort(arr); System.out.println(Arrays.toString(arr)); } /*你的排序算法的代码在这个位置定义*/ }

选择排序:每个元素都和自己后边的相邻元素做比较,外循环和冒泡排序一样
//选择排序 public static void selectSort (int[] arr) { for (int i = 0; i < arr.length - 1; i ++) { for (int j = i + 1; j < arr.length; j ++) { if (arr[i] > arr[j]) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } }
冒泡排序:相邻元素比较,外循环表示要比较几轮,内循环表示每一轮的比较次数
public static void bubbleSort (int[] arr) { for (int i = 0; i < arr.length - 1; i ++) { for (int j = 0; j < arr.length - 1- i; j ++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } }
插入排序: 假定索引为0的元素已经排好序了,后边的元素和前边的元素比较,如果后边的元素小就插入在前边
public static void insertSort(int arr[]){ int length = arr.length; // 数组长度 for (int i = 1; i < length; i ++) { // i从下标为1的元素开始遍历. 插入排序就是假定索引为0的元素已经排好序了,所以从索引为1的开始 for (int j = i - 1; j >= 0; j --) { // 与自己之前的元素比较,如果之前的元素大于自己,那么就替换 if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } }
希尔排序:优化了的插入排序。将整个数组,分割成相同长度的几份,分别比较对应的数字大小;然后再进行分割,再比较大小....; 最后是简单的插入排序, 省去了一些比较时间
public static void shellSort(int[] arr) { for (int increment = arr.length / 2; increment > 0; increment /= 2) {//设置了每次比较分组的步长 for (int i = increment; i < arr.length; i += increment) {//以下代码都和插入排序一样了, 只不过现在互相比较的两个数字的间隔是increment而不是1 for (int j = i - increment; j >= 0; j -= increment) { if (arr[j] > arr[j + increment]) { int temp = arr[j]; arr[j] = arr[j + increment]; arr[j + increment] = temp; } } } } }
快速排序:把整个数组第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理;交换了以后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。

整个方法由三个方法组成,getMiddle计算中轴,divide分割数组, quickSort启动排序
//通过动态的挪移元素位置来查找中轴. 中轴的意义就是要将数组分成两部分,左边的元素都小于中轴,右边的元素都大于中轴 public static int getMiddle (int[] arr, int low, int high) { int temp = arr[low]; // 假定中轴的位置刚开始为数组的初始位置,同时也保存了这个中轴的元素,作为比较 while (low < high) { //挪移过程中会随时改变low低位和high高位的位置,限制循环的截止条件 while (low < high && arr[high] >= temp) { //如果高位的元素 >= 假定中轴的元素,合乎我们的目的 high --;//让高位往前挪一步,考察低一位的元素 } arr[low] = arr[high];//一旦发现高位的元素 < 假定中轴的元素,那么就把这个元素挪到低位的位置 //同时也结束了我们对高位元素的考察 //注意这时候中轴的这个元素也等于高位的元素,高位的那个元素还在那里,替换并没有完成 while (low < high && arr[low] < temp) {//从低位元素开始考察,如果低位元素 < 假定中轴元素,合乎我们的目的 low ++; //让低位往后挪一步,考察更高位的元素 } arr[high] = arr[low];//如果一旦有低位的元素 >= 中轴的元素,那么就把这个元素挪到高位的位置 //注意这个时候替换才完成,将假定中轴的元素赋给了那个高位 }//那么本次的挪移就结束了,每次调用该方法最多都会调整两个元素的位置,找到一个高位里面不合要求的,找到一个低位里不合要求的, //把两个互换 arr[low] = temp;//对于元素的一轮考察,挪移结束以后,那么中轴的位置还是在这个位置 return low; } public static void divide (int[] arr, int low, int high) { if (low < high) { int middle = getMiddle(arr, low, high); divide(arr, low, middle - 1); divide(arr, middle + 1, high); } } public static void quickSort (int[] arr) { if (arr.length > 0) { divide(arr, 0, arr.length - 1); } }
归并排序:
将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

整个方法由两个方法构成,mergeSort(arr, low, high) 也就是arr从0到arr.length-1来调用,merge(arr, low, mid, high) 归并数组的方法
/** * 归并排序 * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列 * 时间复杂度为O(nlogn) * 稳定排序方式 * @param arr 待排序数组 * @return 输出有序数组 */ public static int[] mergeSort(int[] arr, int low, int high) { int mid = (low + high) / 2; if (low < high) { // 左边 mergeSort(arr, low, mid); // 右边 mergeSort(arr, mid + 1, high); // 左右归并 merge(arr, low, mid, high); } return arr; } /** * 将数组中low到high位置的数进行排序 * @param arr 待排序数组 * @param low 待排的开始位置 * @param mid 待排中间位置 * @param high 待排结束位置 */ public static void merge(int[] arr, int low, int mid, int high) { int[] temp = new int[high - low + 1]; int i = low;// 左指针 int j = mid + 1;// 右指针 int k = 0; // 把较小的数先移到新数组中 while (i <= mid && j <= high) { if (arr[i] < arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; } } // 把左边剩余的数移入数组 while (i <= mid) { temp[k++] = arr[i++]; } // 把右边边剩余的数移入数组 while (j <= high) { temp[k++] = arr[j++]; } // 把新数组中的数覆盖nums数组, 这一步也可以在Java中由System.arraycopy方法完成 // System.arraycopy(temp, 0, nums, low, temp.length); for (int k2 = 0; k2 < temp.length; k2++) { arr[k2 + low] = temp[k2]; } }
堆排序:
* 根据现有的数组,建立一个堆,堆顶的元素最大,最下层的元素最小,然后将堆顶的元素和最下层最小的元素交换位置。
* 这时最大的元素跑到了堆的末尾,从堆中去除这个最大的元素,记录这个元素为最大,需要被放置在排序后的数组的末尾,其余的元素都要在它之前插入。
* 然后再把堆重新调整(让现在的堆中最大的元素跑到堆顶,最小的元素位于堆底)再把堆顶元素取出
* 就是每次取出一个最大的元素,调整堆,插入最后的数组的前边
整个方法由heapSort(arr)来调用,配合的方法有 getLeftChildIndex, getRightChildIndex, getParentIndex, buildMaxHeapify和maxHeapify
//注意:<<左移运算就是相当于十进制的*2,>>右移运算就是相当于十进制的/2, 带符号右移,正数高位补0,负数高位补1 //父节点索引 public static int getParentIndex (int current) { return (current - 1) >> 1;//简单来说就是 i / 2, 因为是int型的,奇数除不尽就直接省略小数部分 } //左子节点索引 public static int getLeftChildIndex (int current) { return (current << 1) + 1;//简单说就是current * 2 + 1 } //右子节点索引 public static int getRightChildIndex (int current) { return (current << 1) + 2;//简单说就是current * 2 + 2 } public static void buildMaxHeapify(int[] arr) { //没有子节点的才需要创建最大堆,从最后一个的父节点开始 int startIndex = getParentIndex(arr.length - 1); //从尾端开始创建最大堆,每次都是正确的堆 for (int i = startIndex; i >= 0; i --) { maxHeapify(arr, arr.length, i); } } /** * 创建最大堆 * @param arr * @param heapSize 需要创建最大堆的大小,一般在sort的时候用到,因为最多值放在末尾,末尾就不再归入最大堆了 * @param index 当前需要创建最大堆的位置 */ public static void maxHeapify(int[] arr, int heapSize, int index) {//最开始传入的index是数组最后元素的索引,假想该位置元素最大 //当前点与左右子节点比较 int left = getLeftChildIndex(index);//找出该节点的左子节点索引 int right = getRightChildIndex(index);//找出该节点的右子节点索引 int largest = index;//假定index位置的元素最大 if (left < heapSize && arr[largest] < arr[left]) {//发现这个假定为最大值的父节点比左子节点元素小 largest = left; } if (right < heapSize && arr[largest] < arr[right]) {//发现这个假定为最大值的父节点比右子节点元素小 largest = right; } //经过比较,如果子节点索引表示的元素大,那么largest就应该等于这个最大的子节点,交换数据 if (largest != index) { int temp = arr[largest]; arr[largest] = arr[index]; arr[index] = temp; maxHeapify(arr, heapSize, largest);//再继续调整堆的元素,这个时候就把上一轮选出的最大值置于父节点位置 } } /** * 排序,最大值放末尾,arr虽然是最大堆,在排序后就成了递增的了 * @param arr 需要排序的数组 * */ public static void heapSort (int[] arr) {//对该函数的解释如下图 //调用了buildHeapify生成了最大堆之后的特性决定了,堆顶也就是最大元素在左边,堆底在右边 //末尾与头交换,交换后调整最大堆 for (int i = arr.length - 1; i > 0; i--) { int temp = arr[0]; arr[0] = arr[i]; arr[i] = temp;//堆顶堆底元素互换,此时堆顶位于最右边,符合我们的排序期望 maxHeapify(arr, i, 0);//那么剩余的元素再调整堆 } }
/* 调用buildHeapify之后生成的数组是[90, 89, 14, 58, 87, 11, 7, 13, 56, 5, 32, 2, 1, 7, 4, 5, 3] 它表示的堆如下 90 89 14 58 87 11 7 56 5 32 2 1 7 4 5 3 可以看出堆中最大的元素排到了最左边 那么我们之后调用heapSort的时候, 1 把最左边也就是堆顶的元素和堆末尾的元素调换位置 2 这时候最大的元素就在最右边了,那么剩余的元素组成的堆,很显然应该让最大的元素位于堆顶,所以让剩余的元素重新调整堆的结构 */
基数排序: 基数排序又称桶排序。先按照数组中数字的个位数排列,再按照十位数排列....


public static void radixSort (int[] arr) { int maxDigit = 0;//获取数组中的数最多有几位数 for (int i: arr) {//将数组中的每个数转成字符串,记录并比较他们的长度即可 maxDigit = Math.max(maxDigit, Integer.toString(i).length()); } int[][] bucket = new int[10][arr.length];//数组的第一位表示 int[] order = new int[10];//数组order[i] 用来表示该位是i的数的个数 int n = 1;//一个参数取每个位数时使用,一个整数/1 % 10 表示取个位数,整数/10 % 10 表示取十位数... for (int m = 1; m <= maxDigit; m ++, n *= 10) {//m 表示先从哪个位数开始排序,1表示个位数,一直到maxDigit最大数位 //每次循环结束都要 m++ 比较下一个数位,同时n *= 10 方便取出这个数位的数字 for (int i = 0; i < arr.length; i ++) { int lsd = ((arr[i] / n) % 10);//取出数组中每个数的每一位,从个位数...十位数...百位数... bucket[lsd][order[lsd]] = arr[i];//每个数应该中放置在二维数组中的什么位置 order[lsd] ++;//该位是lsd的数的个数+1 } for (int i = 0, k = 0; i < 10; i ++) {// 一轮排序结束,将桶,也就是二维数组中保存的数重新写入原数组,以供下一轮排序使用 //i 表示的是 每一个数位的数都是从0~9, 也就是说,十位数是0~9的循环遍历一遍; k 表示的是数组的索引,循环赋值数组0 ~ arr.length if (order[i] != 0) {//该数位表示的数的个数不是0,也就是说,比如个位数是8的元素存在 for(int j = 0; j < order[i]; j ++, k ++) { arr[k] = bucket[i][j]; } } order[i] = 0;//因为order[]存放的是每一轮比较后每个数位的个数,所以一轮比较结束,并重新赋值后要将该数位清空 } } }
...................
浙公网安备 33010602011771号