八大排序算法原理及实现

插入排序

原理:从数组第二个元素开始,依次与第一个元素到当前元素的前一个元素比较,直到找到合适位置,移动、插入。

public static void insertSort(int[] arr){
        //用于交换的临时变量
        int temp;
        //从第二个元素起,依次从最左侧比较
        for(int i=1;i<arr.length;i++){
            for (int j=0;j<i;j++){
                //找到插入位置
                if(arr[i]<arr[j]){
                    temp=arr[i];
                    //从插入位置到该元素位置的前一个元素依次向后移动
                    while(i>j){
                        arr[i]=arr[i-1];
                        i-=1;
                    }
                    //插入
                    arr[i]=temp;
                    break;
                }
            }
        }
    }

希尔排序

也叫减小增量排序, 是对插入算法的优化,解决插入一个元素时,移动元素过多导致速度慢的问题。解决方法是(原理):根据增量把数组分为多个组,对每个分组进行插入排序。折半减小增量,直至为一,在数组基本有序的基础上最后再进行一次插入排序。
移位式:

	//插入式希尔排序
    public static void shellSort2(int[] arr){
        //插入元素时使用的临时变量
        int temp;
        //循环折半减小增量,直至增量为1
        for(int cap=arr.length/2;cap>0;cap/=2){
            //对所有子数组进行插入排序
            for(int i=0;i<cap;i++){
                //对每个子数组进行插入排序,增量为cap
                for(int j=i+cap;j<arr.length;j+=cap) {
                    //j用于循环,不能修改其值,因此用临时变量k
                    int k=j;
                    //从每个子数组的第二个元素开始比较插入
                    temp=arr[k];
                    while(k-cap>=0 && temp<arr[k-cap]){
                        //需要移动
                        arr[k]=arr[k-cap];
                        //继续向前比较
                        k-=cap;
                    }
                    //已经找到插入位置
                    arr[k]=temp;
                }
                //不需移动则直接排序下一个子数组
            }
            System.out.println("增量为"+cap+"排序后的子数组:"+Arrays.toString(arr));
        }
    }

交换式(效率比移位式低):

	//交换式希尔排序
    public static void shellSort1(int[] arr){
        //用于交换的临时变量
        int temp;
        //循环折半减小增量,直到为1,并对子数组排序
        for(int cap=arr.length/2;cap>0;cap/=2){
            //对每个子数组进行交换排序
            for(int i=0;i+cap<arr.length;i+=1){
                for(int j=i;j>=0;j-=cap) {
                    if (arr[j] > arr[j + cap]) {
                        //交换元素值
                        temp = arr[j];
                        arr[j] = arr[j + cap];
                        arr[j + cap] = temp;
                    }
                }
            }
            System.out.println("增量为"+cap+"排序后的子数组:"+Arrays.toString(arr));
        }
    }

选择排序

原理:n个数比较(n-1)轮,每轮求出一个最小或最大值,通常从前往后排序。

public static void selectSort(int[] arr){
        //元素值交换时的临时变量
        int temp;
        //n个数,比较n-1次
        for(int i=0;i<arr.length-1;i++){
            //从arr[i]后一个元素比较到最后一个元素
            for(int j=i+1;j<arr.length;j++){
                //找到新的最小元素,值交换
                if(arr[i]>arr[j]){
                    temp=arr[j];
                    arr[j]=arr[i];
                    arr[i]=temp;
                }
            }
        }
    }

冒泡排序

原理:两两元素相比较,每轮求出一个最小或最大值,通常从后往前排序。

