【数据结构与算法】排序问题总结之冒泡、选择、插入、希尔、归并、快速排序 || 附Java源码

算法总结:
在这里插入图片描述

图片名词解释:

n:数据规模

k:"桶"的个数

In-place:占用常数内存,不占用额外内存

Out-place:占用额外内存

冒泡排序:

每次比较两个数,如果顺序不一致则交换顺序,不断遍历数组,直到没有可以交换的数为止。

演示

在这里插入图片描述

代码实现:

/**
 * @author CT
 * @create 2021-06-27 10:38
 * @data 2021/6/27 - 10:38
 * 先 把程序跑通
 * 再 把边界问题改进
 * 后 可重复调用的写成方法
 */
public class BubbleSort {
    static void print(int[] arr){
        for( int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    static void swap(int[] arr,int i,int j){
        //交换位置
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
     public static void main(String[] args) {
        int[] arr = {4,8,6,72,9,13,2,58,7};
        sort(arr);
        print(arr);
    }
    //以上是主体框架,由输出数组print()、交换顺序swap()和main主方法组成
    
    static void sort(int[] a){
        //排序方法
        for(int i = a.length-1;i>0;i--){
            //第一层循环:遍历数组,此处是用的减法;i表示的是第二层循环到的最终位置
            //减一是为了保障数据在后面加一时不会超出范围
            //可以转用加法:for(int i = 0 ; i<a.length-1;i++){}
            for(int j = 0;j<i;j++){
                //第二层循环:从在开头开始将最大的值往后交换
                //因为在设计的时候第一层循环是从最后的开始遍历               
                if(a[j]>a[j+1])
                    swap(a,j,j+1);
            }
        }
    }
}

选择排序

选择排序是稳定的算法之一

遍历数组,找到其中最大或者最小的数,并将其交换到最前或者最后,然后再剩下的元素中继续寻找

演示

在这里插入图片描述

代码实现:

/**
 * @author CT
 * @create 2021-06-22 22:44
 * @data 2021/6/22 - 22:44
 */

/*
* 先 把程序跑通
* 再 把边界问题改进
* 后 可重复调用的写成方法
*/
public class SelectionSort {
   static void print(int[] arr){
        for( int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    static void swap(int[] arr,int i,int j){
        //交换位置
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    //以上是主体框架,由输出数组print()、交换顺序swap()组成
    public static void main(String[] args) {
        //建立数组
        int[] arr = {4,8,6,72,9,13,2,58,7};
        for(int i = 0 ; i<arr.length-1;i++){
            //设置最小
            int minPos = i;
            //找到最小
            for(int j = i+1;j<arr.length;j++){
//                if (arr[j]<arr[minPos]){
//                    minPos = j;
//                }
                //替换成下面这个
                minPos = arr[j]<arr[minPos]?j:minPos;
                //arr[j]是否小于arr[minPos] 如果是则minPos为j,如果不是则是minPos
            }
            //交换位置
            swap(arr,i,minPos);
            System.out.println("经过第"+i+"后的内容");
            //打印
            print(arr);
        }
    }

}

插入排序

在数组中遍历数,选择一个数在已排队列中两两比较,找到合适位置时插入

演示

在这里插入图片描述

代码实现:

/**
 * @author CT
 * @create 2021-06-27 14:27
 * @data 2021/6/27 - 14:27
 * 先 把程序跑通
 * 再 把边界问题改进
 * 后 可重复调用的写成方法
 */
public class InsterSort {
    static void print(int[] arr){
        for( int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    static void swap(int[] arr,int i,int j){
        //交换位置
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
     public static void main(String[] args) {
        int[] arr = {4,8,6,72,9,13,2,58,7};
        sort(arr);
        print(arr);
    }
    //以上是主体框架,由输出数组print()、交换顺序swap()和main主方法组成
    
    static void sort(int[] a){
        for (int i = 1;i<a.length;i++){
            for (int j = i;j>0 && a[j]<a[j-1] ;j--){
//                int temp = a[j];
//                a[j] = a[j-1];
//                a[j-1] = temp;
                //使用临时变量交换数据
                //if(a[j]<a[j-1]){
                //这个判断可以放到for的判断中
                    swap(a,j,j-1);
                //}
            }
        }
    }
}

希尔排序

希尔排序是插入排序的一种变体

能够缩短插入排序的时间复杂度

他是选择一个增量gap序列,按着序列中的个数对数组分组排序排序

演示

在这里插入图片描述

代码实现:

/**
 * @author CT
 * @create 2021-06-27 15:12
 * @data 2021/6/27 - 15:12
 * 先 把程序跑通
 * 再 把边界问题改进
 * 后 可重复调用的写成方法
 */
public class shellSort {
    static void print(int[] arr){
        for( int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    static void swap(int[] arr,int i,int j){
        //交换位置
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    public static void main(String[] args) {
        int[] arr = {4,8,6,72,9,13,2,58,7};
        sort(arr);
        print(arr);
    }
    //以上是主体框架,由输出数组print()、交换顺序swap()和main主方法组成
    
    static void sort(int[] a){
//        1使用:for(int gap = a.length/2;gap>0;gap/=2) { //对半取gap
        
        int h = 1;
        while(h<=a.length/3){
            h = h*3+1;
        }
        //2、使用knuth序列

        for(int gap = h;gap>0;gap = (gap-1)/3) {
            for (int i = gap; i < a.length; i++) {
                for (int j = i; j > gap - 1; j -= gap) {
                    if (a[j] < a[j - gap]) {
                        swap(a, j, j - gap);
                    }
                }
            }
        }
    }
}

归并排序

归并排序不受输入数据的影响,时间复杂度始终是O(nlogn)

思路

  • 把长度为n的数组(arr)分成两半(left right)
  • 分别从这两半中各取出一值相互比较,并将较小的存入另一个数组空间(temp)内,直到全部排完
    • 如果其中一半有剩余的,便将剩余的一并放入temp中
    • 利用递归重复排序

演示

在这里插入图片描述

程序

import javax.crypto.AEADBadTagException;

public class MerqeSort {
    static void print(int[] arr){
        //打印数据
        for( int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    static void swap(int[] arr,int i,int j){
        //交换位置
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
    public static void main(String[] args) {
        int[] arr = {1,4,7,8,3,6,9};
        //左右两半顺序对
        sort(arr,0,arr.length-1);
        //传入数组,左半的起始指针,右半的起始指针
        print(arr);
    }
    
    static void sort(int[] a,int left,int right){
//        merage1(a);
//        merage2(a,1,4,5);

        if (left == right) return;
        //分两半
        int mid = left+(right-left)/2;
        //左排序
        sort(a,left,mid);
        //右排序
        sort(a,mid+1,right);
        merage(a,left,mid+1,right);
    }
    
    static void merage(int[] arr,int leftPtr,int rightptr,int rightBound) {
        //优化2.0
        int mid = rightptr-1;
        // mid,中间指针位置
        int[] temp = new int[rightBound-leftPtr+1];
		// 创建temp数组,大小为左指针到指定的最右侧边界中间的大小
        int i = leftPtr;//前半截开头指针
        int j = rightptr;//后半截指针
        int k = 0;//temp的第一个位置

        while(i<=mid && j<=rightBound){
            temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
            //whether arr[i] <= arr[j],
            // yes:temp[k++] = arr[i++]
            // no:temp[k++] = arr[j++];
        }
        while (i<=mid) temp[k++] = arr[i++];
        while (j<=rightBound) temp[k++] = arr[j++];
		// 将剩余的数组元素放入temp中
        for (int m = 0;m<temp.length;m++) arr[leftPtr+m] = temp[m];
        //把temp放回arr


    }
    
    static void merage2(int[] arr,int leftPtr,int rightptr,int rightBound) {
        //优化1.0
        int mid = rightptr-1;
        int[] temp = new int[rightBound-leftPtr+1];

        int i = leftPtr;//前半截开头指针
        int j = rightptr;//后半截指针
        int k = 0;//temp的第一个位置

        while(i<=mid && j<=rightBound){
            temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
        }
        while (i<=mid) temp[k++] = arr[i++];
        while (j<=rightptr) temp[k++] = arr[j++];
        // j<=rightptr这是一个BUG
        // 在下一代优化中发现了这个BUG
        // 如果是<=rightptr,当右半边有剩余时,
        // 则j会检测不到(righrptr,rightBound)之间的数
        // 所以j<=rightBound

    }

    //以下是初始版本
    static void merage1(int[] arr){
        //优化0.0
        int mid = arr.length/2;
        int[] temp = new int[arr.length];

        int i = 0;//前半截开头指针
        int j = mid+1;//后半截指针
        int k = 0;//temp的第一个位置
        //三个指针,

        //优化1:
        while(i<=mid && j<=arr.length){
            if(arr[i]<=arr[j]){
                temp[k++] = arr[i++];
                //前半截和temp的指针都向后一位
            }else {
                temp[k++] = arr[j++];
            }
        }
        while (i<=mid) temp[k++] = arr[i++];
        while (j<arr.length) temp[k++] = arr[j++];
        //将剩下的部分都转移到temp上

        //优化2:
        while(i<mid && j<arr.length){
            temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
            //whether arr[i] <= arr[j],
            // yes:temp[k++] = arr[i++]
            // no:temp[k++] = arr[j++];
        }
        while (i<=mid) temp[k++] = arr[i++];
        while (j<arr.length) temp[k++] = arr[j++];
        //将剩下的部分都转移到temp上
        print(temp);
    }
}

快速排序

思路

  • 首先,需要设定一个基准pivot
  • 然后,确定左右边界(left设置开始,right设置为rightBound前一个,pivot为最后一个)
  • 接着,当left<=right并且left的值小于pivot时,从两边同时遍历,当遇到左值大于pivot右值小于pivot时,停止遍历并交换left和right指针上的值
  • 递归的使用该方法
  • 由于基准pivot两边大小大致分开,所以每次只需要在分好的两边分别再次试图用该方法就可以全部排序

演示

在这里插入图片描述

代码实现

public class QuickSort {
    static void print(int[] arr){
        for( int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }
    static void swap(int[] arr,int i,int j){
        //交换位置
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    public static void main(String[] args) {
        int[] arr = {1,4,7,8,3,6,9,2,5,10};
        //左右两半顺序对
        sort(arr,0,arr.length-1);
        print(arr);
    }
    public static void sort(int[] arr,int leftBound,int rightBound){
        if (leftBound>=rightBound) return;
        int mid = partition(arr,leftBound,rightBound);
        //基准左右都进行排序
        sort(arr,leftBound,mid-1);
        //排序范围在原最左和上一次的左指针之间
        sort(arr,mid+1,rightBound);
        //排序范围在上一次的左指针和原最右之间
        
    }
    static int  partition(int[] arr,int leftBound,int rightBound){
        int pivot = arr[rightBound];//轴
        int left = leftBound;
        int right = rightBound-1;
        while (left<=right){
            while (left<=right && arr[left]<=pivot) left ++;
            while (left<=right && arr[right]>pivot) right --;
            //左右向中间遍历
            if (left<right) swap(arr,left,right);
            //将交换left和right指针上的值
        }
        swap(arr,left,rightBound);
        //将基准放到他该去的位置
        return left;
        //返回左指针位置,意味着left之前的都是小于pivot的
    }

}

计数排序

是一种非比较排序
适用于特定问题,数据量特别大、且数据范围小的数据

思路

  • 统计数组(arr)中每种元素出现的个数
  • 存入另外的数组(count)中
  • 对count中的数组累加,建立累加数组
  • 反向填充回目标数组中:元素arr[i]放回数组第count[i]的位置,同时count[i]-1

演示

在这里插入图片描述

程序

import java.util.Arrays;
public class CountSort {
    /*计数排序
    * 非比较排序
    * 适用于特定问题,数据量特别大、且数据范围小的数据
    * */

    static void swap(int[] arr,int i,int j){
        //交换位置
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    public static void main(String[] args) {
        int[] arr = {2,4,3,1,1,0,0,5,6,9,8,5,7,4,0,9,8,2,6,4};
        int[] result = sort(arr);
        System.out.println(Arrays.toString(result));
    }
    static int[] sort(int[] arr){
        int[] result = new int[arr.length];//用于存放返回的数据,
        int[] count = new int[10];//桶
        for (int i = 0;i<arr.length;i++){
            count[arr[i]]++;
        }
        System.out.println(Arrays.toString(count));
        for (int i = 1;i<count.length;i++){
            count[i] = count[i]+count[i-1];
        }
        System.out.println(Arrays.toString(count));
        //这边返回的是累加数组,记录着每一个下标值对应的位置的最后位置
        
        //        for (int i = 0,j=0;i<count.length;i++){
        //            while (count[i]--> 0 ) result[j++] = i;
        //            //count减减后大于0
        //        }
        
        for (int i = arr.length-1;i>=0;i--){
            result[--count[arr[i]]] = arr[i];
        }
        return result;
    }
    
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值