【算法】排序算法总结,手写快排,归并,堆排序算法
算法复杂度
相关概念:
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数
手写快排:
先选择第一个数字作为标尺,然后分别从第二个数字往右找,找到比第一个数大的数,和从倒数第一个数字往左找,找到比第一个数小的数,然后将找到的两个数进行交换,一直下去。
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
public class quickSort { private static int[] quickSort(int[] arr, int left, int right) { if (left < right) { int p = patition(arr, left, right);//基数 quickSort(arr, left, p - 1); quickSort(arr, p + 1, right); } return arr; } private static int patition(int[] arr, int left, int right) {// 分区操作 int temp = left; while (left < right) { //从右边开始找到比主键值a[0]小的值,移到左边 while (left < right && arr[right] >= arr[temp]) right--; while (left < right && arr[left] <= arr[temp]) //从左边开始找到比主键值a[0]大的值,移到右边 left++; swap(arr,left,right); } //最终将基准数归位 swap(arr, temp, right); return right; } private static void swap(int[] arr, int left, int right) { int temp=arr[left]; arr[left]=arr[right]; arr[right]=temp; } }
手写归并:
该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
- 把长度为n的输入序列分成两个长度为n/2的子序列;
- 对这两个子序列分别采用归并排序;
- 将两个排序好的子序列合并成一个最终的排序序列。
public class MergeSort { public static void main(String[] args) { int[] arr = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; int[] temp = new int[arr.length]; sort(arr, 0, arr.length - 1, temp); System.out.println(Arrays.toString(arr)); } private static void sort(int[] arr, int left, int right, int[] temp) { if (left < right) { int mid = (left + right) / 2; sort(arr, left, mid, temp); sort(arr, mid + 1, right, temp); MergeSort(arr, left, mid, right, temp); } } private static void MergeSort(int[] arr, int left, int mid, int right, int[] temp) { int i = left; int j = mid + 1; int t = 0; while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[t++] = arr[i++]; } else { temp[t++] = arr[j++]; } } while (i <= mid) {// 将左边剩余的元素填充进temp temp[t++] = arr[i++]; } while (j <= right) { temp[t++] = arr[j++]; } t = 0; while (left <= right) { arr[left++] = temp[t++]; } } }
手写堆排序:
堆排序
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。
堆
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
public class HeapSort { private static void sort(int[] arr) { // 1、构建大顶堆 for(int i=arr.length/2-1;i>=0;i--){ //从第一个非叶子节点从下至上,从右至左调整结构 adjustHeap(arr,i,arr.length); } //2、调整堆结构+交换堆顶元素与末尾元素 for(int j=arr.length-1;j>0;j--){ swap(arr,0,j);//将堆顶元素与末尾元素进行交换 adjustHeap(arr, 0, j);//重新对堆进行调整 } } private static void swap(int[] arr, int a, int b) { int temp=arr[a]; arr[a] = arr[b]; arr[b] = temp; } private static void adjustHeap(int[] arr, int i, int length) { int temp=arr[i]; for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始 if(k+1<length&&arr[k]<arr[k+1]){ k++; } if(arr[k]>temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换) arr[i]=arr[k]; i=k; }else{ break; } } arr[i]=temp;//将temp值放到最终的位置 } }
参考博文:
十大经典排序算法(动图演示)https://www.cnblogs.com/onepixel/articles/7674659.html