java数据结构

排序算法

简单排序

在我们的程序中,排序是非常常见的一种需求,提供一些数据元素,把这些数据元素按照一定的规则进行排序。比
如查询一些订单,按照订单的日期进行排序;再比如查询一些商品,按照商品的价格进行排序等等。所以,接下来
我们要学习一些常见的排序算法。

在 java 的开发工具包 jdk 中,已经给我们提供了很多数据结构与算法的实现,比如 List,Set,Map,Math 等等,都
是以 API 的方式提供,这种方式的好处在于一次编写,多处使用。我们借鉴 jdk 的方式,也把算法封装到某个类中,
那如果是这样,在我们写 java 代码之前,就需要先进行 API 的设计,设计好之后,再对这些 API 进行实现。

就比如我们先设计一套 API 如下:

类名 ArrayList
构造方法 ArrayList():创建 ArrayList 对象
成员方法 1. boolean add(E e):向集合中添加元素
2. E remove(int index):从集合中删除指定的元素

然后再使用 java 代码去实现它。

冒泡排序

冒泡排序(Bubble Sort), 是一种计算机科学领域的较简单的排序算法。

需求:

排序前:{4,5,6,3,2,1}

排序后:{1,2,3,4,5,6}

排序原理:

  1. 比较相邻的元素。如果前一个元素比后一个元素大,就交换这两个元素的位置。
  2. 对每一对相邻元素做同样的工作,从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大
    值。

动画演示:

冒泡排序API设计:

类名 Bubble
构造方法 Bubble():创建Bubble对象

成员方法
1.public static void sort(Comparable[] a):对数组内的元素进行排序
2.private static boolean greater(Comparable v,Comparable w):判断v是否大于w
3.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

冒泡排序的代码实现:

public class Bubble {
    /*
       对数组a中的元素进行排序
    */
    public static void sort(Comparable[] a){
        for(int i=a.length-1;i>0;i--){
            for(int j=0;j<i;j++){
                //{6,5,4,3,2,1}
                //比较索引j和索引j+1处的值
                if (greater(a[j],a[j+1])){
                    exch(a,j,j+1);
                }
            }
        }
    }

    /*
        比较v元素是否大于w元素
     */
    private static  boolean greater(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }

    /*
    数组元素i和j交换位置
     */
    private static void exch(Comparable[] a,int i,int j){
        Comparable temp;
        temp = a[i];
        a[i]=a[j];
        a[j]=temp;
    }
}

测试代码:

import java.util.Arrays;
public class BubbleTest {
    public static void main(String[] args) {
        Integer[] arr = {4,5,6,3,2,1};
        Bubble.sort(arr);
        System.out.println(Arrays.toString(arr));//{1,2,3,4,5,6}
    }

}

冒泡排序使用了双层 for 循环,时间复杂度为 \(\mathcal{O}(n^2)\).

选择排序

选择排序是一种更加简单直观的排序方法。

需求:
排序前:{4,6,8,7,9,2,10,1}
排序后:{1,2,4,5,7,8,9,10}

排序原理:

  1. 每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如果当前索引处
    的值大于其他某个索引处的值,则假定其他某个索引出的值为最小值,最后可以找到最小值所在的索引
  2. .交换第一个索引处和最小值所在的索引处的值

image.png

动画演示:

选择排序API设计:

类名 Selection
构造方法 Selection():创建Selection对象
成员方法 1.public static void sort(Comparable[] a):对数组内的元素进行排序
2.private static boolean greater(Comparable v,Comparable w): 判断v是否大于w
3.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

选择排序的代码实现:

public class Selection {
    /*
       对数组a中的元素进行排序
    */
    public static void sort(Comparable[] a){
        for(int i=0;i<=a.length-2;i++){
            //定义一个变量,记录最小元素所在的索引,默认为参与选择排序的第一个元素所在的位置
            int minIndex = i;
            for(int j=i+1;j<a.length;j++){
                //需要比较最小索引minIndex处的值和j索引处的值;
                if (greater(a[minIndex],a[j])){
                    minIndex=j;
                }
            }
            //交换最小元素所在索引minIndex处的值和索引i处的值
            exch(a,i,minIndex);
        }
    }
    /*
        比较v元素是否大于w元素
    */
    private static  boolean greater(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }
    /*
    数组元素i和j交换位置
    */
    private static void exch(Comparable[] a,int i,int j){
        Comparable temp;
        temp = a[i];
        a[i]=a[j];
        a[j]=temp;
    }
}

测试代码:

import java.util.Arrays;

public class SelectionTest {
    public static void main(String[] args) {
        //原始数据
        Integer[] a = {4,6,8,7,9,2,10,1};
        Selection.sort(a);
        System.out.println(Arrays.toString(a));//{1,2,4,5,7,8,9,10}
    }
}

选择排序使用了双层 for 循环,时间复杂度为 \(\mathcal{O}(n^2)\).

插入排序

插入排序(Insertion sort)是一种简单直观且稳定的排序算法。

插入排序的工作方式非常像人们排序一手扑克牌一样。开始时,我们的左手为空并且桌子上的牌面朝下。然后,我
们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌的正确位置,我们从右到左将它与已在
手中的每张牌进行比较,如下图所示:

image.png

需求:
排序前:{4,3,2,10,12,1,5,6}
排序后:{1,2,3,4,5,6,10,12}

