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

排序(java)

一.概念

排序:对一组数据进行从小到大/从大到小的排序

稳定性:即使进行排序相对位置也不受影响如:

如果再排序后 L  在  i   的前面则稳定性差,像图中这样就是稳定性好。 

二.常见的排序

 三.常见算法的实现

1.插入排序

1.1 直接插入排序

时间复杂度

 最好情况:数据完全有序的时候O(N)

最坏的情况:数据完全逆序的时候O(N^2)

当前数据越有序越快

空间复杂度:O(1)

稳定性:稳定

 public static void insertSort(int[] array) {for (int i = 1; i < array.length; i++) {int tmp = array[i];int j = i - 1;for (; j >= 0; j--) {if (array[j] > tmp) {array[j + 1] = array[j];} else {break;}}array[j + 1] = tmp;}}

该排序就是从第二个数据开始进行比较,以第4个数据进行举例,假设第三个数据大于第四个数据则第一个数据的值就改为第三个数据,第二个数据不大于第四个数据循环结束,当前第二个数据后面的数据就是当时第四个数据的值。

1.2 希尔排序

public static void shellSort(int[] array) {int gap = array.length;while (gap > 1) {gap /= 2;shell(array, gap);}}private static void shell(int[] array, int gap) {for (int i = gap; i < array.length; i += gap) {int tmp = array[i];int j = i - gap;for (; j >= 0; j -= gap) {if (array[j] > tmp) {array[j + gap] = array[j];} else {break;}}array[j + gap] = tmp;}}

                                                                                                           c                      ,

 通过对数据分成多个组,随着组数越来越多,组的大小越来越小,数据也越归于有序,通常gap大小都是gap/=2,这种方法是对于插入算法的优化,但是因为gap不固定所以时间复杂度不固定。

稳定性:不稳定

2.选择排序

基本思想:每次都从元素中找一个最大/最小的值,放在初始/末尾位置。

2.1直接选择排序

方法一:

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

方法一就是找到一个最小的值放在初始位置。 

方法二:

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

方法二就是找到最大值和最小值,然后进行交换,需要注意的是我们在进行最小值交换的时候。如果与最小值交换的元素刚好就是最大值,我们就需要进行判断一下。 然后将最大值的下标更新一下。

1.时间复杂度: O(N^2)

2.空间复杂度:O(1)

3.稳定性:不稳定

2.2堆排序

在使用堆时,我们已经知道可以根据创建一个大根堆来实现对于元素的排序,这种方法是因为大根堆的根结点是最大的,所以我们可以将它和最后一个交换位置,然后对排序元素个数减一后进行向下排序,此时可以确定的是最后一个元素是最大的。

 public static void heapSort(int[] array){creatBigHeap(array);int end = array.length - 1;while(end > 0){swap(array,0 ,end);end--;siftDown(0,array,end);}}public static void creatBigHeap(int[] arrray){for (int parent = (arrray.length - 1 - 1)/2; parent >= 0 ; parent--) {siftDown(parent, arrray,arrray.length);}}private static void siftDown(int parent,int[] array,int end) {int child = 2*parent + 1;while(child < end){if(child + 1 < end && array[child] < array[child+1]){child++;}if(array[child] > array[parent]){swap(array,child,parent);parent = child;child = parent*2 + 1;}else{break;}}}

时间复杂度:O(n*logN)

空间复杂度:O(1)

稳定性:不稳定的

3.交换排序

概念:通过比较大小来交换元素,元素大的向后走,元素小的向前走。

3.1冒泡排序

冒泡排序就是通过遍历来进行前后比较,来进行排序,通过这种方法,数组的末尾总是当前的最大值,所以我们在进行遍历比较是时,应该减去我们我们进行整体排序的次数,之所以给外循环的上限减去一,是因为我们在对n个数据进行在整体排序时,我们只需要排序n-1次就好,因为最后一个数据应该也是有序,没必要再进行排序。

时间复杂度:O(N^2)

空间复杂度: O(1)

稳定性:稳定

public static void popSort(int[] array){boolean flag = false;for (int i = 0; i < array.length - 1; i++) {for (int j = 0; j < array.length - 1 -i ; j++) {if (array[j] > array[j+1]) {swap(array, j, j + 1);flag = true;}}if (flag == false){break;}}}

