排序简单篇——冒泡排序、选择排序、插入排序、希尔排序、快速排序全解析【附完整源码】

🌈hello,你好鸭,我是Ethan,西安电子科技大学大三在读,很高兴你能来阅读。

✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。
🏃人生之义,在于追求,不在成败,勤通大道。加油呀!

🔥个人主页:Ethan Yankang
🔥推荐:史上最强八股文||一分钟看完我的几百篇博客

🔥温馨提示:划到文末发现专栏彩蛋   点击这里直接传送

🔥本篇概览:详细讲解了排序简单篇——冒泡排序、选择排序、插入排序、希尔排序、快速排序全解析【附完整源码】。🌈⭕🔥


【计算机领域一切迷惑的源头都是基本概念的模糊,算法除外】


🌈序言:

排序算法千万不要爆冷!


🌈引出


🌈1.选择排序

 

🔥核心思想

设置数组开始的为最小值下标,在后面完全遍历寻找最小值for(int i=0;i<n-1;i++),与开始的值交换位置,每次遍历都会将当前剩下的元素的最小值放在数组最前方排好序,再一次遍历再从当前排好序的数组末端for(int j=i+1;j<n;j++)出发寻求最值就好。

🔥代码

public static void selectionSort(int[] nums){
        int len= nums.length;
    for (int i = 0; i < len-1; i++) {
        int minIndex=i;
        for (int j = i; j < len; j++) {
            if (nums[j]<nums[i]){
                minIndex=j;
            }
        }
        exch(nums,i,minIndex);
    }
}


🌈2.冒泡排序

🔥核心思想

沿着数组从头到尾挨个比较,大的就往后面交换,这样每一次交换都会在队尾得到当前最大元素。

所以有双重for,第一重遍历全部元素-1,第二重遍历已经全部元素-1-i(已经排序好了的i个就不需要排序了。)


🔥小细节

len-1    按理说是需要排序len-1次的。因为当len-1都拍好了,第一个一定是最小的。所以是len-1.

boolean  flag      这里使用flag来标记这样的事件。具体来说就是如果一开始或者中途就排序好了,就可以直接退出了,如果产生交换,说明还没排序好,如果没交换了,说明直接排序好了。直接退出。


🔥代码 

public class BubbleSort {
    public static int[] bubbleSort(int[]  nums) {
        int len=nums.length;
        boolean flag;
//        这里排序比长度减一,因为其他的所有排好了,剩下的一定排好了。
        for (int i = 0; i < len-1; i++) {
            flag=false;
            for (int j = 0; j < len - i - 1; j++) {
                if (nums[j]>nums[j+1]){
                    nums[j]^=nums[j+1];
                    nums[j+1]^=nums[j];
                    nums[j]^=nums[j+1];
                    flag= true;
                }
            }
            if (!flag){
                break;
            }
        }
        return nums;
    }
}

结果


🌈3.插入排序:

🔥核心思想:

抽离出当前比较元素设为key,在前面找到合适位置index并插入。如果前面元素比他小,就将元素向后移(反正我刚才抽离来了一个位置),直至最后插入合适位置。


🔥细节:

1.int key=nums[i];        抽离当前元素

2.nums[index+1]=nums[Index];        当前元素向后移
3.nums[Index+1]=key        插入当前元素

 public static void insertSort(int[]  nums) {
        int len= nums.length;
        for (int i = 1; i < len; i++) {
            int key= nums[i];
            int Index=i-1;
            while (Index>=0&&nums[Index]>key){
                nums[Index+1]=nums[Index];
                Index--;
            }
            nums[Index+1]=key;
        }
    }



 🌈4.希尔排序:

🔥核心思想:

是插入排序的升级版抽离出当前比较元素设为key,在前面找到合适位置index并插入。如果前面元素比他小,就将元素向后移(反正我刚才抽离来了一个位置),直至最后插入合适位置。

记住插入排序就记住了希尔排序


🔥细节:

1.for (int gap = n/2; gap >0; gap/=2) ;        每次细化当前插入的间隔

2. for (int i = gap; i < n; i++);       表示这是0之后的当前间隔的第一个元素

3. int key=nums[i];        抽离当前元素