public static void bubbleSort(int[] arr){
        //值交换时的临时变量
        int temp;
        //n个数比较n-1次
        for(int i=0;i<arr.length-1;i++){
            //两两元素相比较
            for(int j=0;j+1<arr.length-i;j++){
                //升序值大的往后“沉”
                if(arr[j]>arr[j+1]){
                    temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }
        }
    }

快速排序

原理:每轮比较选取一个中间值,使其左侧元素都不大于它,右侧元素都不小于它,再分别向左右递归快速排序,直到左右元素个数都为1。

	/*
    快速排序
    arr:待排序数组
    start:排序起始下标
    end:排序结束下标
     */
    public static void quickSort(int[] arr,int start,int end){

        //左下标
        int left=start;
        //右下标
        int right=end;
        //值交换时的中间变量
        int temp;
        //选取中间元素
        int midValue=arr[(start+end)/2];

        //使中间元素左侧的值都小于等于它,右侧的值都大于等于它
        while(left<right) {
            //在中间元素左侧找小于它的
            while (arr[left]<midValue){
                left+=1;
            }
            //在中间元素右侧找大于它的
            while (arr[right]>midValue){
                right--;
            }
            //判断左右下标的关系
            if(left>=right){
                //说明满足条件
                break;
            }
            //否则左右元素进行值交换
            temp=arr[left];
            arr[left]=arr[right];
            arr[right]=temp;
            //若交换后左侧元素与中间元素相等,右下标向前取,找不大于中间元素的元素进行值交换
            if(arr[left]==midValue){
                right-=1;
            }
            //若交换后右侧元素与中间元素相等,左下标向后取,找不小于中间元素的元素进行值交换
            if(arr[right]==midValue){
                left+=1;
            }
        }

        //若左右下标相等,防止左右递归时包含中间元素
        if(left==right){
            left+=1;
            right-=1;
        }

        //若中间元素左侧元素个数大于1个,需要排序
        if(start<right) {
            //向左递归快速排序
            quickSort(arr,start,right);
        }

        //若中间元素右侧元素个数大于1个,需要排序
        if(end>left){
            //向右递归快速排序
            quickSort(arr,left,end);
        }

    }

归并排序

原理:将数组递归分,直到元素个数为1,再合并为有序数组。

	//递归归并排序
    public static void mergeSortRecursion(int[] arr){
        if(arr.length<1){
            System.out.println("数组为空!");
            return;
        }else if(arr.length==1){
            System.out.println("分:"+Arrays.toString(arr));
            return;
        }else{
            //向左递归分
            System.out.println("分:"+Arrays.toString(arr));
            int[] leftArr= Arrays.copyOfRange(arr,0,arr.length/2);
            mergeSortRecursion(leftArr);
            //向右递归分
            int[] rightArr=Arrays.copyOfRange(arr,arr.length/2,arr.length);
            mergeSortRecursion(rightArr);
            //用于合的临时数组
            //int[] tempArr=new int[leftArr.length+rightArr.length];
            //将两个有序的数组合并成一个有序的数组
             for(int i=0,j=0,k=0;k<arr.length;k++){
                 if(i<leftArr.length && j<rightArr.length) {
                     //两个数组都没有比到最后一个元素时
                     if (leftArr[i] < rightArr[j]) {
                         arr[k] = leftArr[i];
                         i++;
                     } else {
                         arr[k] = rightArr[j];
                         j++;
                     }
                 }else {
                     //某个数组已经比较完毕
                     if(i<leftArr.length){
                         arr[k]=leftArr[i];
                         i++;
                     }else {
                         arr[k]=rightArr[j];
                         j++;
                     }
                 }
             }
            System.out.println("合:"+Arrays.toString(arr));
        }
    }

基数排序

原理:稳定排序(相同元素,排序后的顺序与排序前的顺序一致)。从个位到最大元素的最高位依次排序,把元素放入对应的一维数组中,再用一个一维数组记录每轮排序中,每个一维数组中的有效数字,直至最高位排序结束后,依次读取每个一维数组中的有效数字。

//基数排序
    public static void radixSort(int[] arr){
        //用于存储待排序数组中元素的二维数组
        int[][] bucket=new int[10][arr.length];
        //用于记录每轮排序后,bucket中每个一维数组中的有效元素个数
        int[] validElementsCounts=new int[10];
        //从个位开始提取数值进行比较,加入bucket中对应索引的一维数组中,直到最高位
        for (int i = 10; getMax(arr)/(i/10) != 0 ; i*=10) {
            //每轮排序前初始化validElementsCounts
            for (int j = 0; j < 10; j++) {
                validElementsCounts[j]=0;
            }
            //遍历待排序数组,并加入对应的一维数组中
            for (int j = 0; j < arr.length; j++) {
                //元素应该加入的一维数组在bucket中的下标(一个元素数值的个位、十位、百位......)
                int index=arr[j] % i / (i/10);
                //把当前元素加入到一维数组中
                bucket[index][validElementsCounts[index]]=arr[j];
                //该一维数组的有效数字加一
                validElementsCounts[index]=validElementsCounts[index]+1;
            }
            //把每一轮的排序结果赋值给原数组
            for (int j = 0,l = 0; j < 10; j++) {
                for (int k = 0; k < validElementsCounts[j]; k++) {
                    arr[l]=bucket[j][k];
                    l++;
                }
            }
            //输出此轮排序后的数组
            System.out.println(Arrays.toString(arr));
        }
    }

    //返回数组中最大值
    public static int getMax(int[] arr){
        int max=arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(max<arr[i]){
                max=arr[i];
            }
        }
        return max;
    }

堆排序

原理:构建大顶堆或小顶堆,交换根节点与数组最后一个元素。重复构建、交换,直至数组有序。

	//堆排序
    public static void heapSort(int[] arr){
        int temp;//交换时的临时节点
        /*
        首次构建大顶堆:
        首次构建大顶堆是从最后一个非叶子节点逐级向上构建,直至根节点。
        构建完成后每轮交换只改变大顶堆根节点的值。
        根节点以下还是排好序的大顶堆,因此不需要从最后一个非叶子节点重新逐级构造。
        只需要从根节点开始构造,提高排序速度。
         */
        for (int j = arr.length/2-1 ; j >= 0; j-- ) {
            adjustHeap(arr,j,arr.length);
        }
        //每次构建完成的大顶堆的根节点与数组最后一个元素交换
        for (int i = arr.length-1; i > 0; i--) {
            temp=arr[0];
            arr[0]=arr[i];
            arr[i]=temp;
            adjustHeap(arr,0,i);//不是首次构建大顶堆只需要从根节点开始构建,因为根节点以下已经是排好序的大顶堆了
        }
    }

    //将以下标为i的元素做为根节点的子树(局部数组)构建成大顶堆
    /*
    arr:待调整数组
    i:非叶子节点在数组中的索引
    length:数组中需要构建大顶堆的元素个数
     */
    public static void adjustHeap(int[] arr,int i,int length){

        //取出当前元素的值,保存在临时变量中
        int temp=arr[i];
        //k表示下标为i的节点的左子节点的下标
        for (int k = 2*i+1; k < length; k=2*i+1) {
            if(k+1<length && arr[k]<arr[k+1]){//存在右子节点,且左子节点小于右子节点
                k++;//k变为右子节点的下标
            }
            if(arr[k]>temp){//如果子节点大于父节点
                //交换父节点与子节点的值,保证父节点为最大值
                arr[i]=arr[k];
                arr[k]=temp;
                i=k;//i指向k,继续循环比较,因为虽然下面的子树是排好序的,但是此处的改变可能会导致其子树也需更改
            }else {
                /*
                堆排序构建堆的方式是:从左往右、从下往上构建堆。
                如果以当前节点(arr[i])作为根节点的子树不许要改变,
                则直接退出循环,因为下面的子树都是已经排好序的
                 */
                break;
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值