3.2快速排序

基本思想就是取一个基准值,将当前数据分为大于基准值和小于基准值两个部分,然后再对这两个部分进行同样的操作,直到所有的元素都有序为止。

最好情况:O(N*logN)       满二叉树/完全二叉树

最坏情况:O(N^2)       单分支的树

空间复杂度:

最好情况:O(logN)       满二叉树/完全二叉树

最坏情况:O(N)            单分支的树

稳定性:不稳定              

3.2.1递归版
 private static void quick(int[] array,int start,int end) {if(start >= end){return;}int pivot = partition3(array,start,end);quick(array,start,pivot - 1);quick(array,pivot+1,end);}
3.2.1.1 Hoare版
  private static int parttionHoare(int[] array,int left,int right){int key = array[left];int i = left;while(left < right){while(left < right && array[right] >= key){right--;}while(left < right && array[left] <= key){left++;}swap(array,left,right);}swap(array,left,i);return left;}

Hoare版的快速排序就是从两边进行调整,左边一碰到大于基准的值就停下来,右边碰到小于基准的值就停下来,然后进行交换,就这样进行下去知道left >= right,最后我们left所停留的位置就是基准应该再的位置,但是基准开始被设为就是left,所以我们提前存的left就有了价值了,让他俩交换,就完成了一次快排。

3.2.1.2挖坑法
 private static int parttion2(int[] array,int left,int right){int key = array[left];while(left < right){while(left < right && array[right] >= key){right--;}array[left] = array[right];while(left < right && array[left] <= key){left++;}array[right] = array[left];}array[left] = key;return left;}

挖坑法就是先挖个坑让右边小于基准的值给填进去,这样右边就也有个坑了,然后再在左边找,找到一个大于基准的数,填满右边的坑,之后肯定有一个坑是空的,这就是我们要找的基准位置。 

3.2.1.3 双指针法
 private static int partition3(int[] array, int left, int right) {int prev = left;int pcur = left + 1;while(pcur <= right){if (array[left] > array[pcur] && array[++prev] != array[pcur]){swap(array,prev,pcur);}pcur++;}swap(array,left,prev);return prev;}

我们会以左边第一个元素为基准,如果碰到比基准小的值,我们会用用pcur来进行记录,而prev移动的条件就是当pcur遇到小的值时,而prev的下一个元素也必然时大于基准的,因为当前这个元素不大于基准pcur不能走。我们来画图举例:

3.2.2 非递归
public static void quickSortNor(int[] array){Stack<Integer> stack = new Stack<>();int left = 0;int right = array.length - 1;int pivot  = parttionHoare(array,left,right);if(pivot - 1 > left) {stack.push(left);stack.push(pivot - 1);}if(pivot + 1 < right) {stack.push(pivot+1);stack.push(right);}while(!stack.isEmpty()){right = stack.pop();left = stack.pop();pivot = parttionHoare(array,left,right);if(pivot - 1 > left){stack.push(left);stack.push(pivot - 1);}if(pivot + 1 < right){stack.push(pivot + 1);stack.push(right);}}}

