DS05排序

五、排序

image-20210817113728225

image-20210418094316799

image-20210418094328171

3.1 冒泡排序

冒泡排序

 @Test
    public void test1Bubble() {

        /**
         * 方式一:
         *      两个循环的初始值都是0 循环次数都是数组长度
         */
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if(arr[j] > arr[i]){
                    int temp = arr[j];
                    arr[j] = arr[i];
                    arr[i] = temp;
                }
            }
        }
        System.out.println("快速排序:" + Arrays.toString(arr));
        /**
         * 方式二:
         *  外层遍历最后一个元素不需要在遍历一次
         *  内层遍历找每次从第i个未移动的进行比较
         *
         */
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = i; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j+1]){
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        System.out.println("冒泡排序:" + Arrays.toString(arr));
    }

3.2 选择排序

(简单)选择排序

3.2.1 思想
  1. 第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换

  2. ,…,

  3. 第n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

@Test
    public void test2Selection() {
        /**
         * 先从第i个找,并认定其最小,然后从后面比较有比他小的没,有就装min中
         * 每次结束一层遍历,就和第i个元素交换
         */
        for (int i = 0; i < arr.length - 1; i++) {
            int min = arr[i];
            int index = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < min) {
                    index = j;
                    min = arr[index];
                }
            }
            int temp = arr[i];
            arr[i] = min;
            arr[index] = temp;
        }
        System.out.println("选择排序:" + Arrays.toString(arr));
    }

3.3 插入排序

(直接)插入排序

3.3.1 思想

​ 插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

@Test
    public void test3Insertion() {
        //从第i个元素开始向前找,比前面小的就换到前面。
        //判断交换即可
        for (int i = 1; i < arr.length - 1; i++) {
            for (int j = i; j > 0; j--) {
                if (arr[j] < arr[j - 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = temp;
                }
            }
        }
        System.out.println("插入排序:" + Arrays.toString(arr));
    }

3.4 希尔排序

3.4.1 思想

​ 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

image-20210503143203035

image-20210418094347144

 @Test
    public void test4Shell() {
        //
        //规定一个跳跃间隔(i),并每次都/2,满足其大于0
        for (int i = arr.length / 2; i > 0; i /= 2) {
            //通过得知遍历的间隔,我们可以确定可以这样遍历的次数j
            for (int j = i; j < arr.length; j++) {
                //得知指向数值的一个指针,通过间隔就可以进行两个数的比较 k=j-i,是获得的最较低位的地方,需要加上一个间隔,就可以进行比较了
                for (int k = j - i; k >= 0; k -= i) {
                    //然后比较两个值 满足条件进行互换
                    if (arr[k] > arr[k + i]) {
                        int temp = arr[k];
                        arr[k] = arr[k + i];
                        arr[k + i] = temp;
                    }
                }
            }
        }
        System.out.println("希尔排序:" + Arrays.toString(arr));
    }

3.5 快速排序

快速排序

3.5.1 思想

​ 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

public class Quick {

    public static void main(String[] args) {
        int arr[] = {3, 9, -1, 10, 20};
        quick(arr,0,arr.length -1);
        System.out.println(Arrays.toString(arr));
    }

    /**
     *
     * @param arr       传入的数组
     * @param left      从0开始
     * @param right     到数组最后一位结束
     */
    private static void quick(int[] arr,int left,int right){
        int l = left;//记录左指针
        int r = right;//记录右指针
        int midValue = arr[(left + right)/2];//找到中间的索引的值
        while (l < r){//满足左指针小于右指针循环
            while (arr[l] < midValue){//当左边的小于中间的就让左指针++
                l++;
            }
            while (arr[r] > midValue){//当右边的大于中间的就让右指针--
                r--;
            }
            if(l>=r){//若本次左指针大于等于右指针了跳出循环
                break;
            }
            //*****交换操作******
            int temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //*****交换操作******
            if(arr[l] == midValue){//当前值指向中间值左指针继续++ 以便于完成递归
               l++;
            }
            if(arr[r] == midValue){//当前值指向中间值右指针继续-- 以便于完成递归
                r--;
            }
        }
        if(l==r){//如果两个指针同时指向同一个位置左+右-
            l++;
            r--;
        }
        //这时候相当于第二遍进行递归快速排序
        //右指针满足大于左指针就递归
        if(r > left){
            quick(arr,left,r);
        }
        if(l < right){
            quick(arr,l,right);
        }
    }
}

3.6 归并排序

归并排序

归并

4.6.1 思路
  • 分治思想

image-20210418094402763

  • 具体过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kvhwaptw-1620135333851)(https://gitee.com/jerrygrj/img/raw/master/img/%E8%B7%AA(1)].png)

  1. 编写合的方法(int[] arr,int left,int mid,int right,int[] temp)
    1. 记录left right mid temp的指针
    2. 左边的数据与右边的数据比较填入temp
    3. 检查有没有剩下的元素,进行填充
    4. 然后将temp数组的内容填入到arr
  2. 编写分的方法(int[] arr,int left,int right,int[] temp)
    1. 声明mid值(left+right)/2
    2. 然后递归本方法
      1. 左半部分(arr,left,mid,temp)
      2. 右半部分(arr,mid+1,right,temp)
    3. 调用合的方法并传入参数
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
    if (left < right) {
        int mid = (left + right) / 2; //中间索引
        //向左递归进行分解
        mergeSort(arr, left, mid, temp);
        //向右递归进行分解
        mergeSort(arr, mid + 1, right, temp);
        //合并
        merge(arr, left, mid, right, temp);

    }
}

