HMJAVA数据结构与算法3【排序】

1 简单排序

 

 

1.1 Comparable接口

 

package com.haifei.demo01sort;

public class Student implements Comparable<Student>{

    private String username;
    private int age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }

    //定义排序规则
    @Override
    public int compareTo(Student o) {
        return this.getAge()-o.getAge();
    }
}
View Code
package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Student;

public class Test01Comparable {

    public static void main(String[] args) {
        Student s1 = new Student();
        s1.setUsername("zhangsan");
        s1.setAge(20);
        Student s2 = new Student();
        s2.setUsername("lisi");
        s2.setAge(18);

        Comparable max = getMax(s1, s2);
        System.out.println(max); // Student{username='zhangsan', age=20}
    }

    public static Comparable getMax(Comparable c1, Comparable c2){
        int result = c1.compareTo(c2);
        //如果result<0,则c1比c2小;
        //如果result>0,则c1比c2大;
        //如果result==0,则c1和c2一样大;
        if (result>=0){
            return c1;
        }else{
            return c2;
        }
    }

}
View Code

 

 

1.2 冒泡排序

 

 

 

package com.haifei.demo01sort;

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++){
                if (greater(a[j], a[j+1])){ //比较索引j和索引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 = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}
View Code

 

package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Bubble;

import java.util.Arrays;

public class Test02Bubble {

    public static void main(String[] args) {
//        int[] arr = {4,5,6,3,2,1}; //自定义的sort方法需要传入的参数是Comparable[]
        Integer[] arr = {4,5,6,3,2,1}; //使用int的包装类,因为public final class Integer extends Number implements Comparable<Integer>
        Bubble.sort(arr);
        System.out.println(Arrays.toString(arr)); //[1, 2, 3, 4, 5, 6]
    }

}
View Code

 

 

 

1.3 选择排序

 

 

 

 

package com.haifei.demo01sort;

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++){
                if (greater(a[minIndex], a[j])){ //比较最小索引minIndex处的值和j索引处的值
                    minIndex = j;
                }
            }
            exch(a, i, minIndex); //交换最小元素所在索引minIndex处的值和索引i处的值
        }
    }

    //比较元素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 = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}
View Code

 

package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Selection;

import java.util.Arrays;

public class Test03Selection {

    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, 6, 7, 8, 10, 9]
    }

}
View Code

 

 

 

1.4 插入排序

 

 

 

 

 

package com.haifei.demo01sort;

public class Insertion {

    //对数组a中的元素进行排序
    public static void sort(Comparable[] a){
        for (int i=1; i<a.length; i++){
            //当前元素为a[i],依次和i前面的元素比较,找到一个小于等于a[i]的元素
            for (int j=i; j>0; j--){
                if (greater(a[j-1], a[j])){ //比较索引j处的值和j-1处的值
                    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 = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}
View Code
package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Insertion;

import java.util.Arrays;

public class Test04Insertion {

    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]
    }

}
View Code

 

 

2 高级排序

 

 

2.1 希尔排序

 

 

 

 

 

 

 

 

 

 

 

package com.haifei.demo01sort;

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 = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

}
View Code

 

package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Shell;

import java.util.Arrays;

public class Test05Shell {

    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]
    }

}
View Code

 

 

