八大排序分析及代码(java版本)

冒泡排序

每次比较相邻的两个数,如果逆序则交换 依次找出最大的

思想及规则

规则:

  • 一共进行数组的大小-1次大的循环

  • 每一趟排序的次数在逐渐减少

  • 如果在某此次排序中,没有发生一次交换,则可以提前结束冒泡。

    时间复杂度:O(n^2)

代码

//冒泡排序
    public static int[] bubble(int[] arr) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            boolean flag = true;
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = false;
                }
            }
            System.out.println("这是第" + (i + 1) + "次排序");
            if (flag) {
                break;
            }
        }
        return arr;
    }

选择排序

思想及规则

思想:第一次从arr[0]–arr[n]找最小的,与第一个交换;
第二次从arr[1]–arr[n]中找最小的,与下标为1的交换
以此类推,直到把整个数组遍历完。
说明

1.选择排序一共有数组大小-1轮排序
2.每1轮排序,又是一个循环,
2.1 先假定当前这个数是最小数
2.2 然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小的数,
并得到下标
2.3 当遍历到数组的最后时,就得到本轮最小的数和下标
2.4交换

代码

//选择排序
    public static int[] select(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            int min = arr[i];
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) { //说明假定的值并不是最小的值
                    min = arr[j]; //重置最小值和下标
                    minIndex = j;
                }
            }
            //将最小的值放在arr[i]位置上  与当前下标交换
            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
        return arr;
    }

插入排序

思想

把n个待排序的元素堪称为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含n-1个元素,排序过程中每次从无序表中取出第一个元素,把他的排序码依次与有序元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

代码

//插入排序
    public static int[] insert(int[] arr) {
        //使用for循环  简化代码
        for (int i = 1; i < arr.length; i++) {
            //定义待插入的数
            int insertVal = arr[i];
            int insertIndex = i - 1;//已经处于另一个数组的最后的下标
            //给insertVal找到插入的位置
            //说明:1.insertIndex>=0保证插入数组的位置不越界
            //2.insertVal<arr[insertIndex] 待插入的值,还没有找到要插入的位置
            //3.需要将arr[insertIndex]后移
            while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            //当退出while循环时,说明插入的位置已经找到,insertIndex
            //或者为:  insertVal>arr[insertIndex];
            //判断是否要插入
            if (insertIndex + 1 != i) {
                arr[insertIndex + 1] = insertVal;
            }
            System.out.println("第" + i + "轮插入");
            System.out.println(Arrays.toString(arr));
        }
        return null;
    }

希尔排序

思想

插入排序的改进,把记录按下标一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减小,每组包含的关键词越来越多,当增量减为1,整个文件恰好被分为一组,算法便终止。

代码

下面写了两种方法 我们通常选择第二种,其效率更高,也更好理解。

//希尔排序  交换法
    public static void shell1(int[] arr) {
        int temp = 0;
        //使用循环处理   分组数
        // 3, 9, -1, 10, 11,8,-2,0
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                //遍历各组中所有的元素,共gap组,每组有arr.length/gap个元素,步长为gap
                for (int j = i - gap; j >= 0; j -= gap) {
                    //如果当前元素大于加上步长后的那个元素。说明交换
                    if (arr[j] > arr[j + gap]) {
                        temp = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = temp;
                    }
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }

    //  优化 移位法
    public static void shell2(int[] arr) {
       //增量gap,并逐步的缩小增量
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            //从第gap个元素,逐个对其所在的组进行插入排序
            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-=gap;
                 }
                 //当退出while后,就给temp找到插入的位置
                 arr[j]=temp;
             }
            }
        }
        System.out.println(Arrays.toString(arr));
    }

快速排序

思想

对冒泡排序的一种改进。

基本思想:通过一趟比较将要排列的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按照此方法对这两部分数据分别进行快速排序,整个排序过程可以递归实现,依次达到整个数据都变为有序序列。

