5.冒泡+选择+插入+希尔

一、排序算法

排序基础
1.排序算法的稳定性在这里插入图片描述
2.何为原地排序算法
在这里插入图片描述

1.冒泡排序

在这里插入图片描述
从下面第一个元素开始往上冒泡,一轮冒泡下来,最大的元素就去了最上面了
在这里插入图片描述
步骤:无序数组
在这里插入图片描述
每次冒泡都可以将最大的元素放到最右边去
第一轮比较了5次:
在这里插入图片描述
第二轮比较了4次:
在这里插入图片描述
第三轮比较了3次:
在这里插入图片描述
。。。
第四轮比较了2次
。。。
在这里插入图片描述
有6个元素比较了6次,第一次比较了5次,第二次4次。。。
code:
两次遍历,第一次控制冒泡的轮数,第二次控制每次比较次数

public class BubbleSorter {
    public void sort(int[] data){
        if(data == null || data.length <=1) return;
        for (int round = 1; round <= data.length; round++) { //控制冒泡的轮数
            int compareTimes = data.length - round;
            for (int i = 0; i < compareTimes; i++) {   //控制每轮比较次数
                if(data[i]>data[i+1]){
                    int tmp = data[i];
                    data[i] = data[i+1];
                    data[i+1] = tmp;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] data = new int[]{12,23,36,9,24,42};
        new BubbleSorter().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

特点

  • 时间复杂度分析:
    时间复杂度:O(n²)
    在这里插入图片描述
    在这里插入图片描述
  • 空间复杂度为O(1),原地排序算法
  • 冒泡排序是稳定的排序算法
    在这里插入图片描述
    只有大于才进行交换,两个36的位置不会改变
    优化:减少元素交换次数
    在这里插入图片描述
    在这里插入图片描述
    在第二轮中已经是有序的了,就不需要再进行第三轮、第四轮交换了

代码实现

public class BubbleSorter {
    public void sort(int[] data){
        if(data == null || data.length <=1) return;
        
        for (int round = 1; round <= data.length; round++) { //控制冒泡的轮数
            boolean hasSwap = false;
            int compareTimes = data.length - round;
            for (int i = 0; i < compareTimes; i++) {   //控制每轮比较次数
                if(data[i]>data[i+1]){
                    int tmp = data[i];
                    data[i] = data[i+1];
                    data[i+1] = tmp;
                    hasSwap = true;
                }
            }
            //如果没有发生交换就可以退出了
            if(!hasSwap){
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] data = new int[]{12,23,36,9,24,42};
        new BubbleSorter().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

2. 选择排序

重在选择,每次选择出来一个最小的元素和前面的元素进行交换
选择排序的步骤:
1.找到数组中最小的数组元素
2.将这个最小的数组元素和数组第一个元素进行交换
3.在剩下元素中找到最小的数组元素和数组第二个元素进行交换
。。。
假设第一个元素是最小值,在剩下元素里面找最小值,还需要一个指针 j 来遍历数组找最小值,和 i 进行比较
在这里插入图片描述
遍历到12的时候,12比第一个元素值小,可能是最小值,将其索引放到minNumIndex中
在这里插入图片描述
第一轮遍历完成后就可以找到数组中最小的元素值的索引,存放在minNumIndex中,将这个索引指向的元素和指针i 指向的元素进行交换
第二轮:
在这里插入图片描述
在这里插入图片描述
第三轮:
在这里插入图片描述
在这里插入图片描述
第三轮,找到最小元素值的索引为4,将其和 i 指针指向元素进行交换
。。。
在这里插入图片描述
数组有多少个元素就需要进行多少轮的排序
时间复杂度:O(n²)
在这里插入图片描述
code:
将交换元素的方法抽取为父类

public class Sorter {
    public void swap(int[] nums,int i,int j){
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}

代码实现

public class SelectionSort extends Sorter{
    public void sort(int[] data){
        if(data == null || data.length<=1) return;

        for (int i = 0; i < data.length; i++) { //控制选择排序的轮数
            //找到[i,n)中的最小元素所在的索引  minNumIndex用来存放最小值的索引
            int minNumIndex = i;
            //j从i+1开始遍历数组,找到最小元素值
            for (int j = i+1; j < data.length; j++) {
                if(data[j]<data[minNumIndex]){
                    minNumIndex = j;
                }
            }
            //将data[i]和最小元素进行交换
            swap(data,i,minNumIndex);
        }
    }

    public static void main(String[] args) {
        int[] data = new int[]{12,23,36,9,24,42};
        new SelectionSort().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

特点

  • 空间复杂度:O(1),原地排序算法 时间复杂度:O(n²)
  • 选择排序是不稳定的排序算法
    在这里插入图片描述

3. 插入排序

将每一个元素插入到已经有序的数组的位置
在这里插入图片描述
核心思想:取未排序区间的元素在已排序区间中找到合适的插入位置将其插入,并保证已排序区间的数据一直有序,重复这个过程直到未排序区间没有元素为止
一般是假设第一个元素为已排序元素
在这里插入图片描述
插入排序的步骤:有n个元素就进行n轮
第一轮:取第一个元素34,这个元素已经有序
在这里插入图片描述
第二轮:
在这里插入图片描述
从第二个元素开始,对比 j 指向元素和 j-1 元素,如果比 j-1 元素值要小,则进行交换
在这里插入图片描述
在这里插入图片描述
第三轮:从第三个元素开始,比前一个元素,位置正确不用动
在这里插入图片描述
第四轮:从第四个元素开始,比前一个元素小,要向前移动
在这里插入图片描述
在这里插入图片描述
进行交换,注意此时,j-1 和 j 需要继续向前移动
第五轮:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第六轮:
在这里插入图片描述

拿到未排序区间的元素,一直往前找,找到合适的位置将这个元素插入进去,一直到未排序区间为空,就表示这个数组排序完成了
时间复杂度:
遍历一遍数组,时间复杂度为O(n)
每轮遍历需要将一个元素插入到有序数组的正确位置这个插入过程的最坏时间复杂度为O(n)
根据乘法法则:O(n²)
code:

代码实现(优化前)

public class InsertionSorter extends Sorter{
    public void sort(int[] data){
        if(data==null||data.length<=1)return;

        for (int i = 1; i < data.length; i++) { //控制插入排序的轮数,第一个元素已经默认有序,可以存第二个开始
            for (int j = i; j > 0 ; j--) {  //j从i开始向前移动,和j-1进行判断,j-1不能小于0,j-1也不能等于0,不然就变成data[-1]了
                if(data[j] < data[j-1]){
                    swap(data,j,j-1);
                }else{
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] data = new int[]{12,23,36,9,24,42};
        new InsertionSorter().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

特点

  • 空间复杂度是O(1),时间复杂度为O(n²),原地排序算法
  • 插入排序是稳定的排序算法,可以选择不去进行交换,只有小于的时候才去交换
    在这里插入图片描述

代码实现(优化后)

插入排序的优化:
不使用元素交换,使用赋值代替元素交换,减少元素访问次数
可以将有序数组中较大的元素总是向右移动,然后将元素插入到正确的位置

//优化
public void sort1(int[] data){
    if(data==null||data.length<=1)return;

    for (int i = 1; i < data.length; i++) { //控制插入排序的轮数,第一个元素已经默认有序,可以存第二个开始
        int tmp = data[i];
        int j;
        for (j = i; j > 0 ; j--) {  //j从i开始向前移动,和j-1进行判断,j-1不能小于0,j-1也不能等于0,不然就变成data[-1]了
            if(tmp < data[j-1]){
                //将较大的元素向后移动
                data[j] = data[j-1];
            }else{
                break;
            }
        }
        //找到i 对应的元素需要插入的位置
        data[j] = tmp;
    }
}

冒泡、选择、插入排序性能对比

时间复杂度都是O(n²)
通过比较交换次数来比较性能

package com.douma.line.algo.sort;

import java.util.Random;

/**
 * 对比冒泡、选择、插入排序性能
 */
public class SortCompare {
    private static Random random = new Random();

    private static int[] getData(int n){
        int[] data = new int[n];
        for (int i = 0; i < n; i++) {
            data[i] = random.nextInt();
        }
        return data;
    }

    private static long time(String sortType,int[] data){
        long start = System.currentTimeMillis();

        if(sortType.equals("bubble"))new BubbleSorter().sort(data);
        else if(sortType.equals("selection"))new SelectionSort().sort(data);
        else if(sortType.equals("insertion"))new InsertionSorter().sort(data);

        return System.currentTimeMillis() - start;
    }

    private static long manyTimeSort(String sortType,int n,int k){
        long totalTime = 0;
        for (int i = 0; i < k; i++) {
            totalTime += time(sortType,getData(n));
        }
        return totalTime;
    }

    public static void main(String[] args) {
        double t1 = manyTimeSort("bubble",1000,100);
        double t2 = manyTimeSort("selection",1000,100);
        double t3 = manyTimeSort("insertion",1000,100);
        System.out.println(t1 / t2);    //t1 > t2
        System.out.println(t2 / t3);    //t2 > t3

        //结论:插入排序性能最好(稳定),其次是选择排序(不稳定),最后是冒泡排序(稳定)
    }
}

推荐使用插入排序,性能最好,是稳定的
插入排序有两种实现方式,一种是基于交换实现的;另一种是基于赋值实现的;赋值的效率要比交换效率要高

4. 希尔排序

什么是希尔排序?
是对插入排序的优化
如果有一个大规模乱序数组插入排序很慢,因为它只会交换相邻的两个元素
在这里插入图片描述
这个大规模数组在进行若干次插入排序后,前面已经有序了,最后一个是最小的元素,这个时候需要将最小元素一步步的向前比较移动,
在这里插入图片描述
这个过程会非常慢
希尔排序就可以解决大规模数组插入排序慢的问题

希尔排序的思想:
先使数组中任间隔为h(4)的元素有序,然后对全局(也就是h=1)进行排序
希尔排序递增序列计算公式
间隔h的取值逻辑
在这里插入图片描述
在这里插入图片描述
最常用的是:
在这里插入图片描述
递增序列的取值:
在这里插入图片描述
使用公式计算得出h取值,k取1,2,3,4,5带入公式计算出h取值,h应该小于N/3

public class ShellSorter {
    public void sort(int[] data){
        if(data == null || data.length <=1)return;

        //1.计算递增序列
        int n = data.length;
        ArrayList<Integer> list = new ArrayList<>();
        int k = 1;
        int h;
        do{
            h = (int)(Math.pow(3,k)-1)/2;
            if(h > n/3)break;
            list.add(h);    // 1,4,13,40,121....
            k++;
        }while (h <= n/3);
    }
}

倒序遍历递增序列

//2.希尔排序
for (k = list.size()-1; k >=0 ; k--) {  //倒序遍历递增序列
    h = list.get(k);
    // 将数组变为 h 有序

}

希尔排序详细步骤
希尔排序过程 h = 4,经过此次排序,间隔为4的元素就局部有序了
全局排序 h =1

代码实现

public class ShellSorter extends Sorter{
    public void sort(int[] data){
        if(data == null || data.length <=1)return;

        //1.计算递增序列
        int n = data.length;
        ArrayList<Integer> list = new ArrayList<>();
        int k = 1;
        int h;
        do{
            h = (int)(Math.pow(3,k)-1)/2;
            if(h > n/3)break;
            list.add(h);    // 1,4,13,40,121....
            k++;
        }while (h <= n/3);

        //2.希尔排序
        for (k = list.size()-1; k >=0 ; k--) {  //倒序遍历递增序列
            h = list.get(k);
            // 将数组变为 h 有序
            for (int i = h; i < n; i++) { //倒序遍历
                for (int j = i; j >= h ; j=j-h) {  
                    if(data[j] < data[j-h]){
                        swap(data,j,j-h);
                    }else{
                        break;
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] data = new int[]{12,23,36,9,24,42};
        new ShellSorter().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

希尔排序对比插入排序性能
性能要比插入排序好,具体要看递增序列h的取值,根据公式获取

特点

希尔排序的特点

  • 空间复杂度:O(1),原地排序算法
  • 希尔排序是不稳定的排序算法
    在这里插入图片描述
  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是使用Python实现对输入数据进行排序并输出预期结果的代码: ``` # 直接插入排序 def insert_sort(nums): for i in range(1, len(nums)): j = i - 1 key = nums[i] while j >= 0 and nums[j] > key: nums[j+1] = nums[j] j -= 1 nums[j+1] = key return nums # 折半插入排序 def binary_insert_sort(nums): for i in range(1, len(nums)): key = nums[i] left, right = 0, i-1 while left <= right: mid = (left + right) // 2 if nums[mid] > key: right = mid - 1 else: left = mid + 1 for j in range(i-1, left-1, -1): nums[j+1] = nums[j] nums[left] = key return nums # 希尔排序 def shell_sort(nums): n = len(nums) gap = n // 2 while gap > 0: for i in range(gap, n): j = i while j >= gap and nums[j-gap] > nums[j]: nums[j-gap], nums[j] = nums[j], nums[j-gap] j -= gap gap //= 2 return nums # 冒泡排序 def bubble_sort(nums): for i in range(len(nums)-1): for j in range(len(nums)-i-1): if nums[j] > nums[j+1]: nums[j], nums[j+1] = nums[j+1], nums[j] return nums # 快速排序 def quick_sort(nums): if len(nums) <= 1: return nums pivot = nums[len(nums)//2] left, right, equal = [], [], [] for num in nums: if num < pivot: left.append(num) elif num > pivot: right.append(num) else: equal.append(num) return quick_sort(left) + equal + quick_sort(right) # 简单选择排序 def select_sort(nums): for i in range(len(nums)-1): min_index = i for j in range(i+1, len(nums)): if nums[j] < nums[min_index]: min_index = j nums[i], nums[min_index] = nums[min_index], nums[i] return nums # 输入数据 nums = list(map(int, input().split())) # 输出排序结果 print("1. 直接插入排序", insert_sort(nums)) print("2. 折半插入排序", binary_insert_sort(nums)) print("3. 希尔排序", shell_sort(nums)) print("4. 冒泡排序", bubble_sort(nums)) print("5. 快速排序", quick_sort(nums)) print("6. 简单选择排序", select_sort(nums)) ``` 注意,以上代码仅供参考,实际使用时还需要考虑输入数据的范围、数据类型等因素,并对代码进行测试和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懿所思

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值