非递归使用了栈,首先将元素分为大于基准和小于基准两组,然后重复进行操作,通过HOare操作来对数组进行排序。 

 快速排序优化
 private static void insertSortRange(int[] array,int begain,int end) {for (int i = begain + 1; i <= end; i++) {int tmp = array[i];int j = i - 1;for (; j >= begain; j--) {if (array[j] > tmp) {array[j + 1] = array[j];} else {break;}}array[j + 1] = tmp;}}private static void quick(int[] array,int start,int end) {if(start >= end){return;}if (end - start + 1 <= 15){//插入排序insertSortRange(array,start,end);return;}//三数取中int index =  midOfThree(array,start,end);swap(array,index,start);int pivot = parttionHoare(array,start,end);quick(array,start,pivot - 1);quick(array,pivot+1,end);}

当我们遇到小的区间可以用插入排序来解决,在选择基准时我们可以尽量选择取中间值来进行排序,所以我们又有了一个方法就是三数取中。

4.归并排序

基本思想:将一组数据分成两组,然后对分开的组在进行分组,直到分为一个组,然后进行合并,在合并过程中进行排序,开始时一 一排序,然后是二二,知道全部组都合并为一个组。

归并排序总结

1.归并排序缺点在于时间复杂度过高,因为占用外部空间来排序很多。 

2.时间复杂度:O(N*logN)

3.空间复杂度:O(N)

4.稳定性:稳定

代码实现

 public static void mergeSort(int[] array){mergeSortFunc(array,0,array.length - 1);}private static void mergeSortFunc(int[] array, int left, int right) {if(left >= right){return ;}int mid = (left + right) / 2;mergeSortFunc(array,left,mid);mergeSortFunc(array,mid + 1,right);merge(array,left,right,mid);}private static void merge(int[] array, int left, int right, int mid) {int s1 = left;int s2 = mid + 1;int[] tmpArr = new int[right - left + 1];int k = 0;while(s1 <= mid && s2 <= right){if (array[s2] <= array[s1]){tmpArr[k++] = array[s2++];}else{tmpArr[k++] = array[s1++];}}while (s1 <= mid){tmpArr[k++] = array[s1++];}while(s2 <= right){tmpArr[k++] = array[s2++];}for (int i = 0; i < tmpArr.length; i++) {array[i + left ] = tmpArr[i];}}

非递归实现

 public static void mergeSortNor(int[] array){int gap = 1;while(gap < array.length ){for (int i = 0; i < array.length; i += 2*gap ) {int left = i;int mid = left + gap - 1;int right = mid + gap;if (mid >= array.length){mid = array.length - 1;}if (right >= array.length){right = array.length - 1;}merge(array,left,right,mid);}gap*=2;}}

在这七大排序中稳定的只有归并排序,冒泡排序,插入排序,其中归并排序,快速排序,插入排序应当作为重点。

其他排序

计数排序

 public static void countSort(int[] array){int minVal = array[0];int maxVal = array[0];for (int i = 1; i < array.length; i++) {if (array[i] < minVal){minVal = array[i];}if (array[i] > maxVal){maxVal = array[i];}}int[] count = new int[maxVal - minVal + 1];for (int i = 0; i < array.length; i++) {count[array[i] - minVal]++;}int index = 0;for (int i = 0; i < count.length; i++) {while(count[i] > 0){array[index] = i+minVal;index++;count[i]--;}}}

通过对相同元素出现的次数来进行排序。

相关文章:

  • 任务的状态
  • 投资理财_从0到1:如何用1000元开启你的二级市场投资之旅?
  • 实战5:Python使用循环神经网络生成诗歌
  • 解决virtualbox7.1无法启动3d加速的问题
  • 大数据人工智能
  • 算法的时间复杂度
  • L37.【LeetCode题解】三数之和(双指针思想)
  • Java练习——day2(集合嵌套)
  • Nginx:轻量级高性能的Web服务器与反向代理服务器
  • 开源推荐#6:可爱的临时邮箱服务
  • 模型提示词
  • Ubuntu源码制作openssh 9.9p2 deb二进制包修复安全漏洞 —— 筑梦之路
  • 基于.NET后端实现图片搜索图片库 核心是计算上传图片与库中图片的特征向量相似度并排序展示结果
  • [Jenkins]pnpm install ‘pnpm‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。
  • Harmonyos-Navigation路由跳转
  • ios app的ipa文件提交最简单的方法
  • 论文阅读笔记:Generative Modeling by Estimating Gradients of the Data Distribution
  • 云钥科技柔性上料振动蜘蛛手工作原理及应用范围详细介绍
  • 盈达科技GEO技术体系全景解密:AIM³ Pro × AICC × GEO-BENCH Pro构建全球认知堡垒​
  • 计算机网络 应用层
  • 房产经济人怎么做网站/长沙百度
  • 网站建设企划/新闻软文发布平台
  • 免费制作模板网站/好的seo公司营销网
  • 如何做网站营销/长沙 建站优化
  • 做网站和做网页有什么区别/太原网络推广公司哪家好
  • 户外做旅游网站/啦啦啦资源视频在线观看8