一次性搞懂排序算法,常用的排序算法总结

}

}

归并排序


归并排序核心是分治思想,先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了

若将两个有序表合并成一个有序表,称为二路归并

归并排序是稳定的排序算法

在这里插入图片描述

实现

public class MergeSort {

public static void main(String[] args) {

int data[] = { 9, 5, 6, 8, 0, 3, 7, 1 };

megerSort(data, 0, data.length - 1);

System.out.println(Arrays.toString(data));

}

public static void mergeSort(int data[], int left, int right) { // 数组的两端

if (left < right) { // 相等了就表示只有一个数了 不用再拆了

int mid = (left + right) / 2;

mergeSort(data, left, mid);

mergeSort(data, mid + 1, right);

// 分完了 接下来就要进行合并,也就是我们递归里面归的过程

merge(data, left, mid, right);

}

}

public static void merge(int data[], int left, int mid, int right) {

int temp[] = new int[data.length]; //借助一个临时数组用来保存合并的数据

int point1 = left; //表示的是左边的第一个数的位置

int point2 = mid + 1; //表示的是右边的第一个数的位置

int loc = left; //表示的是我们当前已经到了哪个位置了

while(point1 <= mid && point2 <= right){

if(data[point1] < data[point2]){

temp[loc] = data[point1];

point1 ++ ;

loc ++ ;

}else{

temp[loc] = data[point2];

point2 ++;

loc ++ ;

}

}

while(point1 <= mid){

temp[loc ++] = data[point1 ++];

}

while(point2 <= right){

temp[loc ++] = data[point2 ++];

}

for(int i = left ; i <= right ; i++){

data[i] = temp[i];

}

}

}

希尔排序


基本思想:

算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d,对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成

