排序学习总结java实现

目录

1、冒泡排序

2、选择排序

3、插入排序


1、冒泡排序

描述:
        1. 依次比较数组中相邻两个元素大小,若 a[j] > a[j+1],则交换两个元素,两两都比较一遍称为一轮冒泡,结果是让最大的元素排至最后
        2. 重复以上步骤,直到整个数组有序

算法实现:

public class BubbleSort {
    public static void main(String[] args) {
        int[] a = {5,9,7,4,15,11,22,20};
        System.out.println("数组长度为" + a.length);
//        bubble(a);
        bubble_v2(a);
    }

    /**
     * 方法一:第一次优化:每经过一次循环 内层循环就减少一次
     * @param a
     */
    public static void bubble(int[] a) {
        for (int j = 0; j < a.length-1; j++) {
            boolean swapped = false;
            for (int i = 0; i < a.length - 1 -j; i++) {
                System.out.println("比较次数" + i);
                if (a[i] > a[i + 1]) {
                    swap(a, i, i + 1);
                    swapped = true;
                }
            }
            System.out.println("第"+ j + "轮冒泡"+Arrays.toString(a));
            if(!swapped){
                break;
            }
        }
    }

    /**
     * 方法二:优化过后
     * 每经过一次循环 内层循环就减少一次
     * 如果某一轮冒泡没有发生交换,则表示所有数据有序,可以结束外层循环
     * @param a
     */
    public static void bubble_v2(int[] a) {
        int n = a.length - 1;
        while (true) {
            int last = 0;                        //最后一次交换索引的位置
            for (int i = 0 ; i < n ; i++){      //交换次数 == 最后最后一次比较时候i的索引
                System.out.println("比较次数" + i);
                if (a[i] > a[i+1]) {
                    swap(a,i,i+1);
                    last = i;
                }

            }
            System.out.println("第轮冒泡"+Arrays.toString(a));
            n = last;
            if (last == 0) {
                break;
            }

        }

    }

    public static void swap(int[] a, int i ,int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;

    }
}

2、选择排序

描述:

1. 将数组分为两个子集,排序的和未排序的,每一轮从未排序的子集中选出最小的元素,放入排序子集

2. 重复以上步骤,直到整个数组有序

与冒泡比较

1. 二者平均时间复杂度都是 $O(n^2)$

2. 选择排序一般要快于冒泡,因为其交换次数少

3. 但如果集合有序度高,冒泡优于选择

4.冒泡稳定,选择不稳定

/**
 * 选择排序:
 * 将数组分为两个子集,排序和未排序,每一轮从未排序的子集中选出最小的元素,放入排序子集
 * 重复以上步骤
 */

public class SelectionSort {
    public static void main(String[] args) {
        int[] a = {5,9,7,4,15,11,22,20};
        selection(a);
    }

    public static void selection(int[] a) {

        for (int i = 0; i < a.length - 1;  i++) {
            //i是每轮选择最小元素交换的位置
            int s = i; //代表最小元素的索引值
            for (int j = s+1; j < a.length; j++) {   //本轮找出最小值的索引 赋给s
                if (a[s] > a[j]) {
                    s = j;
                }
            }
            if (s != i) {            //s不等于i表示 最小值索引变了
                swap(a,s,i);
            }
            System.out.println(Arrays.toString(a));
        }
    }

    public static void swap(int[] a, int i ,int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;

    }

}

3、插入排序

算法描述

1. 将数组分为两个区域,排序区域和未排序区域,每一轮从未排序区域中取出第一个元素,插入到排序区域(需保证顺序)

2. 重复以上步骤,直到整个数组有序

public class InsertSort {
    public static void main(String[] args) {
        int[]  a  = {9,3,7,2,5,8,1,4};
        System.out.println("素组元素大小"+a.length);
        insertsort(a);
        System.out.println("最后结果"+ Arrays.toString(a));
    }

    private static void insertsort(int[] a) {
        
        // i 表示等待插入的值,索引从1开始
        for (int i = 1; i < a.length; i++) {
            int t = a[i];   //t带插入的值
            int j = i-1;  //代表已经排序的索引
            while (j >= 0 ){
                if ( t < a[j]) {     //插入的值小于前面已经排序的
                    a[j+1] = a[j];      //待插入的就往后移动
                }else {
                    break;              //否则就是t比前面都打
                }
                j--;                    //往前面去比较
            }
            a[j+1] = t;                 //while循环从break结束。将t插入j+1索引 也就是与t比较的后一位,因为此时t比j大
            System.out.println(Arrays.toString(a));
        }

    }
}

 4、快速排序

算法描述

1. 每一轮排序选择一个基准点(pivot)进行分区
   1. 让小于基准点的元素的进入一个分区,大于基准点的元素的进入另一个分区
   2. 当分区完成时,基准点元素的位置就是其最终位置
2. 在子分区内重复以上过程,直至子分区元素个数少于等于 1,这体现的是分而治之的思想 ([divide-and-conquer](https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm))
3. 从以上描述可以看出,一个关键在于分区算法,常见的有洛穆托分区方案、双边循环分区方案、霍尔分区方案

---------------单边循环快排(lomuto 洛穆托分区方案)

1. 选择最右元素作为基准点元素

2. j 指针负责找到比基准点小的元素,一旦找到则与 i 进行交换

3. i 指针维护小于基准点元素的边界,也是每次交换的目标索引

4. 最后基准点与 i 交换,i 即为分区位置

 

public class QuickSort1 {
    public static void main(String[] args) {
        int[] a = {5,3,7,2,9,8,1,4};
        quick(a,0,a.length-1);
    }


    public static void quick(int[] a, int l,int h){
        if (l >= h) {
            return;
        }
        int p = partition(a,l,h);
        System.out.println("quick里面的p="+p);
        quick(a, l, p-1);
        quick(a, p+1,h);
    }
    /**
     * 以基准点做分区
     * @param a 数组
     * @param l 上边界
     * @param h 下边界
     * @return
     */
    private static int partition(int[] a, int l, int h) {
        int pv = a[h];                  //选取最后一位元素做为比较基准点
        int i = l;                     //维护小于基准点元素边界
        for (int j = l; j < h; j++) {        //j负责往后循环与pv比较
            if (a[j] < pv) {
                swap(a,i,j);        //a[j]比pv小就放到i前面
                i++;                   //小于基准点元素的边界往后+1
            }
        }
        swap(a,h,i);             //最后基准点与i交换,i就是分区位置
        System.out.println(Arrays.toString(a) + "   i=" + i);
        //返回值为基准点元素所在的正确索引,用他确定下一轮分区边界
        return i;
    }
    public static void swap(int[] a, int i ,int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;

    }
}

---------------双边循环快排-

1. 选择最左元素作为基准点元素
2. j 指针负责从右向左找比基准点小的元素,i 指针负责从左向右找比基准点大的元素,一旦找到二者交换,直至 i,j 相交
3. 最后基准点与 i(此时 i 与 j 相等)交换,i 即为分区位置

要点

1. 基准点在左边,并且要先 j 后 i

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值