常用的排序算法有冒泡排序、插入排序和快速排序等
-
冒泡排序
-
简单选择排序
-
直接插入排序
-
希尔排序
-
堆排序
-
归并排序
-
快速排序
冒泡排序(n^2)
冒泡排序的特点是,每一轮循环后,最大的一个数被交换到末尾,因此,下一轮循环就可以“刨除”最后的数,每一轮循环都比上一轮循环的结束位置靠前一位。代码实现
import java.util.Arrays; public class Main { public static void main(String[] args) { int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 }; // 排序前: System.out.println(Arrays.toString(ns)); for (int i = 0; i < ns.length - 1; i++) { for (int j = 0; j < ns.length - i - 1; j++) { if (ns[j] > ns[j+1]) { // 交换ns[j]和ns[j+1]: int tmp = ns[j]; ns[j] = ns[j+1]; ns[j+1] = tmp; } } } // 排序后: System.out.println(Arrays.toString(ns)); } }
快速排序(nlogn)
基本思路:(从后往前找小于关键字的,从前往后找大于,找到的数挪到上一轮挪走数的位置上)
一次快排
先找到一个关键字,并将该关键字赋值给
tmp,通常选择第一个值作为关键字,tmp=array[0](为接下来的操作腾出一个地方,将比关键字小的值放在该处,保证该关键字不会被覆盖)定义一个low(即数组的第二个值 array[1]),和 height(即数组的最后一个值array[n])
第一次,从数组尾部开始向前查找(height--),判断前一个值是否小于设置的关键字
tmp,比关键字大的略过,比关键字小的数放到tmp位置上( array[0] = array[height]),即将 找到比关键字小的,放到空位上。然后 height不变,开始从前往后找(low++) ,找到比关键字小的略过,找到比关键字大的,将该值放到height(上一轮该位置的值被挪走,现在将找到的值填补到被挪走的)位置上,即 array[height] = array[low]
然后从后往前查找比关键字小的,重复3步骤,如此前后前后循环迭代
直到low与height相等时,将关键字
tmp的值放到该位置上 即 array[low] =tmp;一次快排后 关键字左边均小于关键字,右边均大于关键字,递归的对左右两边进行上述排序,直到顺序全部正确为止
/** * 快速排序:通过一趟排序将待排记录分割成独立的两部分,其中一部分均比另一部分小,则可分别对这两部分继续进行排序,已达到整个序列有序的目的 * * @author 张 */ public class QuickSortDemo { public static void main(String[] args) { int[] arr = {50, 10, 90, 30, 70, 40, 80, 60, 20}; System.out.println(Arrays.toString(arr)); quickSort(arr, 0, arr.length - 1); System.out.println(Arrays.toString(arr)); } private static void quickSort(int[] arr, int low, int height) { // 关键字的下标 即low和height重合时的下标 int pivot; if (low < height) { // 将数组分割为两部分 pivot = partition(arr, low, height); // 递归对左子数组和右子数组进行排序 quickSort(arr, low, pivot - 1); quickSort(arr, pivot + 1, height); } } /** * 对数组进行划分 * * @param arr 数组 * @param low 数组最小下标 * @param height 数组最大下标 * @return 划分完后关键字下标 */ private static int partition(int[] arr, int low, int height) { // 用子表的第一个记录作为关键字 int pivotKey = arr[low]; while (low < height) { // 先从后往前找大于关键字的 while (low<height && arr[height] >= pivotKey ) { height--; } // 将找到的大于关键字的数 直接赋值到‘空’位置上 arr[low] = arr[height]; // 从前往后找大于关键词 while(low<height && arr[low] <= pivotKey){ low++; } // 将找到的小于关键字的数 直接赋值到‘空’位置上 arr[height] = arr[low]; } // 最后将关键字的值赋值给最后空的位置上 arr[low] = pivotKey; // 返回关键字最终所在的下标 return low; } }
归并 排序(nlogn)
递归方法 :空间复杂度n+logn
基本思路:将初始数组的n个记录 看做是n个有序的子序列,每个子序列长度为1,然后两两归并,得到[n/2]个长度的为2或1的有序子序列,再两两归并,如此重复,直至得到一个长度为n的有序序列为止
/** * 归并排序 * 归并意思就是合并,并入 * @author 张 */ public class MergeSortDemo { public static void main(String[] args) { int[] arr = {16,7,13,10,9,15,3,2,5,8,12,1,11,4,6,14}; int[] temp = new int[arr.length]; System.out.println(Arrays.toString(arr)); mergeSort(arr,temp,0,arr.length-1); System.out.println(Arrays.toString(temp)); System.out.println(Arrays.toString(arr)); } private static void mergeSort(int[] arr,int[] temp, int low, int high) { int m; // 判断子序列长度是否为1 if (low == high){ temp[low] = arr[low]; }else { m = (low+high)/2; // 先对原数组的左右子序列进行递归切分,直至每个子序列长度为1 mergeSort(arr,temp,low,m); mergeSort(arr,temp,m+1,high); // 当每个字序列长度都为1时,开始进行合并 merge(arr,temp,low,m,high); } } /** * 合并方法 * @param arr 原数组 * @param temp 临时数组 * @param low 最左侧数的下标 * @param m 中间数的下标 * @param high 最右侧数的下标 */ private static void merge(int[] arr,int[] temp, int low, int m, int high) { int i =0; int j=m+1; int l = low; while(l<=m&&j<=high){ // 判断左子序列的值是否小于右子序列值 if (arr[l]<arr[j]){ // 是的话,将左子序列的值放在temp数组中 temp[i++] = arr[l++]; }else { // 否则将右子序列的值存放在temp数组中 temp[i++] = arr[j++]; } } // 将左子序列的其余值依次放进temp数组中 while(l<=m){ temp[i++] = arr[l++]; } // 将右子序列的其余值依次放入temp数组中 while(j<=high){ temp[i++] = arr[j++]; } // 将排好序的temp数组复制给原数组,以保证向上返回时为有序数组 for (int k = 0; k < i; k++){ // low+k 是为了右子序列进行复制从右边low开始 arr[low+k] = temp[k]; } } }
堆排序(nlogn)
基本思路:
先将大小为n的未排序的序列构造成一个大顶堆
将大顶堆的根节点和未排序序列的最后一个值交换, 将最大元素"沉"到数组末端;
然后将剩余n-1个未排序的序列()调整成一个大顶堆(从上往下进行,与子节点最大值交换位置进行调整)
然后得到元素中第二大的值
如此反复执行,便能得到一个有序序列
如何构造大/小顶堆?
根据大顶堆的特性: 给个节点的值都大于或等于其左右孩子的值 即
i >= 2i(左孩子) i>=2i+1(有=右孩子)0开头的数组为
arr[i]>=arr[2i+1] arr[i]>=arr[2i+2]构造大顶堆:就是从下往上,从右往左,将每个非叶子节点当做根节点,将该子树调整成一个大顶堆 即:比较非叶子节点值和它的子树值,如果该节点小于其左/右子树的值就交换(意思就是将最大的值放到该节点)
调整大顶堆:从上往下进行,与子节点最大值交换位置进行调整,循环往复,直到无子节点或大于左右子节点中最大的
/** * 堆排序 * 1\. 先调整为大顶堆 * 2\. 交换根节点和未排序序列最后一个 * * @author 张 */ public class HeapSort { public static void main(String[] args) { int[] arr = {5, 1, 9, 3, 7, 4, 8, 6, 2}; System.out.println(Arrays.toString(arr)); // 将无序数组构建成大顶堆(从下至上,从右至左,选择非叶子结点调整结构) for (int i = arr.length / 2 - 1; i >= 0; i--) { heapAdjust(arr, i, arr.length); } // 交换堆顶元素与末尾元素后,重新调整为大顶堆结构 for (int j = arr.length - 1; j > 0; j--) { //将堆顶元素与末尾元素进行交换 swap(arr, 0, j); //重新对堆进行调整 heapAdjust(arr, 0, j); } System.out.println(Arrays.toString(arr)); } /** * 交换元素 * * @param arr 数组 * @param i 0 构造或调整成大顶堆后的根节点在数组中的下标 * @param j 未排序序列的末尾元素下标 */ private static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } /** * 调整为大顶堆 * * @param arr 数组 * @param i 开始的节点位置 在数组中的下标 * @param length 数组的长度 */ private static void heapAdjust(int[] arr, int i, int length) { int temp = arr[i]; for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { // 判断右字节点是否大于左字节点 是的话 将k值+1(代表k为右字节点下标),k代表左子节点下标 if (k + 1 < length && arr[k] < arr[k + 1]) { k += 1; } // 判断父节点值是否大于子节点的最大值 是的话,将最大值赋值到要改变的节点,将该节点在数组中的下边改变为最大子节点的下标 if (temp < arr[k]) { arr[i] = arr[k]; i = k; } else { break; } } //将temp值放到最终的位置 arr[i] = temp; } }