package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Insertion;
import com.haifei.demo01sort.Shell;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class Test06SortCompare {

    public static void main(String[] args) throws IOException {
        //1.创建一个ArrayList集合,保存读取出来的整数
        ArrayList<Integer> list = new ArrayList<>();

        //2.创建缓存读取流BufferedReader,读取数据,并存储到ArrayList中;
        BufferedReader reader = new BufferedReader(new InputStreamReader(Test06SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt")));
        String line=null;
        while((line=reader.readLine())!=null){
            //line是字符串,把line转换成Integer,存储到集合中
            int i = Integer.parseInt(line);
            list.add(i);
        }
        reader.close();
        /*
        项目中新加入外部文件时,可能找不到文件而报空指针,需要在项目名上右键rebuild module一下
         */

        //3.把ArrayList集合转换成数组
        Integer[] a = new Integer[list.size()];
        list.toArray(a);

        //4.调用测试代码完成测试
//        testInsertion(a);//21906毫秒
//        testShell(a);//20毫秒
    }

    //测试希尔排序
    public static void testShell(Integer[] a){
        //1.获取执行之前的时间
        long start = System.currentTimeMillis();
        //2.执行算法代码
        Shell.sort(a);
        //3.获取执行之后的时间
        long end = System.currentTimeMillis();
        //4.算出程序执行的时间并输出
        System.out.println("希尔排序执行的时间为:"+(end-start)+"毫秒");

    }

    //测试插入排序
    public static void testInsertion(Integer[] a){
        //1.获取执行之前的时间
        long start = System.currentTimeMillis();
        //2.执行算法代码
        Insertion.sort(a);
        //3.获取执行之后的时间
        long end = System.currentTimeMillis();
        //4.算出程序执行的时间并输出
        System.out.println("插入排序执行的时间为:"+(end-start)+"毫秒");
    }


}
View Code

 

 

 

 

2.2 归并排序

1、递归

 

 

 

 

 

 

 

 

 

 

 

package com.haifei.demo01sort.test;

/**
 * 递归
 */
public class Test07Factorial {

    public static void main(String[] args) {
        System.out.println(factorial(5)); //120
//        System.out.println(factorial(100000)); //java.lang.StackOverflowError
    }

    //求n的阶乘
    public static long factorial(int n){
        if (n == 1){
            return 1;
        }
        return n * factorial(n-1);
    }

}
View Code

 

 

2、归并排序

 

 

 

 

 

 

 

 

 

package com.haifei.demo01sort;

public class Merge {

    //归并所需要的辅助数组
    private static Comparable[] assist;

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

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

    /*
           对数组a中的元素进行排序
        */
    public static void sort(Comparable[] a) {
        //1.初始化辅助数组assist;
        assist = new Comparable[a.length];
        //2.定义一个lo变量,和hi变量,分别记录数组中最小的索引和最大的索引;
        int lo=0;
        int hi=a.length-1;
        //3.调用sort重载方法完成数组a中,从索引lo到索引hi的元素的排序
        sort(a,lo,hi);
    }

    /*
    对数组a中从lo到hi的元素进行排序
     */
    private static void sort(Comparable[] a, int lo, int hi) {
        //做安全性校验;
        if (hi<=lo){
            return;
        }

        //对lo到hi之间的数据进行分为两个组
        int mid = lo+(hi-lo)/2;//   5,9  mid=7

        //分别对每一组数据进行排序
        sort(a,lo,mid);
        sort(a,mid+1,hi);

        //再把两个组中的数据进行归并
        merge(a,lo,mid,hi);
    }

    /*
    对数组中,从lo到mid为一组,从mid+1到hi为一组,对这两组数据进行归并
     */
    private static void merge(Comparable[] a, int lo, int mid, int hi) {
        //定义三个指针
        int i=lo;
        int p1=lo;
        int p2=mid+1;

        //遍历,移动p1指针和p2指针,比较对应索引处的值,找出小的那个,放到辅助数组的对应索引处
        while(p1<=mid && p2<=hi){
            //比较对应索引处的值
            if (less(a[p1],a[p2])){
                assist[i++] = a[p1++];
            }else{
                assist[i++]=a[p2++];
            }
        }

        //遍历,如果p1的指针没有走完,那么顺序移动p1指针,把对应的元素放到辅助数组的对应索引处
        while(p1<=mid){
            assist[i++]=a[p1++];
        }
        //遍历,如果p2的指针没有走完,那么顺序移动p2指针,把对应的元素放到辅助数组的对应索引处
        while(p2<=hi){
            assist[i++]=a[p2++];
        }
        //把辅助数组中的元素拷贝到原数组中
        for(int index=lo;index<=hi;index++){
            a[index]=assist[index];
        }
    }

}
View Code

 

package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Merge;

import java.util.Arrays;

public class Test08Merge {

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

 

 

 

 

 

 

package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Insertion;
import com.haifei.demo01sort.Merge;
import com.haifei.demo01sort.Shell;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class Test06SortCompare {

    public static void main(String[] args) throws IOException {
        //1.创建一个ArrayList集合,保存读取出来的整数
        ArrayList<Integer> list = new ArrayList<>();

        //2.创建缓存读取流BufferedReader,读取数据,并存储到ArrayList中;
        BufferedReader reader = new BufferedReader(new InputStreamReader(Test06SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt")));
        String line=null;
        while((line=reader.readLine())!=null){
            //line是字符串,把line转换成Integer,存储到集合中
            int i = Integer.parseInt(line);
            list.add(i);
        }
        reader.close();
        /*
        项目中新加入外部文件时,可能找不到文件而报空指针,需要在项目名上右键rebuild module一下
         */

        //3.把ArrayList集合转换成数组
        Integer[] a = new Integer[list.size()];
        list.toArray(a);

        //4.调用测试代码完成测试
//        testInsertion(a);//21906毫秒
//        testShell(a);//20毫秒
        testMerge(a);//56毫秒
    }

    //测试希尔排序
    public static void testShell(Integer[] a){
        //1.获取执行之前的时间
        long start = System.currentTimeMillis();
        //2.执行算法代码
        Shell.sort(a);
        //3.获取执行之后的时间
        long end = System.currentTimeMillis();
        //4.算出程序执行的时间并输出
        System.out.println("希尔排序执行的时间为:"+(end-start)+"毫秒");

    }

    //测试插入排序
    public static void testInsertion(Integer[] a){
        //1.获取执行之前的时间
        long start = System.currentTimeMillis();
        //2.执行算法代码
        Insertion.sort(a);
        //3.获取执行之后的时间
        long end = System.currentTimeMillis();
        //4.算出程序执行的时间并输出
        System.out.println("插入排序执行的时间为:"+(end-start)+"毫秒");
    }

    //测试归并排序
    public static void testMerge(Integer[] a){
        //1.获取执行之前的时间
        long start = System.currentTimeMillis();
        //2.执行算法代码
        Merge.sort(a);
        //3.获取执行之后的时间
        long end = System.currentTimeMillis();
        //4.算出程序执行的时间并输出
        System.out.println("归并排序执行的时间为:"+(end-start)+"毫秒");
    }

}
View Code

 

 

 

2.3 快速排序

 

 

 

 

 

 

 

 

 

 

package com.haifei.demo01sort;

public class Quick {

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

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

    //对数组内的元素进行排序
    public static void sort(Comparable[] a) {
        int lo = 0;
        int hi = a.length-1;
        sort(a,lo,hi);
    }

    //对数组a中从索引lo到索引hi之间的元素进行排序
    private static void sort(Comparable[] a, int lo, int hi) {
        //安全性校验
        if (hi<=lo){
            return;
        }
        //需要对数组中lo索引到hi索引处的元素进行分组(左子组和右子组);
        int partition = partition(a, lo, hi);//返回的是分组的分界值所在的索引,分界值位置变换后的索引
        //让左子组有序
        sort(a,lo,partition-1); //递归
        //让右子组有序
        sort(a,partition+1,hi); //递归
    }

    //对数组a中,从索引 lo到索引 hi之间的元素进行分组,并返回分组界限对应的索引
    public static int partition(Comparable[] a, int lo, int hi) {
        //确定分界值
        Comparable key = a[lo];
        //定义两个指针,分别指向待切分元素的最小索引处和最大索引处的下一个位置
        int left=lo;
        int right=hi+1;
        //切分
        while(true){
            //先从右往左扫描,移动right指针,找到一个比分界值小的元素,停止
            while(less(key,a[--right])){
                if (right==lo){
                    break;
                }
            }
            //再从左往右扫描,移动left指针,找到一个比分界值大的元素,停止
            while(less(a[++left],key)){
                if (left==hi){
                    break;
                }
            }
            //判断 left>=right,如果是,则证明元素扫描完毕,结束循环,如果不是,则交换元素即可
            if (left>=right){
                break;
            }else{
                exch(a,left,right);
            }
        }
        //交换分界值
        exch(a,lo,right);
        return right;
    }

}
View Code
package com.haifei.demo01sort.test;

import com.haifei.demo01sort.Quick;

import java.util.Arrays;

public class Test09Quick {

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

}
View Code

 

 

 

 

 

 

 

 

2.4 排序的稳定性

 

 

 

 

 

 

 

posted @ 2021-09-10 16:23  yub4by  阅读(42)  评论(0)    收藏  举报