代码

1.递归

 /**
     * 递归
     * */
    public  static void quick(int[] arr,int left,int right) {
        int m = quickSort(arr, left, right);
      //  System.out.println(m);
        if (left < right) {
         if(m!=left) {
             quick(arr, left, m - 1);
         }
            if (m != right) {
                quick(arr, m + 1, right);
            }
        }
        //System.out.println(Arrays.toString(arr));
    }
    public static int quickSort(int[] arr, int left, int right) {
       int pivot = arr[left];
        while (left < right) {
            while (left < right && arr[right] >= pivot) {
                right--;
            }
            arr[left] = arr[right];
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = pivot;
        return left;
    }

2.非递归

//非递归
    public void sort(int []arr, int left, int right) {
        int privot, top, last;
        Stack<Integer> s = new Stack<Integer>();
        last=0;
        top=0;
        privot=QuickSort(arr, left, right);
        if(privot>left+1) {
            s.push(left);
            s.push(privot-1);
        }
        if(privot<right-1) {
            s.push(privot+1);
            s.push(right);
        }
        while(!s.empty()) {
            top = s.pop();
            last = s.pop();
            privot = QuickSort(arr, last, top);
            if(privot>last+1) {
                s.push(last);
                s.push(privot-1);
            }
            if(privot<top-1) {
                //System.out.println(top);
                s.push(privot+1);
                s.push(top);
            }
        }
    }
    public int QuickSort(int []arr, int left, int right) {
        int pivot = arr[left];
        while (left < right) {
            while (left < right && arr[right] >= pivot) {
                right--;
            }
            arr[left] = arr[right];
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = pivot;
        return left;
    }

归并排序

思想

是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分为一些小的问题然后递归求解,而治的阶段则将分的阶段得到的阶段得到的各答案“修补”在一起,即分而治之)

代码

   //归并排序
    public static void merge(int[] arr,int left,int mid,int right) {
        int[] temp = new int[arr.length];
        int i = left, j = mid + 1;
        int k = 0;//表示temp的下标
        while(i<=mid&&j<=right){
            if(arr[i]<=arr[j]){
                temp[k++]=arr[i++];
            }
            else {
                temp[k++]=arr[j++];
            }
        }

        //此时mid后面的已经合并完,剩下前面的全部合并
        while (i <= mid) {
            temp[k++] = arr[i++];
        }//前面的合并完 剩下后面的直接写入temp中
        while (j <= right) {
            temp[k++] = arr[j++];
        }
        //完成后 将temp的值 赋值给原arr数组
        for (int x = left,y=0; x <= right; x++,y++) {
            arr[x] = temp[y];
        }
    }

    public static void mergeSort(int[] arr,int start,int end){
        if(start<end){//至少要大于1个元素  小于一个元素 则停止
            int mid=(start+end)/2;
            mergeSort(arr,start,mid);
            mergeSort(arr,mid+1,end);
            merge(arr,start,mid,end);
        }
    }

基数排序

思想

1>基数排序属于"分配式排序",又称"桶子法",顾名思义,他是通过键值的哥哥位的值, 将要排序的元素分配至某一些桶中,达到排序的作用
2>基数排序法是属于稳定性的排序,基数排序法的效率高的稳定性排序法
3>基数排序法是桶排序的扩展
4>基数排序是1887年赫尔曼.何乐礼发明的。他是这样实现的:将整数按位切割成不同的数字,然后按照每个位数分别比较。

代码(包含对负数的排序)

 //基数排序  思路 可以按照个位十位 逐步放  最终寻找其共同处
    public static void RadixSort(int[]arr){
        int max=arr[0];
         int[] brr=Arrays.copyOf(arr,arr.length);

        for (int i = 0; i < brr.length; i++) {
            if(brr[i]<0){
                brr[i]/=-1;
            }
        }

        for (int i = 1; i < brr.length; i++) {
            if(max<brr[i]){
                max=brr[i];
            }
        }
        //求max是几位数: max+" "length
        int maxLength=(max+"").length();
        // 共有19个桶  前9个桶存放负数后10个桶存放0和正数
        int[][] temp=new int[19][arr.length];
        //记录每个桶中实际存放的多少个数据
        int[] bucketElementCounts=new int[19];
        for(int i=0,n=1;i<maxLength;i++,n*=10) {
            for (int j = 0; j < arr.length; j++) {
                //取出每个桶对应的位数的值
                int m = arr[j]/n % 10;
                //向前移动9位  确保负数在前九个 正数在后面
                m+=9;
                temp[m][bucketElementCounts[m]] = arr[j];
                bucketElementCounts[m]++;
            }
            //按照每个桶的顺序  (一维数组的下标依次取出数据,放入原来的数组)
            int index = 0;
            //遍历每一桶,并将桶中的数据放入元素组
            for (int k = 0; k < bucketElementCounts.length; k++) {
                //如果桶中有数据 采访到原数组
                if (bucketElementCounts[k] != 0) {
                    if(k<9){
                        //循环该桶放入   从后往前输入 负数 大的在后 小的在前
                        for (int l = bucketElementCounts[k]-1; l >=0; l--) {
                            //取出元素到arr
                            arr[index++] = temp[k][l];
                        }
                    }
                    else {
                        //循环该桶放入  从前往后
                        for (int l = 0; l < bucketElementCounts[k]; l++) {
                            //取出元素到arr
                            arr[index++] = temp[k][l];
                        }
                    }
                }
                bucketElementCounts[k]=0;
            }
        }
    }

堆排序

思想

1>将待排序的序列构成一个大根堆
2>此时,整个序列的最大值就是堆顶的根节点
3>将其与末尾元素进行交换,此时末尾元素就是最大值
4>然后将剩余n-1个元素造成一个堆,这样会得到n个元素的次小值。如此反复执行,便得到 一个有序序列。
可以看到在构建大顶堆的过程中,元素的个数逐渐减少,最后就得到了一个有序序列。

代码

 /**
     * 功能:完成将以i对应的非叶子结点的树调整成大顶堆
     *     * 举例:int[]arr={4,6,8,5,9}=>i=1=>adjustHeap=>得到{4,9,8,5,6}
     *     * 如果我们再次调用adjustHeap 传入的是 i=0=>得到{4,9,8,5,6}
     *     * 得到=》{9,6,8,5,4}
     * @param arr   待调整的数组
     * @param i   表示非叶子节点在数组中索引
     * @param length  表示对多少元素继续调整,length逐渐在减少
     */
    public static void adjustHeap(int[] arr,int i,int length ){
        int temp=arr[i];//先取出当前元素的值,保存在临时变量中
        //开始调整  说明
        //1.k=i*2+1 k表示i节点的左子节点
        for(int k=i*2+1;k<length;k=k*2+1){
            if(k+1<length&&arr[k]<arr[k+1]){
                k++;
            }
            if(arr[k]>temp){
                arr[i]=arr[k];
                i=k;
            }
            else {
                break;
            }
        }
        //当for循环结束后,我们已经以i为父节点的树的最大值,放在了最顶(局部)
        arr[i]=temp;
    }
    public static void heapSort(int[] arr){
        for (int i = arr.length/2-1; i >= 0; i--) {
            adjustHeap(arr,i,arr.length);
        }
        /**
         * 2. 将堆顶元素与末尾元素交换,将最大的元素沉到数组末尾
         * 3.重新调整结构,使其满足堆定义,然后交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,知道整个序列有序
         */
        int temp=0;
        for (int j = arr.length-1; j >0; j--) {
            //交换
            temp=arr[j];
            arr[j]=arr[0];
            arr[0]=temp;
            adjustHeap(arr,0,j);
        }
    }

总结(时间复杂度比较)

在这里插入图片描述在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值