【排序算法】

比较排序

七大排序算法

image-20221107091051071

❤️稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的

插入排序

⭕️插入排序的思想:从第二个元素开始往后遍历,遍历到每个元素都把该元素插入该元素之前的序列中,并保持序列有序性。

比如将第二个元素插入第二个元素之前的序列并保持有序性,然后把第三个元素插入第三个元素之前的序列,并保持有序性,到此前三个元素已经有序,然后把第四个插入前三个元素,并保持有序性,依次类推,直到所有元素都有序。

//插入排序(由小到大)
//时间复杂度:O(N*N) 空间复杂度:O(1)
public static void insertSort(int[] arr){
   
    for (int i = 1; i < arr.length; i++) {
   
        int j = 0;
        int cur = arr[i];
        for (j = i - 1; j >=0; j--) {
   
            if (cur < arr[j]){
   
                arr[j+1] = arr[j];
            }else {
   
                arr[j+1] = cur;
                break;
            }
        }
        if(j == -1){
   
            arr[0] = cur;
        }
    }
}

希尔排序

⭕️希尔排序的思想:希尔排序是插入插入排序的优化算法。希尔排序是先取一个整数gap,意思是将整个序列分为gap组(下面的图会演示具体是怎么分组的),然后针对每个组进行插入排序,这一趟下来,序列就会稍微有序点,然后gap/=2,然后再针对每个组进行插入排序,然后序列就会更有序点。然后gap/=2,再进行下一轮。直到gap==1,然后再对序列整体来个插入排序(这次就不分组了)

⭕️当gap>1时都是预排序,预排序的目的是让序列变得更有序,只有最后一次gap==1时这次的插入排序才能让序列真正的有序。如果不分组排序,直接一上来就插入排序,当数据量比较大时,比较次数就会很多。分组就能减少比较次数。

⭕️希尔排序又称为缩小增量排序,gap就是那个增量。希尔排序的时间复杂度并不好计算,因为增量的取值不确定。资料上显示的时间复杂度是:O(N1.25)到O(1.6*N1.25)

image-20221107090551685

private void shell(int[] arr, int gap){
   
    for (int i = gap; i < arr.length; i++) {
   
        int cur = arr[i];
        int j = 0;
        for (j = i - gap; j >= 0 ; j -= gap) {
   
            if (cur < arr[j]){
   
                arr[j+gap] = arr[j];
            }else {
   
                break;
            }
        }
        arr[j+gap] = cur;
    }
}
//希尔排序
public void shellSort(int[] arr){
   
    int gap = arr.length;
    while (gap > 1){
   
        shell(arr, gap);
        gap/=2;
    }
    shell(arr,1);
}

选择排序

⭕️选择排序的思想:每次选取未排序序列中最小值放到最前面,第一次选最小值放到0下标处,第二次选剩余序列中最小值放到1下标处,第三次选剩余序列中最小值放到2下标处。依次往后遍历。

public static void swap(int[] arr,int index1,int index2){
   
    int tmp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2] = tmp;
}
//选择排序,选择排序不管数据是有序还是无序,都要比较这么多次。所以该算法比较慢
//时间复杂度:O(N*N) 空间复杂度:O(1)
public static void selectSort(int[] arr){
   
    for (int i = 0; i < arr.length; i++) {
   
        int minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
   
            if(arr[j] < arr[minIndex]){
   
                minIndex = j;
            }
        }
        swap(arr,minIndex,i);
    }
}

时间复杂度:O(N * N),空间复杂度: O(1)

堆排序

⭕️堆排序思想:比如要排成递增序列,要先将无序序列建成一个大根堆,然后第一次取堆顶元素和最后一个元素交换,然后再调整为大根堆(调整的范围不包括已经交换下来的元素),第二次取堆顶元素和倒数第二个元素交换,然后再调整为大根堆(调整的范围不包括已经交换下来的元素)