4.nums[Index+gap]=nums[Index];​​​​​​​        将小于当前元素向后移动一个基本(gap)单元
5. nums[Index+gap]=key;        插入当前元素到正确的位置


   public static void shellSort(int[] nums) {
        int n=nums.length;
        for (int gap = n/2; gap >0; gap/=2) {
            for (int i = gap; i < n; i++) {
                int key=nums[i];
                int Index=i-gap;

    // 对每个子数组进行插入排序
                while (Index>=0&& nums[Index]>key){
                    nums[Index+gap]=nums[Index];
                    Index-=gap;
                }
                nums[Index+gap]=key;
            }

        }
    }

🌈5.快速排序:

🔥递归思想:

随便找一个基数(一般就拿第一个),将整个数组中,小于它的数放在左边,大于它的数放在右边,这样会分成两个区间。再分别对这两个区间进行处理,最后即可得到的是排好序的数组。


🔥细节:

1.exch(a, lo, j);        注意再返回基准元素的新位置时,要与交换j处的元素交换位置,因为这才是基准位置该处于的位置。他的右边的j+1元素一定比他大,左边的一定比它小(实际上j位置处就比他小了)。

2.return j;        最后一定是返回该基准元素的位置方便左右区间分别再次快速排序。

3.大师级别代码        

            while ((a[++i] < a[lo])) {// 找出大于基准元素a[lo]的元素a[++i];
                if (i == hi) break;
            }
            while (a[lo] > a[--j]) { // 找出小于基准元素a[lo]的元素a[--j];
                if (j == lo) break;
            }


 public static void qiuckSort(int[] a) {
        sort(a, 0, a.length - 1);
    }

    private static void sort(int[] a, int lo, int hi) {
        if (hi <= lo) return;
        int j = partition(a, lo, hi); // Partition the array
        sort(a, lo, j - 1);           // Sort the left part
        sort(a, j + 1, hi);           // Sort the right part
    }

    private static int partition(int[] a, int lo, int hi) {
        int i = lo, j = hi + 1;

        while (true) {
            
            while (a[++i] < a[lo]) {// 找出大于基准元素a[lo]的元素a[++i];
                if (i == hi) break;
            }
            while (a[lo] > a[--j]) { // 找出小于基准元素a[lo]的元素a[--j];
                if (j == lo) break;
            }

            if (i >= j) break; // 出现这种情况说明已经拍好序了,左边一定比他小,右边一定比他大。直接退出就是.
            // 交换 i 和 j 处的元素,确保左边部分的元素小于基准元素,右边部分的元素大于基准元素。
            exch(a, i, j);
        }
        exch(a, lo, j);
        return j;
    }

    private static void exch(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

🔥所有源码,一键测试:

package com.itheima.Array.sort;

import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;

/**
 * @Author: Ethan Yankang
 * @Program: code-random-recordings
 * @Date: 2024-08-06 13:01
 **/
class sort {

//======================================================================
//    冒泡排序
    public static void bubbleSort(int[] nums) {
        int len = nums.length;
        boolean flag;
//        这里排序比长度减一,因为其他的所有排好了,剩下的一定排好了。
        for (int i = 0; i < len - 1; i++) {
            flag = false;
            for (int j = 0; j < len - i - 1; j++) {
                if (nums[j] > nums[j + 1]) {
                    nums[j] ^= nums[j + 1];
                    nums[j + 1] ^= nums[j];
                    nums[j] ^= nums[j + 1];
                    flag = true;
                }
            }
            if (!flag) {
                break;
            }
        }
    }


//======================================================================
//    快速排序
    public static void qiuckSort(int[] a) {
        sort(a, 0, a.length - 1);
    }

    //    递归思想,你排好了,左右在拍好了,就完全拍好了。
    private static void sort(int[] a, int lo, int hi) {
        if (hi <= lo) return;
        int j = partition(a, lo, hi); // Partition the array
        sort(a, lo, j - 1);           // Sort the left part
        sort(a, j + 1, hi);           // Sort the right part
    }

    private static int partition(int[] a, int lo, int hi) {
        int i = lo, j = hi + 1;

        while (true) {
            // 找出大于基准元素a[lo]的元素a[++i];
            while ((a[++i]<a[lo])) {
                if (i == hi) break;
            }

            // 找出小于基准元素a[lo]的元素a[--j];
            while (a[lo]<a[--j]) {
                if (j == lo) break;
            }

            // 出现这种情况说明已经拍好序了,左边一定比他小,右边一定比他大。直接退出就是。
            if (i >= j) break;

            // 交换找到的不合理元素位置。到最后肯定全部交换完了。
            // 交换 i 和 j 处的元素,确保左边部分的元素小于基准元素,右边部分的元素大于基准元素。

            exch(a, i, j);
        }

        // 这里是抽离基准元素的动作,将他抽离到刚刚让左边比他小,右边比他大的位置上去。为什么一定是这样呢?
        // 因为j+1位置处的元素一定比他大,j这个位置的元素已经确认比他小了,所以交换,这下才使得小的在左边,大的在右边。

        exch(a, lo, j);

        // 返回基准元素的最新位置,方便对他的左右两边的子数组做递归处理
        return j;
    }

    // Helper functions
    private static boolean less(int v, int w) {
        return v<w;
    }

    private static void exch(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }


    //======================================================================
//    选择排序
public static void selectionSort(int[] nums){
        int len= nums.length;
    for (int i = 0; i < len-1; i++) {
        int minIndex=i;
        for (int j = i; j < len; j++) {
            if (nums[j]<nums[i]){
                minIndex=j;
            }
        }
        exch(nums,i,minIndex);
    }
}

//======================================================================
//    插入排序
    public static void insertSort(int[]  nums) {
        int len= nums.length;
        for (int i = 1; i < len; i++) {
            int key= nums[i];
            int Index=i-1;
            while (Index>=0&&nums[Index]>key){
                nums[Index+1]=nums[Index];
                Index--;
            }
            nums[Index+1]=key;
        }

    }


//======================================================================
//    shell排序
    public static void shellSort(int[] nums) {
        int n=nums.length;
        for (int gap = n/2; gap >0; gap/=2) {
            for (int i = gap; i < n; i++) {
                int key=nums[i];
//                int j=i;
                int Index=i-gap;
// 对每个子数组进行插入排序
                while (Index>=0&& nums[Index]>key){
                    nums[Index+gap]=nums[Index];
                    Index-=gap;
                }
                nums[Index+gap]=key;
            }

        }
    }

}

    public class Main{
        public static void main(String[] args) {

            int size = 50000-1;  // 数组大小
            int[] nums = new int[size];

            // 生成从1到100000的数字数组
            for (int i = 0; i < size; i++) {
                nums[i] = size - i;
            }
//===========================================================================
            // 冒泡排序
            System.out.println("冒泡排序");
            System.out.println("Before sort,nums[]:");
            System.out.println(Arrays.toString(nums));
            System.out.println("After sort,nums[]: ");
            int[] nums1=nums.clone();
//            计时器
            Instant start = Instant.now();
            sort.bubbleSort(nums1);
            Instant end = Instant.now();
            Duration time1 = Duration.between(start, end);
            System.out.println("耗时"+time1.toMillis());
            System.out.println(Arrays.toString(nums1));
            System.out.println();


//===========================================================================
            //  快速排序
            System.out.println("快速排序");
            System.out.println("Before sort,nums[]:");
            System.out.println(Arrays.toString(nums));
            System.out.println("After sort,nums[]: ");
            int[] nums2=nums.clone();

//            计时器
            start = Instant.now();
            sort.qiuckSort(nums2);
            end = Instant.now();
            Duration time2 = Duration.between(start, end);
            System.out.println("耗时"+time2.toMillis());
            System.out.println(Arrays.toString(nums2));
            System.out.println();

//===========================================================================

            //  选择排序
            System.out.println("选择排序");
            System.out.println("Before sort,nums[]:");
            System.out.println(Arrays.toString(nums));
            System.out.println("After sort,nums[]: ");
            int[] nums3=nums.clone();

//            计时器
            start = Instant.now();
            sort.selectionSort(nums3);
            end = Instant.now();
            Duration time3 = Duration.between(start, end);
            System.out.println("耗时"+time3.toMillis());
            System.out.println(Arrays.toString(nums3));
            System.out.println();


//===========================================================================

            //  插入排序
            System.out.println("插入排序");
            System.out.println("Before sort,nums[]:");
            System.out.println(Arrays.toString(nums));
            System.out.println("After sort,nums[]: ");
            int[] nums4=nums.clone();

//            计时器
            start = Instant.now();
            sort.insertSort(nums4);
            end = Instant.now();
            Duration time4 = Duration.between(start, end);
            System.out.println("耗时"+time4.toMillis());
            System.out.println(Arrays.toString(nums4));
            System.out.println();

//===========================================================================

            //  shell排序
            System.out.println("希尔排序");
            System.out.println("Before sort,nums[]:");
            System.out.println(Arrays.toString(nums));
            System.out.println("After sort,nums[]: ");
            int[] nums5=nums.clone();

//            计时器
            start = Instant.now();
            sort.shellSort(nums5);
            end = Instant.now();
            Duration time5 = Duration.between(start, end);
            System.out.println("耗时"+time5.toMillis());
            System.out.println(Arrays.toString(nums5));


            System.out.println();
            System.out.println();
            System.out.println("===========================================================================================");
            System.out.println("耗时分别如下:");
            System.out.println("冒泡排序time1 = " + time1.toMillis());;
            System.out.println("快速排序time2 = " + time2.toMillis());
            System.out.println("选择排序time3 = " + time3.toMillis());
            System.out.println("插入排序time4 = " + time4.toMillis());
            System.out.println("希尔排序time5 = " + time5.toMillis());
        }
    }

测试结果 


二、测试细节

🌈7.处理输出

1.标准ACM模式


    public static void main(String[] args) {
        System.out.println("Enter Nums[]'s length:");
        Scanner scanner = new Scanner(System.in);
        int length=scanner.nextInt();
        int[] nums = new int[length];
        System.out.println("Enter Nums[]'s :");
        for (int i = 0; i < length; i++) {
           nums[i]=scanner.nextInt();
        }

      
        System.out.println("Before sort,nums[]:");
        System.out.println(Arrays.toString(nums));
        System.out.println("After sort,nums[]: ");
        Instant start = Instant.now();
        bubbleSort(nums);
        Instant end = Instant.now();
        Duration elapsed = Duration.between(start, end);
        long time = elapsed.toMillis();
        System.out.println(Arrays.toString(nums));
        System.out.println("整个过程耗时"+time+"ms");
    }

}

