【收藏】排序算法总结

本文详细介绍了几种常见的排序算法,包括冒泡排序、选择排序、插入排序等简单排序算法,以及希尔排序、归并排序、快速排序等高级排序算法。不仅阐述了每种排序算法的工作原理,还提供了具体的代码实现,并对每种算法的时间复杂度、空间复杂度及稳定性进行了深入分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序算法总结

一、排序算法总结

二、简单排序

2.1 冒泡排序
  1. 排序原理:

    1)比较相邻的元素。如果前一个元素比后一个元素大,就交换这两个元素的位置。

    2)对每一对相邻元素做同样的工作,从开始第一对元素到末尾的最后一对元素。最终最后位置的元素就是最大值。

  2. 代码实现

    public class Bubble{
        public static void sort(Comparable[] a){
            for(int i = a.length - 1; i > 0; i--){
                for(int j = 0; j < i; j++){
                    if(greater[a[j], a[j + 1]]){
                        swap(a, j, j + 1);
                    }
                }
            }
        }
        private static boolean greater(Comparable v, Comparable w){
            return v.compareTo(w) > 0;
        }
        private static void swap(Comparable[] a, int i, int j){
            Comparable t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }
    
  3. 复杂度分析

    • 时间复杂度

      元素比较次数:n*(n-1)/2,元素交换次数:n*(n-1)/2,总执行次数:n2-n,

      时间复杂度:O(n2)

    • 空间复杂度

      O(1)

    • 稳定性

      稳定,因为相等的时候不交换位置。

2.2 选择排序
  1. 排序原理

    1)每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引处的值为最小值,记录该索引值

    2)交换第一个索引值和记录的索引值

  2. 代码实现

    public class Selection{
        public static void sort(Comparable[] a){
            for(int i = 0; i < a.length - 1; i++){
                int minIndex = i;
                for(int j = i + 1; j < a.length; j++){
                    if(greater(a[minIndex], a[j])){
                        minIndex = j;
                    }
                }
                swap(a, i, minIndex);
            }
        }
        public static void greater(Comparable v, Comparable w){
            return v.compareTo(w) > 0;
        }
        public static void swap(Comparable[] a, int i, int j){
            Comparable t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }
    
  3. 复杂度分析

    • 时间复杂度

      数据比较次数:n*(n-1)/2,数据交换次数:n-1,时间复杂度:O(n2)

    • 空间复杂度

      O(1)

    • 稳定性

      不稳定,排序过程中有当前选则的最小元素进行交换时,发生原来相等元素的位置顺序改变。

2.3 插入排序
  1. 排序原理

    1)把所有的元素分为两组,已经排序的和未排序的

    2)找到未排序的组中的第一个元素,向已经排序的组中进行插入

    3)倒叙遍历已经排序的元素,依次和待插入元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素放到这个位置,其他的元素向后移动一位。

  2. 代码实现

    public class Insertion{
        public static void sort(Comparable[] a){
            for(int i = 1; i < a.length; i++){
                for(int j = i; j > 0; j--){
                    if(greater(a[j - 1], a[j])){
                        swap(a, j - 1, j);
                    }else{
                        break;
                    }
                }
            }
        }
        public static void greater(Comparable v, Comparable w){
            return v.compareTo(w) > 0;
        }
        public static void swap(Comparable[] a, int i, int j){
            Comparable t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }
    
  3. 复杂度分析

    • 时间复杂度

      数据比较次数:n*(n-1)/2,数据交换次数:n*(n-1)/2,总执行次数:n2-n,

      时间复杂度:O(n2)

    • 空间复杂度

      O(1)

    • 稳定性

      稳定,相等时,不改变元素位置顺序。

三、高级排序

3.1 希尔排序
  1. 排序原理

    1)选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组

    2)对分好组的每一组数据完成插入排序

    3)减小增长量,最小减为1,重复第二部操作

  2. 代码实现

    public class Shell{
        public static void sort(Comparable[] a){
            int N = a.length;
            int h = 1;
            while(h < N / 2){
                h = h * 2 + 1;
            }
            while(h >= 1){
                for(int i = h; i < N; i++){
                    for(int j = i; j >= h; j -= h){
                        if(greater(a[j - h], a[j])){
                            swap(a, j, j - h);
                        }else{
                            break;
                        }
                    }
                }
            }
        }
        public static void greater(Comparable v, Comparable w){
            return v.compareTo(w) > 0;
        }
        public static void swap(Comparable[] a, int i, int j){
            Comparable t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }
    
    //从本地读取文件测试希尔排序
    public static void main(String[] args)throws Exception{
        ArrayList<Integer> list = new ArrayList<>();
        //读取本地文件
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("rest.txt")));
        String line = null;
        while((line = reader.readLine()) != null){
            //把每一个数字存入到集合中
            list.add(Integer.valueOf(line));
        }
        reader.close();
        //把集合转换成数组
        Integer[] arr = new Integer[list.size()];
        list.toArray(arr);
        
        long start = System.currentTimeMillis();
        Shell.sort(arr);
        long end = System.currentTimeMillis();
        System.out.println("使用希尔排序耗时:"+(end-start)); 
    }
    
  3. 复杂度分析

    • 时间复杂度

      对于不同的递增序列可能导致不同的事前估计值,应该采用事后分析法。

    • 空间复杂度

      O(1)

    • 稳定性

      不稳定,多次插入排序有可能移动原来相等元素的位置顺序。

