编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 01.冒泡排序
  • 02.插入排序
  • 03.选择排序
  • 04.快速排序
  • 05.希尔排序
  • 06.归并排序
  • 07.堆排序
  • 08.计数排序
  • 09.桶排序
  • 10.基数排序
  • 11.线性排序案例

03.选择排序

选择排序

  • 1.基本思想
  • 2.排序过程
  • 3.代码实现
  • 4.如何优化
  • 5.复杂度
  • 6.使用场景

0.选择排序种类

  • 选择排序分为三种,直接选择排序、树形选择排序(锦标赛排序)、堆排序(大根堆、小根堆)。
  • 直接选择排序和堆排序是不稳定排序,树形选择排序是稳定排序。

1.直接选择排序

1.1 基本思想

  • 在长度为N的无序数组中,第一次遍历n-1个数,找到最小的数值与第一个元素交换;
  • 第二次遍历n-2个数,找到最小的数值与第二个元素交换;
  • 依此类推……
  • 第n-1次遍历,找到最小的数值与第n-1个元素交换,排序完成。

1.2 排序过程

  • 通过设置哨位,将哨位位置的数与哨位之后(包括哨位)的序列中最小的数进行交换,然后哨位递增,直到哨位到达数组中最后一个数为止。
  • image
    image

1.3 代码实现

  • 如下所示
    /*
     * 直接选择排序
     */
    public static void selectSort1(int[] data) {
        int pos ;
        for (int i = 0; i < data.length; i++) {
            pos = i;
            // 找出从i开始,到数组末尾这段数组中最小的数,pos标志的是这个最小的数在数组中的位置
            for (int j = i + 1; j < data.length; j++) {
                if (data[j] < data[pos]) {
                    pos = j;
                }
            }
            // 交换两个数的位置
            if(pos != i){
                int temp = data[i];
                data[i] = data[pos];
                data[pos] = temp;
            }
        }
        for(int i=0;i<data.length;i++){
            System.out.println("yc1-----" +data[i]);
        }
    }

1.4 如何优化

  • 直接选择排序的最好时间复杂度和最差时间复杂度都是O(n²),因为即使数组一开始就是正序的,也需要将两重循环进行完,平均时间复杂度也是O(n²)。
  • 空间复杂度为O(1),因为不占用多余的空间。直接选择排序是一种原地排序(In-place sort)并且稳定(stable sort)的排序算法,优点是实现简单,占用空间小,缺点是效率低,时间复杂度高,对于大规模的数据耗时长。

1.5复杂度

  • 最好时间复杂度和最差时间复杂度都是O(n²)

2.树形选择排序(锦标赛排序)

2.1 基本思想

  • 树形选择排序利用满二叉树的性质,将待排序的数放入叶子节点中,然后同属于一个根节点的两个叶子节点相互比较,较小的叶子节点复制到其根节点,然后根节点之间再相互比较,直到整棵树的根节点,此时整棵树的根节点为待排序数组中最小的一个数,在下一次循环中要将这个数置为最大值,然后再开始循环,直到全部的数都被取出,排序完成。因为这种排序类似于比赛中的淘汰赛,所以又称之为锦标赛排序。

2.2 排序过程

  • 构造一棵满二叉树,要求可以将待排序数组全部放入叶子节点中
  • 将两个叶子节点中较小的数挪入根节点中,全部挪完之后,再将两个根节点中较小的数挪入它们的根节点中,直到整棵树的根节点。
  • 取出根节点中的数,将叶子节点中的这个数置为max,重复第二步,直到每个数都被取出过一次。

2.3 代码实现

  • 代码如下所示
    public static void treeSelectSort(int[] a){
        int len = a.length;
        int treeSize = 2 * len - 1;  //完全二叉树的节点数
        int low = 0;
        int[] tree = new int[treeSize];    //临时的树存储空间
        //由后向前填充此树,索引从0开始
        for(int i = len-1,j=0 ;i >= 0; --i,j++){      //填充叶子节点
            tree[treeSize-1-j] = a[i];
        }
    
        for(int i = treeSize-1;i>0;i-=2){ //填充非终端节点
            //noinspection unchecked
            tree[(i-1)/2] = ((Comparable)tree[i-1]).compareTo(tree[i]) < 0 ? tree[i-1]:tree[i];
        }
    
        //不断移走最小节点
        int minIndex;
        while(low < len){
            int min = tree[0];    //最小值
            a[low++] = min;
            minIndex = treeSize-1;
            //找到最小值的索引
            //noinspection unchecked
            while(((Comparable)tree[minIndex]).compareTo(min)!=0){
                minIndex--;
            }
            tree[minIndex] = Integer.MAX_VALUE;  //设置一个最大值标志
            //找到其兄弟节点
            while(minIndex > 0){      //如果其还有父节点
                if(minIndex % 2 == 0){   //如果是右节点
                    //noinspection unchecked
                    tree[(minIndex-1)/2] = ((Comparable)tree[minIndex-1]).compareTo(tree[minIndex])
                            < 0 ? tree[minIndex-1]:tree[minIndex];
                    minIndex = (minIndex-1)/2;
                }else{                   //如果是左节点
                    //noinspection unchecked
                    tree[minIndex/2] = ((Comparable)tree[minIndex]).compareTo(tree[minIndex+1])
                            < 0 ? tree[minIndex]:tree[minIndex+1];
                    minIndex = minIndex/2;
                }
            }
        }
    
    
        for(int i=0;i<a.length;i++){
            System.out.println("yc1-----" +a[i]);
        }
    }
贡献者: yangchong211
上一篇
02.插入排序
下一篇
04.快速排序