⭕️第一次交换放到最后位置是最大的元素,第二次交换放到倒数第二位置的是第二大元素,第三次交换放到倒数第三位置的是第三大元素,依次类推,就可以排序

🔑总共两个大步骤:建堆,交换和向下调整

/**
 *
 * @param arr:向下调整的堆
 * @param root 向下调整的子树的根节点
 * @param len 调整的界限(调整的数据下标<len)
 */
public static void shiftDown(int[] arr,int root,int len){
   
    int parent = root;
    int child = root*2+1; //左孩子
    while(child < len){
   
        //让child指向最大的孩子节点
        if(child + 1 < len && arr[child+1] > arr[child]){
   
            child++;
        }
        //孩子比父亲大就交换
        if(arr[child] > arr[parent]){
   
            swap(arr,child,parent);
            parent = child;
            child = parent * 2 + 1;
        }else{
   
            break;
        }
    }
}
//堆排序
public static void heapSort(int[] arr){
   
    //建大根堆
    for (int i = (arr.length-2)/2; i >=0 ; i--) {
   
        shiftDown(arr,i,arr.length);
    }
    //排序
    int end = arr.length - 1;
    while (end >= 0){
   
        swap(arr,end,0);
        shiftDown(arr,0,end);
        end--;
    }
}

堆排序时间复杂度:O(N*logN) 空间复杂度:O(1)

🔑==堆排序时间复杂度分析:==堆排序有两大步骤,建堆和操作堆。建堆之前在堆那一节已经分析过了,时间复杂度是:O(N)。下面主要来分析操作堆:

以满二叉树来分析:最下一层元素交换到堆顶之后的向下调整次数:h-1(最坏情况下是h-1,h是树的高度),倒数第二层是:h-2,倒数第三层是h-3,第一层是:0

S=(2 ^ 0 * 0) + (2 ^ 1 * 1) + (2 ^ 2 * 2) + (2 ^ 3 * 3) +…+ (2 ^ h-2 * (h-2)) + (2 ^ h-1 * (h-1))

2S=(2 ^ 1 * 0) + (2 ^ 2 * 1) + (2 ^ 3 * 2) + (2 ^ 4 * 3) +…+ (2 ^ h-1 * (h-2)) + (2 ^ h * (h-1))

相减得:- (2 ^ 1+ 2 ^ 2 + 2 ^ 3 + 2 ^ h-1)+ (2 ^ h * (h-1)-0 ,化简后可得时间复杂度是O(n*logn-2n),上面建堆是O(N),相加得O(N * logN-N),再化简得O(N * logN)

冒泡排序

⭕️==冒泡排序思想:==每次遍历将当前序列中最大值交换至最右侧,第一次遍历将前n个元素中最大值交换至最右侧,第二次将前n-1个元素中最大值交换至右侧第二个位置,第三次将前n-2个元素中最大值交换至右侧第三个位置。循环遍历n-1次即可将序列排序。

public static void swap(int[] arr,int index1,int index2){
   
    int tmp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2] = tmp;
}
public static void bubbleSort(int[] arr){
   
    for (int i = 0; i < arr.length - 1; i++) {
   
        boolean flg = false;
        for (int j = 0; j < arr.length - 1 - i; j++) {
   
            if (arr[j] > arr[j+1]){
   
                flg = true;
                swap(arr,j,j+1);
            }
        }
        if (!flg){
   
            return;
        }
    }
}

🔑时间复杂度:O(N * N),空间复杂度:O(1)

快速排序

⭕️快速排序是采用分治的思想,使用递归的方式来排序,快排的思想:先从当前序列中选取一个基准,然后把比基准小的数字放到基准左面,比基准大的数字放到基准右面。然后再分别对分出的左右序列采取相同的思想。

根据基准划分左右序列的算法以下两种都可以:

private static void insert1Sort(int[] arr,int left,int right){
   
    for (int i = left + 1; i <= right; i++) {
   
        int cur = arr[i];
        int j = i - 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值