//合并的方法

/**
 * @param arr   排序的原始数组
 * @param left  左边有序序列的初始索引
 * @param mid   中间索引
 * @param right 右边索引
     * @param temp  做中转的数组
 */
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {

    int i = left; // 初始化i, 左边有序序列的初始索引
    int j = mid + 1; //初始化j, 右边有序序列的初始索引
    int t = 0; // 指向temp数组的当前索引

    //(一)
    //先把左右两边(有序)的数据按照规则填充到temp数组
    //直到左右两边的有序序列,有一边处理完毕为止
    while (i <= mid && j <= right) {//继续
        //如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
        //即将左边的当前元素,填充到 temp数组
        //然后 t++, i++
        if (arr[i] <= arr[j]) {
            temp[t] = arr[i];
            t += 1;
            i += 1;
        } else { //反之,将右边有序序列的当前元素,填充到temp数组
            temp[t] = arr[j];
            t += 1;
            j += 1;
        }
    }

    //(二)
    //把有剩余数据的一边的数据依次全部填充到temp
    while (i <= mid) { //左边的有序序列还有剩余的元素,就全部填充到temp
        temp[t] = arr[i];
        t += 1;
        i += 1;
    }

    while (j <= right) { //右边的有序序列还有剩余的元素,就全部填充到temp
        temp[t] = arr[j];
        t += 1;
        j += 1;
    }


    //(三)
    //将temp数组的元素拷贝到arr
    //注意,并不是每次都拷贝所有
    t = 0;
    int tempLeft = left; //
    //第一次合并 tempLeft = 0 , right = 1 //  tempLeft = 2  right = 3 // tL=0 ri=3
    //最后一次 tempLeft = 0  right = 7
    while (tempLeft <= right) {
        arr[tempLeft] = temp[t];
        t += 1;
        tempLeft += 1;
    }

}

3.7 基数排序

基数排序

3.7.1基数排序基本思想
  1. 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

  2. 这样说明,比较难理解,下面我们看一个图文解释,理解基数排序的步骤

image-20210418094437548 image-20210418094453483
3.7.2 步骤
  1. 首先找到arr中的最大数的位数,作为外层循环
int maxLength = (max+"").length;
  1. 创建一个二维数组(表示10个桶,和每个桶中的数字),再创建一个一维数组,表示每个桶中数字的个数。按个位、十位…的数字添加到对应的桶中
  2. 将每个桶中的数组再拿出来按顺序放到arr中
