数据结构与算法(7)——排序算法

1.冒泡排序

在这里插入图片描述

小结:
(1). 一共进行数组的大小-1次大的循环
(2).每一趟排序的次数在逐渐的减少
(3).如果我们发现在某趟排序中,没有发生一次交换,则提前结束冒泡排序(优化)

代码实现:

public static void bubbleSort(int[] arr){
        //冒泡排序,时间复杂度为O(n^2)
        int temp = 0;//临时变量
        boolean flag = false;//标识变量,表示是否进行过交换
        for (int i = 0;i < arr.length-1;i++){
            for (int j = 0;j < arr.length-1-i;j++){
                //如果前面的数比后面的数大则交换
                if (arr[j] > arr [j+1]){
                    flag = true;//进行过交换
                    temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }
//            System.out.println("第"+(i+1)+"趟排序后的数组");
//            System.out.println(Arrays.toString(arr));

            if (!flag){//在一趟排序中一次交换都没有发生过
                break;
            }else {
                flag = false;//重置flag,进行下一次判断
            }
        }
    }

2.选择排序

选择排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的
在这里插入图片描述
说明:
1.选择排序一共有数组大小-1轮排序
2.每一轮排序又是一个循环,循环的规则
2.1先假定当前这个数是最小数
2.2然后和后面的每一个数进行比较,如果发现有比当前数更小的数,就重新确定这个最小数,并得到下标
2.3当遍历完数组的最后时,就得到了本轮循环的最小数及其对应下标
2.4交换

代码实现:

    //选择排序
    public static void selectSort(int[] arr){
        for (int i = 0;i<arr.length-1;i++){//一共要进行length-1次
            int min = arr[i];//开始排序前将未排序的数的第一个假定为最小
            int minIndex = i;//假定最小值所对应的数组索引
            for (int j = i+1;j < arr.length;j++){
                if (min > arr[j]){//找最小值及其对应索引
                    min = arr[j];
                    minIndex = j;
                }
            }
            if(minIndex!=i){//如果找到的最小值索引不是第一个数,那么交换
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }

3.插入排序

插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。
在这里插入图片描述

    //插入排序
    public static void insertSort(int[] arr){
        
        int insertVal;
        int insertIndex;
        for (int i =0 ;i < arr.length-1;i++){
            //定义待插入的数
            insertVal = arr[i+1];
            insertIndex = i;

            //给insertVal找到插入的位置
            while (insertIndex >= 0 && insertVal < arr[insertIndex]){//保证在给insertVal找插入位置时不越界
                arr[insertIndex+1] = arr[insertIndex];
                insertIndex--;
            }
            //退出while循环时,插入位置找到 insertIndex+1
            if (insertIndex+1 != i){
                arr[insertIndex+1] = insertVal;
            }
            System.out.println("第"+(i+1)+"轮插入结果:");
            System.out.println(Arrays.toString(arr));
        }
    }

4.希尔排序

对于插入排序,(加入按照从小到大的顺序排列)若有一个很小的数在数列的靠后位置,这种情况下,后移的次数明显增多,对效率有影响。

希尔排序是插入排序的改进版本

在这里插入图片描述

4.1 希尔排序(交换法)代码实现

//希尔排序(交换法)
    public static void shellSort(int[] arr){
        int temp = 0;
        int count = 0;
        for (int gap = arr.length / 2 ;gap > 0;gap /= 2 ){
            for (int i = gap;i < arr.length;i++){//每组的第二个数开始遍历
                //遍历各组中所有的元素(共gap组,步长gap)
                for (int j = i - gap;j >= 0;j=j-gap){
                    //如果当前这个元素大于加上步长后的那个元素,说明交换
                    if (arr[j] > arr[j+gap]){
                        temp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = temp;
                    }
                }
            }
            count++;
            System.out.println("希尔排序第"+count+"轮之后"+ Arrays.toString(arr));
        }

4.2 希尔排序(移位法)代码实现

//对交换式的希尔排序进行改进————————》》》》》移位法
    public static void shellSort2(int[] arr){
        for (int gap = arr.length/2;gap > 0;gap /= 2 ){
            for (int i = gap;i < arr.length;i++){
                int j = i;
                int temp = arr[j];
                if (arr[j] < arr[j - gap]){
                    while (j-gap >=0 && temp < arr[j-gap]){
                        //移动
                        arr[j] = arr[j - gap];
                        j=j-gap;
                    }
                    //当退出while循环找到temp插入的位置
                    arr[j] = temp;
                }
            }
        }
    }

5.快速排序

在这里插入图片描述

先把数组中的一个数当作基准数,一般会把数组中最左边的数当作基准数,然后从两边进行检索。先从右边检索比基准数小的。再从左边检索比基准数大的。如果检索到了,就停下,然后交换这两个元素。然后再继续检索。此时数组被基准数分为两部分,左边比基准数小,右边比基准数大。第一轮排序结束。再分别把左边和右边当作两个新数组,再以第一轮排序方式进行第二轮排序,知道整个数组排序完成。
在这里插入图片描述

代码实现

    public static void quickSort(int[] arr,int left,int right){
        //进行判断,如果左边索引比右边大,不合法
        if (left > right ){
            return;
        }
        //定义变量保存基准数
        int base = arr[left];
        //定义变量i,指向最左边
        int i = left;
        //定义变量j,指向最右边
        int j = right;

        //当i和j不相遇,在循环中检索
        while (i != j){
            //先由j从右往左检索比基准数小的,检索到比基准数小的就停下
            while (arr[j] >= base && i < j){
                j--; //j从右往左移动
            }
            while (arr[i] <= base && i < j){
                i++; //j从左往右移动
            }
            //代码运行到此处代表i j 都停下了,交换i和j位置的元素
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        //如果上面循环条件不满足,说明i j 相遇了,就交换基准数和相遇位置的元素
        arr[left] = arr[i];
        arr[i] = base;

        //基准数在这里就归为了左边的数比他小,右边的数比他大
        //排基准数左边
        quickSort(arr,left,i-1);
        //排基准数右边
        quickSort(arr,i+1,right);
    }

6.归并排序

在这里插入图片描述
在这里插入图片描述
代码实现:

public class MergeSort {
    public static void main(String[] args) {
        int arr[] = {8,4,5,7,1,3,6,2,9};
        int temp[] = new int[arr.length];
        mergeSort(arr,0,arr.length-1,temp);
        System.out.println(Arrays.toString(arr));
    }

    //数组的分解+合并的方法
    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){
            if (arr[i] <= arr[j]){ //左边的数填充到临时数组
                temp[t] = arr[i];
                t++;
                i++;
            }else {
                temp[t] = arr[j]; //将右边序列的当前元素填充到temp数组
                t++;
                j++;
            }
        }
        //把有剩余数据的一边的数据依次全部填充到temp
        while ( i <= mid){
            temp[t] = arr[i];
            t++;
            i++;
        }
        while (j <= right) {
            temp[t] = arr[j];
            t++;
            j++;
        }

        //将temp数组拷贝到arr,并不是每次都拷贝所有元素
        t = 0;
        int templeft = left;
        while (templeft <= right){
            arr[templeft] = temp[t];
            t++;
            templeft++;
        }
    }
}

7.基数排序

在这里插入图片描述
在这里插入图片描述
代码实现:

//基数排序方法
    public static void radixSort(int[] arr){

        //根据推导过程,我们可以得到最终的基数排序的代码
        //1.得到数组中最大的数的位数
        int max = arr[0];
        for (int i = 0; i<arr.length;i++){
            if (arr[i] > max){
                max = arr[i];
            }
        }
        //得到最大数是几位数
        int maxLength = (max+"").length();

        //定义一个二维数组,表示10个桶,每个桶就是一个一维数组
        /*说明
        1.二维数组包含10个一维数组
        2.为了在放入数的时候数据溢出,则每个一维数组的大小定义为arr.length
         */
        int[][] bucket = new int[10][arr.length];

        //为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶每次放入的数据的个数
        int[] bucketElementCounts = new int[10];

        //使用循环将代码处理
        for (int i= 0,n = 1;i < maxLength;i++,n *= 10){
            //针对每个元素的对应的位进行排序
            for (int j = 0;j < arr.length;j++){
                //q取出每个元素的个位的值
                int digitOfElement = arr[j] / n % 10;
                //放入到对应的桶中
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }

            //按照这个桶的顺序放入原来数组
            int index = 0;
            for (int k = 0;k < bucketElementCounts.length;k++ ){
                //如果桶中有数据,才放入原数组
                if (bucketElementCounts[k] != 0){
                    //循环该桶即第k个一维数组
                    for(int l = 0;l < bucketElementCounts[k];l++){
                        //取出元素放到arr
                        arr[index] = bucket[k][l];
                        index++;
                    }
                }
                bucketElementCounts[k] = 0;
            }
            System.out.println("第"+(i+1)+"轮排序的结果"+ Arrays.toString(arr));
        }
    }

8.排序算法时间复杂度的比较

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值