【算法训练】二叉树系列思想(4)-快排

本文详细介绍了两种基于快速排序思想的算法:一是用于排序数组的快速排序算法,通过随机选取枢轴并进行分区操作实现高效排序;二是寻找数组中第K大元素的快速选择算法,利用二分搜索思想找到目标位置。这两种算法都涉及到随机化选择枢轴以防止最坏情况的发生,并且在处理大规模数据时具有较高的效率。
摘要由CSDN通过智能技术生成

一、排序数组 912

在这里插入图片描述

1、分析

今天我们用快排来做这道题。
快排的思想通俗的描述,挑选一个节点,然后将大于该节点的部分放在其右边,小于等于该节点的部分放在其左边,然后再对其左边和右边部分中的元素依次做类似的操作。其实可以看作是二叉树的前序遍历,先对当前节点做操作,然后再操作其左右相关部分。
具体见代码及注释。

2、代码

import random
class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        def partition(nums,lo,hi):
            pivot_idx = random.randint(lo,hi) #随机选择,避免出现很极端的情况,导致时间复杂度过大
            nums[lo],nums[pivot_idx] = nums[pivot_idx],nums[lo] #先将目标节点的值存放在最左边
            pivot = nums[lo] #保存目标节点的值
            left,right = lo,hi
            while left<right: #开始将大于目标节点值的放在右边,小于他的放在左边
                while left<right and nums[right]>=pivot: #循环结束的时候,right索引处的值是小于目标节点的
                    right -= 1
                nums[left] = nums[right]
                while left<right and nums[left]<pivot: #循环结束的时候,left索引处的值是大于等于目标节点的
                    left += 1
                nums[right] = nums[left]
            nums[left] = pivot #找到了目标节点应该放置的位置
            return left #返回目标节点的索引,其左边的都是小于等于他的,右边的都是大于他的值
        def quick_sort(nums,lo,hi):
            if lo>=hi: #base case
                return 
            p = partition(nums,lo,hi)
            quick_sort(nums,lo,p-1) #递归的处理 其实就是二叉树前序遍历的关键位置
            quick_sort(nums,p+1,hi)
        quick_sort(nums,0,len(nums)-1)
        return nums

JS

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
    var partition = function(nums,lo,hi){
        let privot_idx = Math.floor(Math.random()*(hi-lo))+lo;
        let temp = nums[privot_idx];
        nums[privot_idx] = nums[lo];
        nums[lo] = temp;
        let privot = nums[lo]
        let left=lo,right = hi;
        while(left<right){
            while(left<right && nums[right]>=privot){
                right -= 1;
            }
            nums[left] = nums[right];
            while(left<right && nums[left]<privot){
                left += 1;
            }
            nums[right] = nums[left];
        }
        nums[left] = privot;
        return left;
    }
    var quick_sort = function(nums,lo,hi){
        if(lo>=hi){
            return
        }
        let p = partition(nums,lo,hi);
        quick_sort(nums,lo,p-1);
        quick_sort(nums,p+1,hi);
    }
    quick_sort(nums,0,nums.length-1);
    return nums;

};

二、数组中的第K个最大元素 215

在这里插入图片描述

1、分析

这里用快速选择算法,因为在这个过程中,我们每次找到的分界位置p,就能够保证左边有p个元素是小于当前位置的元素的。因此找第k大元素,就是找应该放在len(nums)-k位置的元素。因此我们可以用二分搜索的思想去找到想要的p值即可。
具体看代码及注释

2、代码

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        def partiotion(nums,lo,hi): #分割,这个函数与快速排序中的分割函数是一样的
            privot_idx = random.randint(lo,hi) #随机找基准值
            nums[privot_idx],nums[lo] = nums[lo],nums[privot_idx]
            privot = nums[lo]
            left,right = lo,hi
            while left<right:
                while left<right and nums[right]>=privot:
                    right -= 1
                nums[left] = nums[right]
                while left<right and nums[left]<privot:
                    left += 1
                nums[right] = nums[left]
            nums[left] = privot
            return left
        lo,hi = 0,len(nums)-1 #采用二分搜索的思想,去找对应的p
        k = len(nums)-k
        while lo<=hi:
            p = partiotion(nums,lo,hi)
            if p<k:
                lo = p+1
            elif p>k:
                hi = p-1
            else:
                return nums[p]
        return -1

JS

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var findKthLargest = function(nums, k) {
    var partition = function(nums,lo,hi){
        let privot_idx = Math.floor(Math.random()*(hi-lo))+lo;
        let temp = nums[privot_idx];
        nums[privot_idx] = nums[lo];
        nums[lo] = temp;
        let privot = nums[lo];
        let left = lo,right = hi;
        while(left<right){
            while(left<right && nums[right]>=privot){
                right -= 1;
            }
            nums[left] = nums[right];
            while(left<right && nums[left]<privot){
                left += 1;
            }
            nums[right] = nums[left]
        }
        nums[left] = privot;
        return left;
    }
    let lo = 0,hi=nums.length-1;
    let target = nums.length-k;
    while(lo<=hi){
        let p = partition(nums,lo,hi);
        if(p>target){
            hi = p-1;
        }else if(p<target){
            lo = p+1;
        }else{
            return nums[p];
        }
    }
    return -1;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值