希尔排序法(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序的方法

假如:数组的长度为10,数组元素为:25、19、6、58、34、10、7、98、160、0

整个希尔排序的算法过程如下如所示:

实现

public static int[] ShellSort(int[] array) {

int len = array.length;

int temp, gap = len / 2;

while (gap > 0) {

for (int i = gap; i < len; i++) {

temp = array[i];

int preIndex = i - gap;

while (preIndex >= 0 && array[preIndex] > temp) {

array[preIndex + gap] = array[preIndex];

preIndex -= gap;

}

array[preIndex + gap] = temp;

}

gap /= 2;

}

return array;

}

快速排序


快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序

  • 先从数列中取出一个数作为基准数。

  • 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。

  • 再对左右区间重复第二步,直到各区间只有一个数。

在这里插入图片描述

实现1

public class QuickSort {

public static void quickSort(int[]arr,int low,int high){

if (low < high) {

int middle = getMiddle(arr, low, high);

quickSort(arr, low, middle - 1);//递归左边

quickSort(arr, middle + 1, high);//递归右边

}

}

public static int getMiddle(int[] list, int low, int high) {

int tmp = list[low];

while (low < high) {

while (low < high && list[high] >= tmp) {//大于关键字的在右边

high–;

}

list[low] = list[high];//小于关键字则交换至左边

while (low < high && list[low] <= tmp) {//小于关键字的在左边

low++;

}

list[high] = list[low];//大于关键字则交换至左边

}

list[low] = tmp;

return low;

}

}

实现2

public class QuickSort {

public static void quickSort(int data[], int left, int right) {

int base = data[left]; // 基准数,取序列的第一个

int ll = left; // 表示的是从左边找的位置

int rr = right; // 表示从右边开始找的位置

while (ll < rr) {

// 从后面往前找比基准数小的数

while (ll < rr && data[rr] >= base) {

rr–;

}

if (ll < rr) { // 表示是找到有比之大的

int temp = data[rr];

data[rr] = data[ll];

data[ll] = temp;

ll++;

}

while (ll < rr && data[ll] <= base) {

ll++;

}

if (ll < rr) {

int temp = data[rr];

data[rr] = data[ll];

data[ll] = temp;

rr–;

}

}

// 肯定是递归 分成了三部分,左右继续快排,注意要加条件不然递归就栈溢出了

if (left < ll)

quickSort(data, left, ll - 1);

if (ll < right)

quickSort(data, ll + 1, right);

}

}

优化

基本的快速排序选取第一个或最后一个元素作为基准。但是,这是一直很不好的处理方法

如果数组已经有序时,此时的分割就是一个非常不好的分割。因为每次划分只能使待排序序列减一,此时为最坏情况,快速排序沦为冒泡排序,时间复杂度为O(n^2)

三数取中

一般的做法是使用左端、右端和中心位置上的三个元素的中值作为枢纽元

举例:待排序序列为:8 1 4 9 6 3 5 2 7 0

左边为:8,右边为0,中间为6

我们这里取三个数排序后,中间那个数作为枢轴,则枢轴为6

插入排序

当待排序序列的长度分割到一定大小后,使用插入排序 原因:对于很小和部分有序的数组,快排不如插排好。当待排序序列的长度分割到一定大小后,继续分割的效率比插入排序要差,此时可以使用插排而不是快排

重复数组

在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割

在一次划分后,把与key相等的元素聚在一起,能减少迭代次数,效率会提高不少

具体过程:在处理过程中,会有两个步骤

第一步,在划分过程中,把与key相等元素放入数组的两端

第二步,划分结束后,把与key相等的元素移到枢轴周围

举例:

待排序序列 1 4 6 7 6 6 7 6 8 6

三数取中选取枢轴:下标为4的数6

转换后,待分割序列:6 4 6 7 1 6 7 6 8 6

枢轴key:6

第一步,在划分过程中,把与key相等元素放入数组的两端

结果为:6 4 1 6(枢轴) 7 8 7 6 6 6

此时,与6相等的元素全放入在两端了

第二步,划分结束后,把与key相等的元素移到枢轴周围

结果为:1 4 6 6(枢轴) 6 6 6 7 8 7

此时,与6相等的元素全移到枢轴周围了

之后,在1 4 和 7 8 7两个子序列进行快排

堆排序


堆是一种特殊的树。只要满足这两点,它就是一个堆。

  • 堆是一个完全二叉树;

  • 堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值。

对于每个节点的值都大于等于子树中每个节点值的堆,我们叫做“大顶堆”。

对于每个节点的值都小于等于子树中每个节点值的堆,我们叫做“小顶堆”。

在这里插入图片描述

一般升序用大根堆,降序就用小根堆

如何实现一个堆

完全二叉树比较适合用数组来存储。用数组来存储完全二叉树是非常节省存储空间的。因为我们不需要存储左右子节点的指针,单纯地通过数组的下标,就可以找到一个节点的左右子节点和父节点。

在这里插入图片描述

比如查找数组arr中某个数的父结点和左右孩子结点,比如已知索引为i的数,那么

  1. 父结点索引:(i-1)/2(这里计算机中的除以2,省略掉小数)

  2. 左孩子索引:2*i+1

  3. 右孩子索引:2*i+2

所以堆的定义性质:

  • 大根堆:arr(i)>arr(2*i+1) && arr(i)>arr(2*i+2)

  • 小根堆:arr(i)<arr(2*i+1) && arr(i)<arr(2*i+2)

堆排序基本步骤

1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

最后

由于细节内容实在太多了,为了不影响文章的观赏性,只截出了一部分知识点大致的介绍一下,每个小节点里面都有更细化的内容!

小编准备了一份Java进阶学习路线图(Xmind)以及来年金三银四必备的一份《Java面试必备指南》

新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-WiYZuzMB-1710405549170)]
[外链图片转存中…(img-xohCyWob-1710405549171)]
[外链图片转存中…(img-k7oAys6W-1710405549171)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-Kyp998EW-1710405549172)]

最后

由于细节内容实在太多了,为了不影响文章的观赏性,只截出了一部分知识点大致的介绍一下,每个小节点里面都有更细化的内容!

[外链图片转存中…(img-6igzCnWn-1710405549172)]

小编准备了一份Java进阶学习路线图(Xmind)以及来年金三银四必备的一份《Java面试必备指南》

[外链图片转存中…(img-gOsoyJzR-1710405549173)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值