3.2归并排序
  1. 排序原理——分治思想

    1)将数据拆分成两组,再递归的对子组继续拆分,直到每个子组中的元素个数为1;

    2)将相邻的子组合并成一个更大的有序子组;

    3)重复步骤2),直到将整组数组排好序。

  2. 代码实现

    public class Merge {
        private static Comparable[] assist;     //辅助数组
        public static void sort(Comparable[] a){
            assist = new Comparable[a.length];
            int low = 0;
            int high = a.length - 1;
            sort(a, low, high);
        }
        private static void sort(Comparable[] a, int low, int high) {
            if(low >= high){
                return;
            }
            int mid = (low + high) / 2;
            sort(a, low, mid);
            sort(a, mid + 1, high);
            merge(a, low, mid, high);
        }
        private static void merge(Comparable[] a, int low, int mid, int high) {
            int i = low;        //assist数组移动指针
            int left = low;       //左半部分指针,初始指向首元素
            int right = mid + 1;    //右半部分指针,初始指向mid+1位置元素
            while (left <= mid && right <= high){
                if(less(a[left], a[right])){
                    assist[i++] = a[left++];
                }else {
                    assist[i++] = a[right++];
                }
            }
            //左边部分未遍历完
            while (left <= mid){
                assist[i++] = a[left++];
            }
            //右边部分未遍历完
            while (right <= high){
                assist[i++] = a[right++];
            }
            //将排好序的辅助数组复制到目标数组中
            System.arraycopy(assist, 0, a, 0, high);
        }
        private static boolean less(Comparable v, Comparable w) {
            return v.compareTo(w) < 0;
        }
    }
    
  3. 复杂度分析

    • 时间复杂度

      采用了分治策略,递归的拆分子组,时间复杂度为O(nlogn),证明见数据结构与算法一书。

    • 空间复杂度

      O(n),因为借助了辅助数组,典型的空间换时间。

    • 稳定性

      稳定,只有不等(本例为小于)时才会交换位置,相等元素不改变位置顺序。

3.3 快速排序
  1. 排序原理

    1)首先设定一个分界值,通过该分界值将数组划分为左右两部分;

    2)将大于或等于该分界值的数据放到数组右边,小于的放到数组左边;

    3)递归对左右两边进行排序,重新选取分界值,重新划分;

    4)重复上述处理,直到整体有序。

    划分原理:

    1)确定一个基准值,用两个指针分别指向数组的首尾;

    2)分别从首尾开始探寻,从首部开始的,寻找比基准值大的,从尾部开始的,寻找比基准值小的,一旦找到立即停止探寻;——保证数组左边小于基准值,右边大于基准值

    3)交换两指针元素;

    4)重复2),3)步骤,直到左指针大于右指针。

  2. 代码实现

    public class Quick {
        public static void sort(Comparable[] a){
            int low = 0;
            int high = a.length - 1;
            sort(a, low, high);
        }
        private static void sort(Comparable[] a, int low, int high) {
            if(low >= high){
                return;
            }
            //对当前数组进行划分
            int partition = partition(a, low, high);
            sort(a, low, partition);
            sort(a, partition + 1, high);
        }
        private static int partition(Comparable[] a, int low, int high) {
            Comparable baseNum = a[low];        //默认把最左边元素当做基准值
            int left = low;
            int right = high + 1;
            //划分
            while (true){
                //先从右往左扫描,找到一个比基准值小的元素
                while (less(baseNum, a[--right])) {
                    if(right == low){
                        break;  //当扫描到最左边时,停止扫描
                    }
                }
                //从左往右扫描,找到一个比基准值大的元素
                while (less(a[++left], baseNum)){
                    if(left == high){
                        break;
                    }
                }
                if(left >= right){
                    break;          //已经扫描完所有元素
                }else{
                    swap(a, left, right);
                }
            }
            swap(a, low, right);
            return right;
        }
        private static boolean less(Comparable v, Comparable w) {
            return v.compareTo(w) < 0;
        }
    	public static void swap(Comparable[] a, int i, int j){
            Comparable t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }
    
  3. 复杂度分析

    • 时间复杂度

      快速排序的一次切分从两头开始交替搜索,直到left和right重合,因此,一次切分算法的时间复杂度为O(n),但整个快速排序的时间复杂度和切分的次数相关。

      最优情况下快速排序的时间复杂度为O(nlogn);最坏情况下,快速排序的时间复杂度为O(n2);平均时间复杂度为O(nlogn)。

    • 空间复杂度

      O(n)或者O(logn),因为递归造成栈空间的开销,最优(递归深度)为logn,即空间复杂度为:O(logn),最坏情况下需要进行n-1次递归调用,所以为O(n)。

    • 稳定性

      不稳定,基准值的比较机制会移动相等元素的位置顺序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值