CodeTop089 手撕堆排序

本文详细介绍了两种经典的排序算法——快速排序和堆排序。首先,通过手撕代码的方式展示了快速排序的实现,包括随机选取基准元素和递归排序的过程。接着,解释了堆排序的原理,包括如何建立大顶堆、调整堆以及交换最大元素。通过实例代码,阐述了堆排序中节点的上下移动规则。这两部分深入浅出地解析了排序算法的核心思想。
摘要由CSDN通过智能技术生成

这个的话没什么好说的 就是手撕排序算法.这里买一送一,先手撕一个快排.

//快排
    public static void QuickSort(int[] nums){
        if (nums.length<=0) return;
        QuickSort(nums,0,nums.length-1);
    }

    public static void QuickSort(int[] nums,int left,int right){
        if (left>=right) return;
        int random = (int) Math.random()*(right-left+1);
        swap(nums,left+random,right);
        int[] func = func(nums,left,right);
        QuickSort(nums,left,func[0]);
        QuickSort(nums,func[1],right);
    }

    public static int[] func(int[] nums,int left,int right){
        int low = left-1;
        int high = right;

        while (left<high){
            if (nums[left]<nums[right]){
                swap(nums,++low,left++);
            }else if (nums[left]>nums[right]){
                swap(nums,--high,left);
            }else{
                left++;
            }
        }

        swap(nums,high,right);
        return new int[]{low,high+1};
    }

    public static void swap(int[] nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

*堆排序正式开始:首先堆排序中的几个重要概念:

  • (因为是前序遍历)在数组中i位置的左孩子是(2^i)+1 右孩子是(2^i)+2
  • 任意位置index的父节点是(index-1)/2
  • 堆排序最重要的两个过程,一个是向上走,一个是想下走.分别对应于heapInsert()和heapify()

具体看代码:

//堆排序 在数组中i位置的左孩子是(2^i)+1 右孩子是(2^i)+2  (因为是前序遍历)
    //因此任意位置index的父节点是(index-1)/2
    public static void heapSort(int[] arr) {
        if (arr==null || arr.length<2) return;

        //先得到一个大顶堆
        for (int i=0;i<arr.length;i++){
            heapInsert(arr,i);
        }

        //在得到大根堆后 对顶元素就是最大值 将它交换到尾部去
        int size = arr.length;
        swap(arr,0,--size);//将大顶堆最大位置和最后一个位置做交换  然后size--

        while (size>0){
            //将剩下的元素进行调整 继续维持大根堆
            heapify(arr,0,size);
            //在得到大根堆后 对顶元素就是最大值 将它交换到尾部去
            swap(arr,0,--size);
        }
    }


    //某个数现在处于index位置,往上继续移动  (往上走)
    public static void heapInsert(int[] arr,int index){
        //和自己的父节点作比较 只要大于父节点就做交换
        while (arr[index]>arr[(index-1)/2]){
            swap(arr,index,(index-1)/2);
            index = (index-1)/2;
        }
    }

    //将新的节点调整为大顶堆  某个数现在index位置,能否往下移动  (往下走)
    public static void heapify(int[] arr,int index,int heapsize){
        int left = index*2+1;//左孩子的下标
        while(left<heapsize){
            //找到左右孩子中的最大值  left+1就是右孩子
            int largest = (left+1<heapsize) && arr[left]<arr[left+1] ? left+1:left;
            /*
            注意!!!!如果写成largest = (left+1<heapsize) && arr[left]>arr[left+1] ? left:left+1;就会出错
            arr[left]>arr[left+1] ? left:left+1这一段的逻辑是没有问题的.但是前面还有一个右节点判断的问题(left+1<heapsize) 这里不能忽略
            如果右节点判断有问题就会直接返回left+1 然而这个逻辑是有问题的 left+1不符合条件 就不能返回了
            所以上面原代码的(left+1<heapsize) && arr[left]<arr[left+1] ? left+1:left;很牛逼
            * */

            //父和最大的孩子之间,谁的值大,就把下标给largest
            largest = arr[largest]>arr[index]? largest:index;

            if (largest==index){
                break;
            }

            swap(arr,largest,index);//较大的孩子和父做交换
            index = largest;
            left = index*2+1;//左孩子的下标
        }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值