排序原理:

  1. 把所有的元素分为两组,已经排序的和未排序的;
  2. 找到未排序的组中的第一个元素,向已经排序的组中进行插入;
  3. 倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待
    插入元素放到这个位置,其他的元素向后移动一位。

image.png

动画演示:

插入排序API设计:

类名 Insertion
构造方法 Insertion():创建Insertion对象
成员方法 1.public static void sort(Comparable[] a):对数组内的元素进行排序
2.private static boolean greater(Comparable v,Comparable w): 判断v是否大于w
3.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

插入排序代码实现:

public class Insertion {
    /*
       对数组a中的元素进行排序
    */
    public static void sort(Comparable[] a){
        for(int i=1;i<a.length;i++){
            for(int j=i;j>0;j--){
 //比较索引j处的值和索引j-1处的值,如果索引j-1处的值比索引j处的值大,则交换数据,如果不大,那么就找到合适的位置了,退出循环即可;
                if (greater(a[j-1],a[j])){
                    exch(a,j-1,j);
                }else{
                    break;
                }
            }

        }
    }
    /*
        比较v元素是否大于w元素
    */
    private static  boolean greater(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }
    /*
    数组元素i和j交换位置
    */
    private static void exch(Comparable[] a,int i,int j){
        Comparable temp;
        temp = a[i];
        a[i]=a[j];
        a[j]=temp;
    }
}

测试代码:

import java.util.Arrays;
public class InsertionTest {
    public static void main(String[] args) {
        Integer[] a = {4,3,2,10,12,1,5,6};
        Insertion.sort(a);
        System.out.println(Arrays.toString(a));//{1,2,3,4,5,6,10,12}
    }
}

插入排序使用了双层 for 循环,时间复杂度为 \(\mathcal{O}(n^2)\).

高级排序

之前我们学习过基础排序,包括冒泡排序,选择排序还有插入排序,并且对他们在最坏情况下的时间复杂度做了分
析,发现都是 \(\mathcal{O}(n^2)\),而平方阶通过我们之前学习算法分析我们知道,随着输入规模的增大,时间成本将急剧上
升,所以这些基本排序方法不能处理更大规模的问题,接下来我们学习一些高级的排序算法,争取降低算法的时间
复杂度最高阶次幂。

希尔排序

希尔排序是插入排序的一种,又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本。

前面学习插入排序的时候,我们会发现一个很不友好的事儿,如果已排序的分组元素为{2,5,7,9,10},未排序的分组
元素为{1,8},那么下一个待插入元素为1,我们需要拿着1从后往前,依次和10,9,7,5,2进行交换位置,才能完成真
正的插入,每次交换只能和相邻的元素交换位置。那如果我们要提高效率,直观的想法就是一次交换,能把1放到
更前面的位置,比如一次交换就能把1插到2和5之间,这样一次交换1就向前走了5个位置,可以减少交换的次数,
这样的需求如何实现呢?接下来我们来看看希尔排序的原理。

需求:

排序前:{9,1,2,5,7,4,8,6,3,5}
排序后:{1,2,3,4,5,5,6,7,8,9}

排序原理:

  1. 选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组;
  2. 对分好组的每一组数据完成插入排序;
  3. 减小增长量,最小减为1,重复第二步操作。

image.png

增长量h的值每一固定的规则,我们这里采用以下规则:

int h=1
while(h<5){
	h=2h+1;//3,7
}
//循环结束后我们就可以确定h的最大值;
h的减小规则为:
	h=h/2

动画演示:

希尔排序的API设计:

类名 Shell
构造方法 Shell():创建Shell对象
成员方法 1.public static void sort(Comparable[] a):对数组内的元素进行排序
2.private static boolean greater(Comparable v,Comparable w): 判断v是否大于w
3.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

希尔排序的代码实现:

public class Shell {
    /*
       对数组a中的元素进行排序
    */
    public static void sort(Comparable[] a){
        //1.根据数组a的长度,确定增长量h的初始值;
        int h = 1;
        while(h<a.length/2){
            h=2*h+1;
        }
        //2.希尔排序
        while(h>=1){
            //排序
            //2.1.找到待插入的元素
            for (int i=h;i<a.length;i++){
                //2.2把待插入的元素插入到有序数列中
                for (int j=i;j>=h;j-=h){

                    //待插入的元素是a[j],比较a[j]和a[j-h]
                    if (greater(a[j-h],a[j])){
                        //交换元素
                        exch(a,j-h,j);
                    }else{
                        //待插入元素已经找到了合适的位置,结束循环;
                        break;
                    }
                }

            }
            //减小h的值
            h= h/2;
        }

    }

    /*
        比较v元素是否大于w元素
     */
    private static  boolean greater(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }

    /*
    数组元素i和j交换位置
     */
    private static void exch(Comparable[] a,int i,int j){
        Comparable temp;
        temp = a[i];
        a[i]=a[j];
        a[j]=temp;
    }
}

测试代码:

import java.util.Arrays;

public class ShellTest {
    public static void main(String[] args) {
        Integer[] a = {9,1,2,5,7,4,8,6,3,5};
        Shell.sort(a);
        System.out.println(Arrays.toString(a));//{1,2,3,4,5,5,6,7,8,9}
    }
}

参考资料

黑马程序员Java数据结构与算法,全网资料最全,154张数据结构图

汉诺塔杂谈

十大经典排序算法动画与解析,看我就够了!

posted @ 2021-08-03 16:48  梧桐鹿  阅读(77)  评论(0编辑  收藏  举报