【算法】【排序算法】【01】冒泡、选择、堆


(1)前期准备参考:代码环境准备

(2)空间复杂度 & 原地算法

空间复杂度:
	算法执行所需要的临时空间

原地算法 in-place:
	不依赖额外的资源或者依赖少数的额外资源,仅依靠输出来覆盖输入
	空间复杂度为O(1)的可以认为是原地算法

非原地算法,称为 Not-in-place 或者 Out-of-place

开局一张图
在这里插入图片描述
排序过程动态gif


冒泡

经典冒泡

public class _01_Bubble <T extends Comparable<T>> extends Sort<T> {

    @Override
    public void sort() {
        for(int end = array.length -1; end> 0;end--){
            for(int cur = 0;cur < end;cur++){
                if(cmp(array[cur] , array[cur+1]) > 0){
                    swap(cur,cur+1);
                }
            }
        }
    }
}
  • end的定义 :[0,end]范围内都是无序的;当前安排 [0,end]范围内最大元素放在end位置;
  • 在完成当前轮排序前:[0,end]是无序的, (end,length-1]是有序的
    完成当前轮排序后:[0,end)是无序的, [end,length-1]是有序的

冒泡优化1


public class _01_Bubble_2 <T extends Comparable<T>> extends Sort<T> {

    /**
     *  优化思路1:
     *   [0,end] 是乱序的  (end,length-1] 是有序的
     *   如果 cur 在遍历[0,end)的过程中,没有发生swap(cur,cur+1)操作,表示[0,end]数据是有序的
     *   此时,end不需要 end-- 迭代了,直接结束程序
     */
    @Override
    public void sort() {
        for(int end = array.length -1; end> 0;end--){
           
            boolean sorted = true;
            for(int cur = 0;cur < end;cur++){
                if(cmp(array[cur] , array[cur+1]) > 0){
                    swap(cur,cur+1);
                    sorted = false;
                }
            }
            if(sorted) break;
        }
    }
}

这种优化本质上并没有减少冒泡排序的交换次数,只是在达到有序后,提前终止了循环,减少了循环的轮数;

冒泡优化2

public class _01_Bubble_3<T extends Comparable<T>> extends Sort<T> {

    /**
     *  优化思路:
     *      记录下[0,end)最后一次交换的位置 index, [index,length-1]都是有序的
     *      从而实现从end-- 直接跳跃到 index
     *      减少循环次数
     */
    @Override
    protected void sort() {
        for (int i = array.length - 1; i > 0; i--) {

            int end = 1;
            for(int j = 0;j< i;j++){
                if(cmp(array[j], array[j + 1]) > 0){
                    swap(j,j+1);
                    end=j+1;
                }
            }
            i = end;
        }
    }
}

这种优化本质上也没有减少冒泡排序的交换次数,只是实现了end 跨越,从而减少了比较次数;

冒泡排序的复杂度和稳定性

时间复杂度

  • 最坏:O(n2) 完全逆序 从大到小
  • 最好:O(n) 完全正序(优化后的冒泡排序,一轮后直接结束程序;如果是经典的,还需要比较,仍然为O(n2))
  • 平均:O(n2)

空间复杂度

  • 空间复杂度:O(1)
  • 冒泡排序属于 In-place
  • 冒泡排序属于稳定的排序算法

稳定性
冒泡排序的稳定性取决于 发生交换的判决条件:

if(cmp(array[j], array[j + 1]) > 0){
     swap(j,j+1);
}
  • 如果是> 交换,那么两个相等的元素就不会进行交换,在后面的会先放到end,因此是稳定的;
  • 如果是>= 交换,那么两个相等的元素会发生交换,破坏了稳定性;

选择排序

  • [0,end]是无序区,每轮在无序区找到最大元素的索引,和end位置的元素进行交换;
  • 优势:每一轮end 只发生一次交换操作
public class _02_Select<T extends Comparable<T>> extends Sort<T> {

    @Test
    public void sort(){
        for (int end = array.length -1; end > 0; end--) {
           int maxIndex = 0;
           for(int cur = 1;cur<=end;cur++){
               if(cmp(array[cur],array[maxIndex]) >= 0){
                   maxIndex = cur;
               }
           }
           swap(maxIndex,end);
        }
    }
}

选择排序的复杂度和稳定性

时间复杂度

  • 最坏:O(n2)
  • 最好:O(n2)
  • 平均:O(n2)

空间复杂度
空间复杂度:O(1)
冒泡排序属于 In-place
冒泡排序属于稳定的排序算法

稳定性
冒泡排序的稳定性取决于 发生交换的判决条件:

if(cmp(array[cur],array[maxIndex]) >= 0){
     maxIndex = cur;
 }

如果是>,那么假设有两个最大元素,会将第一个元素放到end位置,因此是不稳定的;
如果是>= ,那么假设有两个最大元素,会将最后面一个元素放到end位置,因此是稳定的;

main方法测试验证:

    public static void main(String[] args) {
        /*
            count:测试的数据量
            min:最小值
            max:最大值
         */
        Integer[] array = Integers.random(20000, 1, 10000);

        // 在这里面写要测试的代码
        testSorts(array,
                new _01_Bubble(),
                new _01_Bubble_2(),
                new _01_Bubble_3(),
                new _02_Select()
        );
    }
【_02_Select】
稳定性:true 	耗时:0.4s(400ms) 	比较:2.00亿	 交换:2.00万
------------------------------------------------------------------
【_01_Bubble_2】
稳定性:true 	耗时:1.845s(1845ms) 	比较:2.00亿	 交换:9987.16万
------------------------------------------------------------------
【_01_Bubble_3】
稳定性:true 	耗时:1.93s(1930ms) 	比较:2.00亿	 交换:9987.16万
------------------------------------------------------------------
【_01_Bubble】
稳定性:true 	耗时:2.504s(2504ms) 	比较:2.00亿	 交换:9987.16万
------------------------------------------------------------------

这里选择排序用的是>=判断条件,可以看出是稳定的;
选择排序因为交换次数远小于冒泡,因此效率高很多;

堆排序

图解可参考:
https://blog.csdn.net/weixin_43734095/article/details/105108135

认识二叉堆 Heap结构

public class _03_HeapSort<T extends Comparable<T>> extends Sort<T> {

    private int heapSize; // 堆大小

    @Override
    protected void sort() {
        heapSize = array.length;
        for (int i = (heapSize - 2) >> 1; i >= 0; i--) {
            siftDown(i);
        }
        while (heapSize > 1) {
            // 交换堆顶元素和尾部元素
            swap(0, --heapSize);

            // 对0位置进行siftDown(恢复堆的性质)
            siftDown(0);
        }

    }


    public void siftDown(int index) {
        T old = array[index];
        while (index <= (heapSize - 2) >> 1) {
            int leftIndex = (index << 1) + 1;
            int rightIndex = leftIndex + 1;
            int maxIndex = leftIndex;

            if (index <= (heapSize - 3) >> 1) {//左右都有
                maxIndex = cmp(array[leftIndex], array[rightIndex]) > 0 ? leftIndex : rightIndex;
            }
            if (cmp(array[maxIndex], old) > 0) {
                array[index] = array[maxIndex];
                index = maxIndex;
            } else {
                break;
            }
        }
        array[index] = old;
    }
}

堆排序的复杂度和稳定性

堆排序:先对无序序列原地建堆,有了堆以后,可以直接选出最大值;
将最大值和elements[size-1]互换,再对elements[0]进行下滤;

最好、最坏、平均时间复杂度: O(nlogn)
空间复杂度:O ( 1 )
堆排序属于不稳定排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值