2.基于自动模式


    public static void main(String[] args) {
        
        int size = 10000;  // 数组大小
        int[] nums = new int[size];

        // 生成从1到100000的数字数组
        for (int i = 0; i < size; i++) {
            nums[i] = size-i;
        }

        System.out.println("Before sort,nums[]:");
        System.out.println(Arrays.toString(nums));
        System.out.println("After sort,nums[]: ");
        Instant start = Instant.now();
        bubbleSort(nums);
        Instant end = Instant.now();
        Duration elapsed = Duration.between(start, end);
        long time = elapsed.toMillis();
        System.out.println(Arrays.toString(nums));
        System.out.println("整个过程耗时"+time+"ms");
    }

}

🌈8.时间处理

(四大步)

记住Instant()、Duration()、toMillis()

Instant start = Instant.now();
Instant end = Instant.now();
Duration elapsed = Duration.between(start, end);
long time = elapsed.toMillis();

三、踩坑事项@


1、数组转字符串:

Arrays.toString(nums)


2.修改默认栈大小@

在测试中初始化了一个50000个元素的数组,栈直接报错了StockOverflawErrer。

这里需要修改默认的1M为100M。默认的也太小了。


修改步骤


 成功完成。


3.时间的处理

最后的Duration如果不转化为toMillis()的的话很不友好,如下: 

转化后: 



💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖

热门专栏推荐

🌈🌈JAVA基础要夯牢                         关注走一波💕💕  

🌈🌈​​​​​​JAVA后端技术栈                          关注走一波💕💕  

🌈🌈JAVA面试八股文​​​​​​                          关注走一波💕💕  

🌈🌈代码随想录精讲200题                  关注走一波💕💕

🌈🌈JAVA项目(含源码深度剖析)    关注走一波💕💕  

🌈🌈计算机四件套                               关注走一波💕💕  

🌈🌈必知必会工具集                           关注走一波💕💕

🌈🌈书籍网课笔记汇总                       关注走一波💕💕         



📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤收藏✅ 评论💬,大佬三连必回哦!thanks!!!
📚愿大家都能学有所得,功不唐捐!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值