3.7.4 代码
/**
     * 基数排序
     *
     * @param arr 传入要排序的数组
     */
    public static void radixSort(int[] arr) {
        //找数组中的最大值,并获取最大数的长度,用于外层循环
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        //获得最大数的长度
        int maxLength = (max + "").length();
        //创建二维数组 [数字] [内容] ---数字对应的数据
        int[][] bucket = new int[10][arr.length];
        //用于基数每个数(桶)的个数
        int[] countBucket = new int[10];
        //最大位数作为循环条件抽取每一个放入对应的数(桶)中
        for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {//定义一个除数,用于每次得出个位、十位、百位...
            for (int j = 0; j < arr.length; j++) {
                int digitEnd = arr[j] / n % 10;//将拿到的这个数字存放起来
                bucket[digitEnd][countBucket[digitEnd]] = arr[j];//把对应位的原数据放入bucket中
                countBucket[digitEnd]++;//计数桶自增
            }
            int index = 0;//定义放回到原数组的指针
            for (int j = 0; j < countBucket.length; j++) {//扫描计数桶
                if (countBucket[j] != 0) {//桶里不为空就装入原数组
                    for (int k = 0; k < countBucket[j]; k++) {
                        arr[index++] = bucket[j][k];
                    }
                }
                //记得将桶清空,一遍下次装入
                countBucket[j] = 0;
            }
        }
    }

3.8 堆排序

在这里插入图片描述

3.8.1 堆排序基本介绍
  1. 堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种**选择排序,**它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。

  2. 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。

  3. 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆

  4. 大顶堆举例说明

image-20210419200231558

image-20210419200246347

  • 特点:arr[i] >= arr[2*i+1] && arr[i] >= arr[2*i+2] // i 对应第几个节点,i从0开始编号
  1. 小顶堆举例说明
  • 特点:arr[i] <= arr[2*i+1] && arr[i] <= arr[2*i+2] // i 对应第几个节点,i从0开始编号
3.8.2 排序思想
  1. 将待排序序列构造成一个大顶堆

  2. 此时,整个序列的最大值就是堆顶的根节点。

  3. 将其与末尾元素进行交换,此时末尾就为最大值。

  4. 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

3.8.3 代码
/**
     * 传入待排序的数组
     * @param arr
     */
    private static void heapSort(int[] arr) {
        int temp = 0;
        //通过中间访问
        将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
        for (int i = arr.length / 2-1; i >= 0; i--) {
            sorth(arr,i,arr.length);
        }

        for (int i = arr.length - 1; i > 0 ; i--) {
            temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            sorth(arr,0,i);
        }
    }

    /**
     * i:父节点索引  2*i+1就是左子节点
     * @param arr
     * @param i
     * @param length
     */
    private static void sorth(int[] arr, int i, int length) {
        int temp = arr[i];//先取出当前元素的值,保存在临时变量
        for (int j = 2*i+1; j <length; j= j*2+1) {
            if(j+1<length&&arr[j]<arr[j+1]){//说明左子结点的值小于右子结点的值
                j++;// j 指向右子结点
            }
            if(temp < arr[j]){//子节点大于父节点 ,把大的给了父节点。把子节点指针赋给父节点,循环比较
                arr[i] = arr[j];
                i = j;
            }else {
                break;
            }
        }
        //将原来的父节点的值给了子节点
        arr[i] = temp;
    }

3.9 计数排序

3.9.1 算法步骤
  1. 找出最值
  2. 开辟存储空间,存储次数
  3. 累加次数,根据前一个
  4. 从后往前遍历,找到合适的位置
  5. 讲有序的数组存入新的数组中
3.9.2 代码
public class CountSort {
    public static void main(String[] args) {
        Integer[] arr= Integers.random(10,1,100);
        System.out.println(Arrays.toString(arr));
        int max = arr[0];
        int min = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(max < arr[i]){
                max = arr[i];
            }
            if(min > arr[i]){
                min = arr[i];
            }
        }
        int[] countInt = new int[max-min+1];
        for (int i = 0; i < arr.length; i++) {
            countInt[arr[i] - min]++;
        }
        for (int i = 1; i < countInt.length; i++) {
            countInt[i] += countInt[i-1];
        }
        int[] newInt = new int[arr.length];
        for (int i = arr.length-1; i >= 0 ; i--) {
            newInt[--countInt[arr[i] - min]] = arr[i];
        }
        System.out.println(Arrays.toString(newInt));

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值