最全【Java数据结构与算法】Day2-高级排序(希尔、归并、快速,2024年最新手把手教你写Golang项目文档

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
插入排序O(n²)O(n²)O(n)O(1)稳定
希尔排序O(n1.5)O(n²)O(n)O(1)不稳定
选择排序O(n²)O(n²)O(n²)O(1)不稳定
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(1)不稳定
冒泡排序O(n²)O(n²)O(n)O(1)稳定
快速排序O(nlog2n)O(n²)O(nlog2n)O(nlog2n)不稳定
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(n)稳定

高级排序

1.希尔排序

描述:

第一批突破O(n2)时间复杂度的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,他会优先比较距离较远的元素。希尔排序又叫 缩小增量排序

算法的核心思想是先将整个待排序的记录序列分割成为若干子序列分别进行直接 插入排序,具体算法描述:

  • 先根据数组长度/n,获取增量K(第一次n为2)
  • 按增量序列个数K进行分组,一般可以分为K组;
  • 根据已分好的组进行插入排序;(每组排序,根据对应的增量k来找到当前组的元素)
  • 当每组都排序完成之后,回到第一步将n*2再次分组进行插入排序,直到最终k=1的时候,再执行一次 插入排序 完成最终的排序

实现:

分解1: 先将数组根据长度分为length/2组(增量),并且将第一组的元素进行插入排序

可以先实现获取第一组的元素:

第一轮:增量10/2=5组

public static void main(String[] args) {
        int[] arrs = new int[]{8,6,1,7,2,5,4,12,9,3};
        //分组
        int group = arrs.length / 2;
        System.out.println("输出第一组的元素");
        for (int j = 0; (j + group) < arrs.length; j += group){
            int insert = j + group;
            while ((insert - group) >= 0){
                if(arrs[insert - group] > arrs[insert]){
                    //交换
                    int temp = arrs[insert - group];
                    arrs[insert - group] = arrs[insert];
                    arrs[insert] = temp;
                    //插入的指针向前移动
                    insert = insert - group;
                }else {
                    break;
                }

            }
        }
        for (int i = 0; i < arrs.length; i++){
            System.out.print(arrs[i] + ",");
        }
    }

1

分解2: 将第一轮分好的所有的组都进行插入排序。

第二轮:增量5/2=2组

外面嵌套一个小于group的循环

for (int i = 0; i < group; i++) {
            for (int j = i; (j + group) < arrs.length; j += group){
                int insert = j + group;
                while ((insert - group) >= 0){
                    if(arrs[insert - group] > arrs[insert]){
                        //交换
                        int temp = arrs[insert - group];
                        arrs[insert - group] = arrs[insert];
                        arrs[insert] = temp;
                        //插入的指针向前移动
                        insert = insert - group;
                    }else {
                        break;
                    }

                }
            }
        }

分解3: 执行完第一轮之后,再继续分组执行分解1和分解2的操作,最终到无法分组,排序完成

public static void main(String[] args) {
        int[] arrs = new int[]{8,6,1,7,2,5,4,12,9,3};
        //分组
        int group = arrs.length;
        //等于1时说明无法再分组了
        while (group != 1){
            //分组
            group = group / 2;
            for (int i = 0; i < group; i++) {
                for (int j = i; (j + group) < arrs.length; j += group){
                    int insert = j + group;
                    while ((insert - group) >= 0){
                        if(arrs[insert - group] > arrs[insert]){
                            //交换
                            int temp = arrs[insert - group];
                            arrs[insert - group] = arrs[insert];
                            arrs[insert] = temp;
                            //插入的指针向前移动
                            insert = insert - group;
                        }else {
                            break;
                        }

                    }
                }
            }
        }



            /\*int j = i;
 while ((j + group) < arrs.length){
 int insert = j + group;
 while((insert - group) >= 0){
 if(arrs[insert - group] > arrs[insert]){
 //进行交换
 int temp = arrs[insert - group];
 arrs[insert - group] = arrs[insert];
 arrs[insert] = temp;
 //插入的指针向前移动
 insert = insert - group;
 }else {
 break;
 }
 }
 //j进行增量
 j += group;
 }\*/




        for (int i = 0; i < arrs.length; i++){
            System.out.print(arrs[i] + ",");
        }
    }

在这里插入图片描述

2.归并排序

描述:

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略即将问题成一些小的问题然后递归求解,而的阶段则将分的阶段得到的各答案 “修补” 在一起,即分而治之 。

归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。java中Arrays.sort()采用了 名为TimSort的排序算法,就是归并排序的优化版本。每次合并操作的平均时月复杂度为O(n),而完全二叉树的深度为|1og2n|。总的平均时间复杂度为O(nlogn)。而旦,归并排序的最好,最坏,平均时间复杂度均为o(nlogn)。

归井排序核心思想是先分再治,具体算法描述如下:

先将未排序数组/2进行分组,然后再将分好组的数组继续/2再次分组,直到无法分组,这个就是分的过程。

然后再将之后把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的,同时在合并的过程中完成数组的排列,最终直到全部小的数组合并起来,这个就是治的过程。

在这里插入图片描述

实现

分解1: 先实现分的思想,将数组分解进行实现

先获取数组的中轴,然后以中轴将数组分为两个部分

使用递归分别执行左右两部分分解

public static void main(String[] args) {
        int[] arrs = {8,6,3,7,2,5,4,1};
        mergeSort(arrs,0,arrs.length-1);
    }

    //实现归并排序
    public static void mergeSort(int[] arrs, int first, int last){
        //实现递归推出的条件
        if(first >= last){
            return;
        }
        //求出当前数组的中间值
        int mid = (first + last)/2;

        //将左边的数组继续分解
        mergeSort(arrs,first,mid);
        //将右边的数组继续分解
        mergeSort(arrs,mid + 1, last);

        //输出分组情况
        //先输出左边的数组
        for (int i = first; i <= mid; i++){
            System.out.print(arrs[i] + " ");
        }
        System.out.print("--------");
        //输出右边的数组
        for (int i = mid + 1; i <= last; i++) {
            System.out.print(arrs[i] + " ");
        }

        System.out.println(" ");

    }

分解2: 实现具体治的过程,将左右两个数组合并到一个临时数组中

分别设计两指针i和j,遍历左右两个数组,取出元素进行比较,将小的元素放入到临时数组中。

然后将左右剩下的元素放入到数组中

将排序好的临时数组中的元素返回到未排序的数组中

public static void main(String[] args) {
        int[] arrs = {8,6,3,7,2,5,4,1};
        mergeSort(arrs,0,arrs.length-1);
        //输出排序结果
        for (int i = 0; i < arrs.length; i++) {
            System.out.print(arrs[i] + " ");
        }
    }

    //实现归并排序
    public static void mergeSort(int[] arrs, int first, int last) {
        //实现递归推出的条件
        if (first >= last) {
            return;
        }
        //求出当前数组的中间值
        int mid = (first + last)>>>1;

        //将左边的数组继续分解
        mergeSort(arrs, first, mid);
        //将右边的数组继续分解
        mergeSort(arrs, mid + 1, last);

        //治,实现插入并完成排序
        int[] temp = new int[last + 1];
        //定义两个指针
        int i = first;//左边数组的遍历指针
        int j = mid + 1;//右边数组的遍历指针
        int t = 0;//临时数组的指针
        //遍历左右两个数组,将较小的元素插入到临时数组中
        while (i <= mid && j <= last) {
            if (arrs[i] <= arrs[j]) {
                //将左边的指针指向的元素插入到临时数组中
                temp[t++] = arrs[i++];
            } else {
                //将右边的指针指向的元素插入到临时数组中
                temp[t++] = arrs[j++];
            }
        }
        //再将左右剩余的元素插入到临时数组中
        while (i<=mid){
            temp[t++] = arrs[i++];
        }
        while (j<=last){
            temp[t++] = arrs[j++];
        }

        //还需要将临时数组中的元素复制到原始数组中
        //先将t重置
        t = 0;
        //将t指向的值,复制到first指向的原始数组的位置
        while (first <= last){
            arrs[first++] = temp[t++];
        }
    }

3.快速排序

描述:

快速排序是对冒泡排序的一种改进,通过分而治之的思想减少排序中交换和遍历的次数,整个过程可以通过递归的方式完成。

具体描述如下:

1.首先通过比较算法,找到基准数,比较过程通过交换最终达到基准数左边的数字都比右边的小。

2.然后以基准数作为中轴,将数组分为两部分,分别执行步骤1的算法(可以通过递归实现),直到无法再次分割排序完毕

递归

一个含直接或间接调用本函数语句的函数被称之为递归函数,他必须满足以下两个条件:

1)在每一次调用自己时,必须是(在某种意义上)更接近于解;

