7、Java——数组常用排序方法

简介: 7、Java——数组常用排序方法

前言

       排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。


       常见的排序有:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序等。

1.gif冒泡排序    

  a、冒泡排序,是通过每一次遍历获取最大/最小值


 b、将最大值/最小值放在尾部/头部


 c、然后除开最大值/最小值,剩下的数据在进行遍历获取最大/最小值


Tips: 由于冒泡排序只在相邻元素大小不符合要求时才调换他们的位置, 它并不改变相同元素之间的相对顺序, 因此它是稳定的排序算法.


代码展示:

public static void main(String[] args) {  
  /*
   * 有15个数字要求分别用选择法和冒泡法从大到小输出
   */
  Scanner sc = new Scanner(System.in);
  double[] indexs = new double[15];
  for (int i = 0; i < indexs.length; i++) {
    System.out.print("请输入第" + (i + 1) + "个数字:");
    indexs[i] = sc.nextDouble();
  }
  for (int i = 0; i < indexs.length - 1; i++) {
    for (int j = 0; j < indexs.length - 1 - i; j++) {
    if (indexs[j] < indexs[j + 1]) {
      double temp = indexs[j];
      indexs[j] = indexs[j + 1];
      indexs[j + 1] = temp;
    }
    }
  }
  System.out.print("冒泡排序:");
  for (int i = 0; i < indexs.length; i++) {
    System.out.print(indexs[i] + "\t");
  }
  }


动图效果 :


1.gif

选择排序

 a、将第一个值看成最小值


 b、然后和后续的比较找出最小值和下标


 c、交换本次遍历的起始值和最小值


 d、说明:每次遍历的时候,将前面找出的最小值,看成一个有序的列表,后面的看成无序的列表,然后每次遍历无序列表找出最小值。


Tips: 选择排序每次交换的元素都有可能不是相邻的, 因此它有可能打破原来值为相同的元素之间的顺序. 比如数组[2,2,1,3], 正向排序时, 第一个数字2将与数字1交换, 那么两个数字2之间的顺序将和原来的顺序不一致, 虽然它们的值相同, 但它们相对的顺序却发生了变化. 我们将这种现象称作 不稳定性 .


代码展示:


public static void main(String[] args) {
  /*
   * 有15个数字要求分别用选择法和冒泡法从大到小输出
   */
  Scanner sc = new Scanner(System.in);
  double[] indexs = new double[15];
  for (int i = 0; i < indexs.length; i++) {
    System.out.print("请输入第" + (i + 1) + "个数字:");
    indexs[i] = sc.nextDouble();
  }
  // 选择排序
  for (int i = 0; i < indexs.length - 1; i++) {
    for (int j = i + 1; j < indexs.length; j++) {
    if (indexs[i] < indexs[j]) {// 从小到大排序
      double temp = indexs[i];
      indexs[i] = indexs[j];
      indexs[j] = temp;
    }
    }
  }
  System.out.print("选择排序:");
  for (int i = 0; i < indexs.length; i++) {
    System.out.print(indexs[i] + "\t");
  }
  }


动图效果:


1.gif

插入排序


 a、默认从第二个数据开始比较。

b、如果第二个数据比第一个小,则交换。然后在用第三个数据比较,如果比前面小,则插入(狡猾)。否则,退出循环


 c、说明:默认将第一数据看成有序列表,后面无序的列表循环每一个数据,如果比前面的数据小则插入(交换)。否则退出。


代码展示:

 

public static void main(String[] args) {
        int arr[] = {7, 5, 3, 2, 4};
        //插入排序
        for (int i = 1; i < arr.length; i++) {
            //外层循环,从第二个开始比较
            for (int j = i; j > 0; j--) {
                //内存循环,与前面排好序的数据比较,如果后面的数据小于前面的则交换
                if (arr[j] < arr[j - 1]) {
                    int temp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = temp;
                } else {
                    //如果不小于,说明插入完毕,退出内层循环
                    break;
                }
            }
        }
    }

动图效果:

2.png

希尔排序

a、基本上和插入排序一样的道理

 b、不一样的地方在于,每次循环的步长,通过减半的方式来实现


 c、说明:基本原理和插入排序类似,不一样的地方在于。通过间隔多个数据来进行插入排序。


代码展示:

public static void main(String[] args) {
        int arr[] = {7, 5, 3, 2, 4};
        //希尔排序(插入排序变种版)
        for (int i = arr.length / 2; i > 0; i /= 2) {
            //i层循环控制步长
            for (int j = i; j < arr.length; j++) {
                //j控制无序端的起始位置
                for (int k = j; k > 0  && k - i >= 0; k -= i) {
                    if (arr[k] < arr[k - i]) {
                        int temp = arr[k - i];
                        arr[k - i] = arr[k];
                        arr[k] = temp;
                    } else {
                        break;
                    }
                }
            }
            //j,k为插入排序,不过步长为i
        }
    }

动图效果:

3.png



归并排序

 a、将列表按照对等的方式进行拆分


 b、拆分小最小快的时候,在将最小块按照原来的拆分,进行合并


 c、合并的时候,通过左右两块的左边开始比较大小。小的数据放入新的块中


 d、说明:简单一点就是先对半拆成最小单位,然后将两半数据合并成一个有序的列表。


Tips: 归并排序严格按照从左往右(或从右往左)的顺序去合并子数组, 它并不会改变相同元素之间的相对顺序, 因此它也是一种稳定的排序算法.


代码展示:

public static void main(String[] args) {
        int arr[] = {7, 5, 3, 2, 4, 1,6};
        //归并排序
        int start = 0;
        int end = arr.length - 1;
        mergeSort(arr, start, end);
    }
    public static void mergeSort(int[] arr, int start, int end) {
        //判断拆分的不为最小单位
        if (end - start > 0) {
            //再一次拆分,知道拆成一个一个的数据
            mergeSort(arr, start, (start + end) / 2);
            mergeSort(arr, (start + end) / 2 + 1, end);
            //记录开始/结束位置
            int left = start;
            int right = (start + end) / 2 + 1;
            //记录每个小单位的排序结果
            int index = 0;
            int[] result = new int[end - start + 1];
            //如果查分后的两块数据,都还存在
            while (left <= (start + end) / 2 && right <= end) {
                //比较两块数据的大小,然后赋值,并且移动下标
                if (arr[left] <= arr[right]) {
                    result[index] = arr[left];
                    left++;
                } else {
                    result[index] = arr[right];
                    right++;
                }
                //移动单位记录的下标
                index++;
            }
            //当某一块数据不存在了时
            while (left <= (start + end) / 2 || right <= end) {
                //直接赋值到记录下标
                if (left <= (start + end) / 2) {
                    result[index] = arr[left];
                    left++;
                } else {
                    result[index] = arr[right];
                    right++;
                }
                index++;
            }
            //最后将新的数据赋值给原来的列表,并且是对应分块后的下标。
            for (int i = start; i <= end; i++) {
                arr[i] = result[i - start];
            }
        }
    }


动图效果:


2.png


快速排序

 a、确认列表第一个数据为中间值,第一个值看成空缺(低指针空缺)。


 b、然后在剩下的队列中,看成有左右两个指针(高低)。


 c、开始高指针向左移动,如果遇到小于中间值的数据,则将这个数据赋值到低指针空缺,并且将高指针的数据看成空缺值(高指针空缺)。然后先向右移动一下低指针,并且切换低指针移动。


 d、当低指针移动到大于中间值的时候,赋值到高指针空缺的地方。然后先高指针向左移动,并且切换高指针移动。重复c、d操作。


 e、直到高指针和低指针相等时退出,并且将中间值赋值给对应指针位置。


 f、然后将中间值的左右两边看成行的列表,进行快速排序操作。


代码展示:


public static void main(String[] args) {
        int arr[] = {7, 5, 3, 2, 4, 1, 8, 9, 6};
        //快速排序
        int low = 0;
        int high = arr.length - 1;
        quickSort(arr, low, high);  
    }
    public static void quickSort(int[] arr, int low, int high) {
        //如果指针在同一位置(只有一个数据时),退出
        if (high - low < 1) {
            return;
        }
        //标记,从高指针开始,还是低指针(默认高指针)
        boolean flag = true;
        //记录指针的其实位置
        int start = low;
        int end = high;
        //默认中间值为低指针的第一个值
        int midValue = arr[low];
        while (true) {
            //高指针移动
            if (flag) {
                //如果列表右方的数据大于中间值,则向左移动
                if (arr[high] > midValue) {
                    high--;
                } else if (arr[high] < midValue) {
                    //如果小于,则覆盖最开始的低指针值,并且移动低指针,标志位改成从低指针开始移动
                    arr[low] = arr[high];
                    low++;
                    flag = false;
                }
            } else {
                //如果低指针数据小于中间值,则低指针向右移动
                if (arr[low] < midValue) {
                    low++;
                } else if (arr[low] > midValue) {
                    //如果低指针的值大于中间值,则覆盖高指针停留时的数据,并向左移动高指针。切换为高指针移动
                    arr[high] = arr[low];
                    high--;
                    flag = true;
                }
            }
            //当两个指针的位置相同时,则找到了中间值的位置,并退出循环
            if (low == high) {
                arr[low] = midValue;
                break;
            }
        }
        //然后出现有,中间值左边的小于中间值。右边的大于中间值。
        //然后在对左右两边的列表在进行快速排序
        quickSort(arr, start, low -1);
        quickSort(arr, low + 1, end);
    }


