当前位置: 首页 > news >正文

--- 常见排序算法汇总 ---

在这个汇总中整理了 插入,希尔,选择,快排,堆排,冒泡,归并

还有非比较排序 计数,基数排序

算法的稳定性:再对相同值的元素排序之后,如果元素之间的顺序并没有发生变化,则是稳定的

对一个数组向前表示下标减减 向后加加

插入排序

性质:稳定   时间复杂度 O(N^2)

原理

选择一个元素 v 为基准,假定 v 前面的元素时有序的,那么就可以把 v 和 v 下标 -1 的 p 元素向前进行对比,如果比 v 大那么就覆盖 v 的位置,继续往前比较,当p 小于或者等于 v 时那么就可以退出这一次循环了,因为p 下标 +1 就对应了v应该插入的位置,于是把这个下标值改为 v ,这一次循环就对 v 进行了一次排序 再对整个数组元素进行排序,就完成了最终的排序

    public static void InsertSort(int[] array) {//默认j前面的数是有序的for (int j = 1; j < array.length; j++) {int tmp = array[j];int i = j - 1;for (; i >= 0; i--) {if (array[i] > tmp) {array[i + 1] = array[i];} else {// array[i + 1] = tmp;break;}}array[i + 1] = tmp;}}

希尔排序

不稳定  O(N^1.3)~O(N^1.5)

和插入排序的排序逻辑是相同的,但是他引入增量gap的概念,通过将数组分为gap个组,在对这些组进行插入排序,这样元素就不是一位一位的移动了,能使较大的元素快速的移动到后面,再不断的缩小gap的值,最终在gap等于1时就和插入排序一样了,但是这时的数组已经趋于有序了

    public static void shellSort(int[] array) {int gap = array.length;//增量while (gap > 1) {gap /= 2;InsertSortByShell(array, gap);}}private static void InsertSortByShell(int[] array, int gap) {//默认j前面的数是有序的for (int j = gap; j < array.length; j++) {int tmp = array[j];int i = j - gap;for (; i >= 0; i -= gap) {if (array[i] > tmp) {array[i + gap] = array[i];} else {break;}}array[i + gap] = tmp;}}

选择排序

不稳定   O(N^2)

通过遍历数组来找到最小的元素,在把他和第一位的下标交换,这样一次循环就对一个元素完成了排序,在对整个数组进行排序,最终使得数组有序

优化  可以在遍历时找到最大的元素,这样一次遍历能完成俩个元素的排序

    public static void selectSort(int[] array) {for (int i = 0; i < array.length - 1; i++) {int minIndex = i;for (int j = i; j <= array.length - 1; j++) {if (array[minIndex] > array[j]) {minIndex = j;}}swap(array, minIndex, i);}}    public static void selectSort_better(int[] array) {int i = 0;int k = array.length - 1;while(i <= k){int minIndex = i;int maxIndex = i;for (int j = i; j <= array.length - 1 - i; j++) {if (array[minIndex] > array[j]) {minIndex = j;}if (array[maxIndex] < array[j]) {minIndex = j;}}swap(array, minIndex, i);if (maxIndex == i) {maxIndex = minIndex;}swap(array, maxIndex, k);i++;k--;}}

快速排序

不稳定   O(Nlog(N)) 

快速排序是hoare提出的一种基于二叉树结构的排序算法,通过指定一个待排元素,以他为中心将数组分为俩部分,左边值全是小于该元素,右边值全是大于该元素,然后以这个元素最终的下标,继续向左右俩部分进行排序,最终走到左右部分长度为0 (left == right) 时完成排序

排序主逻辑

public static void quickSort(int[] array) {quickSortMain(array,0,array.length - 1);}private static void quickSortMain(int[] array, int left, int right) {if (right <= left) {return;}//优化// 1 三数取中 保证不会递归很深导致栈溢出
//        getMiddle(array,left,right);// 2 递归到小区间时使用插入排序if (right - left < 7) {InsertSort(array, left, right);return;}//以array[left]为基准,吧数组分开 放回中点的下标int middle = separate_hoare(array, left, right);//递归//左边quickSortMain(array, left, middle - 1);//右边quickSortMain(array, middle + 1, right);}

将数组分割成左右俩部分常见的方法有

hoare版

指定left下标为基准值,使用i j 代表左右下标,i j 分别表示的时 在小于 i 下标的所有元素都小于等于基准值 大于 j 下标的所有元素都大于等于基准值

 private static int separate_hoare(int[] array, int left, int right) {int pivot = array[left];int tmp = left;while (right != left) {//从右边找到比pivot小的数while (right > left && array[right] >= pivot) {right--;}//从左边找到比pivot大的数while (right > left && array[left] <= pivot) {left++;}swap(array, left, right);}swap(array, tmp, left);return left;}

挖坑法

将基准值储存起来,在找到合适的元素时,直接将这个值给覆盖到对应的位置,这样可以少去交换的逻辑

  //挖坑法 将pivot储存在tmp中,直接吧值覆盖在原数据上private static int separate_dig(int[] array, int left, int right) {int pivot = array[left];while (left != right) {//走右边while (left < right && array[right] >= pivot) {right--;}//直接填入array[left] = array[right];//左边while (left < right && array[left] <= pivot) {left++;}array[right] = array[left];}array[left] = pivot;return left;}

双指针法

使用双指针一次遍历完成数组的分割,让 i 来完后找比pivot小的值,然后d代表的时d前面的全是小于或等于piovt的值,在和d交换d++,那么d也就维护了一个(0,d-1)这个区间全是小于或等于pivot,最终和 left 一交换,就完成了数组的分割

 //双指针法private static int separate_index(int[] array, int left, int right) {int d = left + 1;int pivot = array[left];for (int i = left + 1; i <= right; i++) {if (array[i] < pivot) {swap(array, i, d);d++;}}swap(array, d - 1, left);return d - 1;}

优化 

三数取中防止递归太深

将左边中间右边做对比,取中间值来作为基准值

    private static void getMiddle(int[] array, int left, int right) {int middle = (left + right) / 2;if (array[left] < array[middle] && array[middle] < array[right]//取m|| array[right] < array[middle] && array[middle] < array[left]) {swap(array,left, middle);} else if (array[middle] < array[right] && array[middle] < array[left]//去right|| array[left] < array[middle] && array[right] < array[middle]) {swap(array, left, right);} else {return;}}

堆排序

时间复杂都 O(N*logN) 空间复杂度 O(1) 不稳定

堆排序基于大小根堆的性质,因为小根堆有堆顶元素时是数组中最小的元素的性质,那么就可以基于大根堆的shiftdwon来进行原地升序排序,shiftdwon向下调整使用从数组末尾开始对元素进行向下调整,这样这个开始位置最终就是相对的最大元素,等到走到数组头位置,那么就完成了最大元素的寻找

* shiftdwon实现,因为有左节点 child = parent * 2 + 1 右节点 child = parent * 2 + 2, 那么从开始位置记为根节点,与他的左右节点比较,最终和最大的节点交换,然后根节点走到这个子节点,并继续向下交换,当节点下标超过了数组长度,就完成了一次向下调整

  public static void heapSort(int[] array) {int useSize = array.length;//这个是下表的最后元素while (useSize > 0) {creatHeap(array,useSize);//创建大根堆,创建一次找到一个最大值swap(array, 0, useSize-1);//这个是最后元素的下表useSize--;}}public static void creatHeap(int[] array,int userSize) {for (int i = array.length - 1; i >= 0; i--) {//每个元素都要遍历shiftDown(array,i, userSize);}}private static void shiftDown(int[] array,int parent, int userSize) {int child = parent * 2 + 1;//这个是父节点的左节点while (child < userSize) {//和大的交换if ((child+1) < userSize && array[child] < array[child + 1]) {child++;//走到更大的右节点}if (array[child] > array[parent]) {//交换swap(array, child, parent);parent = child;child = child*2+1;//走到写一个左节点}else {//说明调整好了break;}}}

冒泡排序

O(n^2) 稳定

通过俩次嵌套循环来完成排序,里面的一次完成一个元素的排序,这样外层循环遍历一次数组就完成排序

    public static void bubbleSort(int[] array) {for (int i = 0; i < array.length; i++) {boolean flg = true;for (int j = i+1; j < array.length; j++) {if (array[j] < array[i]) {swap(array, j, i);flg = false;}}if (flg) {return;}}}

归并排序

O(Nlog(N)) O(N) 稳定

归并排序主逻辑分为递归和合并俩部分,递归把所有的元素按中间分为俩部分,一直分到数组的叶子节点,在向上回溯,并将左右俩个数组有序合并在一起,这里不用关心左右俩数组是否有序,是因为我时从俩个元素开始合并的,得到的也是有序数组,那么在向上到多个元素也会是有序的,左边俩个元素有序,右边俩个元素也肯定是有序的,最后把这个合并的有序的数组段给覆盖到原来数组的那一段,就完成这一段的排序了

   public static void mergeSort(int[] array) {mergeSortMain(array,0,array.length-1);}private static void mergeSortMain(int[] array, int left, int right) {if (left >= right) {return;}int middle = (right + left) / 2;//递归 吧数递归为单独的数mergeSortMain(array, left, middle);//左mergeSortMain(array, middle+1, right);//右//合并merge(array,middle, left, right);}//创建一个新的数组,再把新的数组的数拷贝到老的数组private static void merge(int[] array, int middle, int left, int right) {int[] newArray = new int[right - left + 1];int i = 0;//左边的数组int s1 = left;int e1 = middle;//右边的数组int s2 = middle + 1;int e2 = right;//排序while (s1 <= e1 && s2 <= e2) {if (array[s1] < array[s2]) {newArray[i++] = array[s1];s1++;} else {newArray[i++] = array[s2];s2++;}}while (s1 <= e1) {//s1没有拍完newArray[i++] = array[s1++];}while (s2 <= e2) {//s2没有拍完newArray[i++] = array[s2++];}//将数组拷贝到老数组i = 0;for (int j = left; j <= right; j++) {array[j] = newArray[i++];}}

计数排序

O(N + K) O(N + K) 不稳定

N是数组长度  K是数据大小范围

计数排序是非排序的算法,通过遍历一次数组并记下所有元素出现的频率,在从小到大把记下的元素给覆盖到原数组中,就完成了排序

public static void countSort(int[] array) {int min = array[0];int max = array[0];for (int i = 1; i < array.length; i++) {if (array[i] < min) {min = array[i];}if (array[i] > max) {max = array[i];}}countSortMain(array,min,max);}public static void countSortMain(int[] array, int min, int max) {int[] newArray = new int[max - min + 1];//遍历arrayfor (int j : array) {newArray[j - min]++;}//拷贝int i = 0;for (int j = 0 ; j < array.length;j++) {while (newArray[j] > 0) {
//                System.out.print((j + min) + " ");array[i++] = j + min;newArray[j]--;}}}

基数排序

O(N * K)   N 元素个数   K最大数的位数

基数排序也是非排序的算法,使用10个队列来储存数字的 0-9 位数,先获取到元素的个位数,并储存在对应的队列中,一次遍历数组,在队列从0-9把储存的元素覆盖给数组,就完成了个位的排序,在排十位数,百位数,直到最大位数所有位都每排过了

    public static void oddSort(int[] array) {//创建10个队列Queue<Integer>[] queues = new Queue[10];for (int i = 0; i < 10; i++) {queues[i] = new LinkedList<Integer>();}//找到最大的数int max = array[1];for (int i = 1; i < array.length; i++) {max = Math.max(max, array[i]);}int times = 0;//决定这次循环比较的是哪位数while (max != 0) {for (int i = 0; i < array.length; i++) {//遍历则这个数组int tmpVal = array[i];for (int j = 0; j < times; j++) {//决定比较哪位数tmpVal /= 10;}int index = tmpVal % 10;//根据该为数来选择插入哪个队列queues[index].add(array[i]);}//重新为这个数组排序int indexArr = 0;for (Queue<Integer> tmp : queues) {while (!tmp.isEmpty()) {array[indexArr++] = tmp.poll();}}times++;max /= 10;}}

http://www.dtcms.com/a/439397.html

相关文章:

  • 惠州网站开发公司网站建设与网页设计的区别
  • 打折网站建设教程下载做二手衣服的网站有哪些
  • 空间里怎么放多个网站wordpress 5.0.2编辑器
  • 国土局网站建设方案网络营销是网上销售吗
  • 微商怎么做网站西安公司注册核名
  • 做视频网站要什么格式网站建好后
  • 自己怎么做网站空间宁波外发加工网
  • Redis配置文件(redis.conf)
  • 网站做可信认证多少钱网站建设的意义是什么
  • 网站开发后台指什么商务网站建设定义
  • 合肥思讯网站建设广东建设项目备案公示网站
  • jquery win8风格企业网站模板宁波seo推广方式排名
  • [光学原理与应用-478]:国内研发用于无图形检测和有图形晶圆检测的皮秒紫外和深紫外激光光源的公司与关键技术
  • 动规:两个数组dp系列
  • 基本定时器的寄存器介绍及案例实践
  • pyhton基础【31】装饰器
  • 陕西网站建设价格网站死链
  • JDK21新特性全解析:重塑Java开发体验的五大突破
  • 合肥企业网站建设工access数据库网站
  • cuda版本和torch版本要对应才能使用
  • 如何解决顽固的盘符修改失败问题
  • Qwen3-Omni
  • 【C语言数据结构】第2章:线性表(1)--定义ADT
  • 做英文网站有用吗wordpress过滤html标签了
  • 瑞幸麦当劳代下单项目
  • 公司做网站找谁公司做网站找谁微信网站与响应式网站
  • ubuntu系统引导重置
  • 2017做电商做什么网站金华市建设局网站职称
  • 模糊视频图像如何处理?
  • 做外汇网站卖判刑多少年做图表的网站