2)必须有一个终止处理或计算的准则;

基本格式

void func()
{
	//递归条件
	if(condition)
		func();
	else
		//退出递归
}

实现

分解1: 创建左右两个指针,将最后一个值作为基准值,通过不断交换将数组分为两部分,左边的比右边的要小。

  • 先判断左指针和基准的值,如果小于等于就向后移动,直到遇到比基准值大的值
  • 再判断右边指针和基准值,如果大于等于就向前移动,直到遇到比基准值小的值
  • 然后交换左右指针的值
  • 循环上述操作,直到左右指针重合,然后交换重合值和基准值
public static void main(String[] args) {
        int[] arrs = {3,9,8,7,2,5,4,1,6};
        //执行快速排序
        quickSort(arrs);

        for (int i = 0; i < arrs.length; i++) {
            System.out.print(arrs[i] + " ");
        }
    }

    //定义快速排序方法
    public static void quickSort(int[] arrs){
        //定义左指针
        int left = 0;
        //定义右指针
        int right = arrs.length - 1;
        //定义基准值
        int pos = arrs.length - 1;
        while (left != right){
            //判断左指针是否比基准值大,如果大就停止移动
            while(arrs[left] <= arrs[pos]){
                left++;
            }

            if(left == right)
                break;

            //判断右指针是否比基准值小,如果小就停止移动
            while (arrs[right] >= arrs[pos]){
                right--;
            }
            if(left < right){
                //交换左右指针指向的值
                int temp = arrs[left];
                arrs[left] = arrs[right];
                arrs[right] = temp;
            }
        }
        //当left和right重合之后,需要将重合的值和基准值进行交换
        int temp = arrs[left];
        arrs[left] = arrs[pos];
        arrs[pos] = arrs[left];
    }

在这里插入图片描述

分解2: 将以left和right的重复位置作为中轴,将数组分为两部分,左右分别执行分解1的操作,直到排序完成

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

mg-blog.csdnimg.cn/32ddecf7f7bb4c00a92145882d820216.png)

分解2: 将以left和right的重复位置作为中轴,将数组分为两部分,左右分别执行分解1的操作,直到排序完成

[外链图片转存中…(img-jqbDbhZV-1715561228035)]
[外链图片转存中…(img-Px9ASwtu-1715561228035)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值