动图效果:

1.gif

目录
打赏
0
0
0
0
0
分享
相关文章
|
16天前
|
Java 中的 toString() 方法详解:为什么它如此重要?
在Java开发中,`toString()`方法至关重要,用于返回对象的字符串表示。默认实现仅输出类名和哈希码,信息有限且不直观。通过重写`toString()`,可展示对象字段值,提升调试效率与代码可读性。借助Lombok的`@Data`注解,能自动生成标准化的`toString()`方法,简化开发流程,尤其适合字段较多的场景。合理运用`toString()`,可显著提高开发效率与代码质量。
49 0
Java 中数组Array和列表List的转换
本文介绍了数组与列表之间的相互转换方法,主要包括三部分:1)使用`Collections.addAll()`方法将数组转为列表,适用于引用类型,效率较高;2)通过`new ArrayList&lt;&gt;()`构造器结合`Arrays.asList()`实现类似功能;3)利用JDK8的`Stream`流式计算,支持基本数据类型数组的转换。此外,还详细讲解了列表转数组的方法,如借助`Stream`实现不同类型数组间的转换,并附带代码示例与执行结果,帮助读者深入理解两种数据结构的互转技巧。
Java 中数组Array和列表List的转换
Java 中的 equals 方法:看似简单,实则深藏玄机
本文深入探讨了Java中`equals`方法的设计与实现。默认情况下,`equals`仅比较对象引用是否相同。以`String`类为例,其重写了`equals`方法,通过引用判断、类型检查、长度对比及字符逐一比对,确保内容相等的逻辑。文章还强调了`equals`方法需遵循的五大原则(自反性、对称性等),以及与`hashCode`的关系,避免集合操作中的潜在问题。最后,对比了`instanceof`和`getClass()`在类型判断中的优劣,并总结了正确重写`equals`方法的重要性,帮助开发者提升代码质量。
50 1
|
1月前
|
《从头开始学java,一天一个知识点》之:方法定义与参数传递机制
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 🚀 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。上篇:《输入与输出:Scanner与System类》 | 下篇剧透:《方法重载与可变参数》。
55 25
|
1月前
|
《从头开始学java,一天一个知识点》之:数组入门:一维数组的定义与遍历
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。明日预告:《多维数组与常见操作》。 通过实例讲解数组的核心认知、趣味场景应用、企业级开发规范及优化技巧,帮助你快速掌握Java数组的精髓。
68 23
Java 中数组的多种定义方式
本文深入解析了Java中数组的多种定义方式,涵盖基础的`new`关键字创建、直接初始化、动态初始化,到多维数组、`Arrays.fill()`方法以及集合类转换为数组等高级用法。通过理论与实践结合的方式,探讨了每种定义方法的适用场景、优缺点及其背后的原理,帮助开发者掌握高效、灵活的数组操作技巧,从而编写更优质的Java代码。
41 0
|
16天前
|
java中一个接口A,以及一个实现它的类B,一个A类型的引用对象作为一个方法的参数,这个参数的类型可以是B的类型吗?
本文探讨了面向对象编程中接口与实现类的关系,以及里氏替换原则(LSP)的应用。通过示例代码展示了如何利用多态性将实现类的对象传递给接口类型的参数,满足LSP的要求。LSP确保子类能无缝替换父类或接口,不改变程序行为。接口定义了行为规范,实现类遵循此规范,从而保证了多态性和代码的可维护性。总结来说,接口与实现类的关系天然符合LSP,体现了多态性的核心思想。
26 0
|
1月前
|
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
57 1
Java 复制数组
本文介绍了Java中数组的基础知识与常用操作,包括数组的概念、创建、访问元素、遍历、复制、排序和搜索等方法。同时详细讲解了数组的五种赋值方式,并通过代码示例演示了求总和平均值、最大最小值、升序降序排序及Arrays类的常用方法。内容深入浅出,适合初学者学习掌握Java数组的核心功能与应用场景。
Java容器及其常用方法汇总
Java Collections框架提供了丰富的接口和实现类,用于管理和操作集合数据。
Java容器及其常用方法汇总