力扣--排序

  1. 第k个最大元素
  2. 根据字符出现频率排序
  3. 荷兰国旗问题-按颜色排序

1. 第k个最大元素

力扣215
方法一:基于堆排序的选择排序
建立一个大根堆,做k-1次删除操作后堆顶元素就是我们要找的答案。1.构建大根堆:先按照顺序将该数组元素构建成一颗完全二叉树,然后调整元素。从第一个非叶子节点为根节点的子树开始,将其调整为大根堆。再调整倒数第二个非叶子节点作为根节点的子树,调整第三个…2.删除堆顶元素,将末尾节点补充到堆顶位置,再进行调整。

void maxheapify(int *a,int i,int heapSize){
    int l=2*i,r=2*i+1,largest=i;
    if(l<heapSize&&a[l]>a[largest]){
        largest=l;
    }
    if(r<heapSize&&a[r]>a[largest]){
        largest=r;
    }
    if(largest!=i){
        int t=a[i];
        a[i]=a[largest];
        a[largest]=t;
        maxheapify(a,largest,heapSize);
    }
}


void Buildmaxheap(int *a,int heapSize){
    for(int i=heapSize/2;i>=0;i--){
        maxheapify(a,i,heapSize);
    }
}


int findKthLargest(int* nums, int numsSize, int k){
    int heapSize=numsSize;
    Buildmaxheap(nums,heapSize);
    for(int i=numsSize-1;i>=numsSize-k+1;i--){
        int t=nums[0];
        nums[0]=nums[i];
        nums[i]=t;
        heapSize--;
        maxheapify(nums,0,heapSize);
    }
    return nums[0];
}

时间复杂度O(nlogn):建堆时间代价O(n),删除的时间代价O(klogn),因为k<n,故渐进时间复杂度O(n+klogn)=O(nlogn)
空间复杂度O(logn):递归使用栈空间的空间代价

方法二:基于快速排序的选择方法(参考题解的,还有些不懂。。。)
向对原数组排序,再返回倒数第k个位置,时间复杂度为O(nlogn),其实可以更快。划分:从子数组a[l…r]中选择任意一个元素x作为主元,调整子数组的元素使得左边的元素都小于等于它,右边的元素都大于等于它,x的最终位置就是q。其实只要某次划分的q为倒数第k个下标的时候,就已经找到答案,直接返回a[q],否则,如果q比目标下标小,就递归右区间,否则递归左区间。把原来递归的两个区间变成一个区间,提高时间效率。

int partition(int *a,int l,int r){
    int x=a[r],i=l-1;
    for(int j=l;j<r;j++){
        if(a[j]<=x){
            int t=a[++i];
            a[i]=a[j];
            a[j]=t;
        }
    }
    int t=a[i+1];
    a[i+1]=a[r];
    a[r]=t;
    return i+1;
}



int randomPartition(int *a,int l,int r){
    int i=rand()%(r-l+1)+l;
    int t=a[r];
    a[r]=a[i];
    a[i]=t;
    return partition(a,l,r);
}


int quickselect(int *a,int l,int r,int index){
    int q=randomPartition(a,l,r);
    if(q==index){
        return a[q];
    }
    else{
        return q < index ? quickselect(a,q+1,r,index) : quickselect(a,l,q-1,index);
    }
}

int findKthLargest(int* nums, int numsSize, int k){
    srand(time(0));
    return quickselect(nums,0,numsSize-1,numsSize-k);
}

时间复杂度O(n):随机化加速进程。
空间复杂度O(logn):递归使用栈空间代价的期望。

2.根据字符出现频率排序

力扣451
第一反应就是用字典的键存储字符串的字符,值存储该字符出现的频率。对值用sorted()进行排序,注意是要降序,而默认情况是升序。最后,利用字符和其频率,将它们拼接成新的字符串。

class Solution:
    def frequencySort(self, s: str) -> str:
        dic={}
        res=''#用来拼接字符串,返回答案
        for i in s:
            dic[i]=dic.get(i,0)+1
        dic=sorted(dic.items(),key=lambda k:-k[1])#按照次数从大到小对字典降序排序
        for i in dic:
            for j in range(i[1]):
                res+=i[0]
        return res            

3.荷兰国旗问题-按颜色排序

75
提示:
n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2

进阶:
1.你可以不使用代码库中的排序函数来解决这道题吗?–那就不能用nums.sort()

2.你能想出一个仅使用常数空间的一趟扫描算法吗?–单指针需要两趟,如果是双指针就只需要一趟。

借助快速排序的思想,我们可以在边遍历边修改(交换)。

方法一:单指针

p指针表示头部范围。刚开始p=0,表示还没有范围第一趟,从左到右遍历,若找到了0,那就和头部此时p的位置交换,这样所有的0就都移到了头部。第二趟,从p开始从左往右遍历,若找到了1,那就和头部此时p的位置交换,这样所有的1就都被移到0的后面,而剩下的都是2,排序完成。

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        #nums.sort()
        #单指针
        n=len(nums)
        p=0
        for i in range(n):
            if nums[i]==0:
                nums[i],nums[p]=nums[p],nums[i]
                p+=1
        for i in range(p,n):
            if nums[i]==1:
                nums[i],nums[p]=nums[p],nums[i]
                p+=1        

方法二:双指针

方法一,可以改进,借助两个指针:p0用来交换0,p1用来交换1。同样,都从0的位置,从左到右遍历,同样是找到了1,和p1的位置交换;找到0,和p0位置交换。也就是说p1,p0是在同时进行交换的,那么这就可能会出现问题:找到0时,如果p0<p1,交换p0(此时p0为1)和这个位置上数0,这时,如果直接就去遍历下一个数i++,那么就不能考虑到刚刚交换得到的1,答案错误,所以要将交换得到的nums[i]和nums[p1]进行交换。最后同时将p0,p1向后移。

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        #nums.sort()
        #双指针
        n=len(nums)
        p0,p1=0,0
        for i in range(n):
            if nums[i]==1:
                nums[i],nums[p1]=nums[p1],nums[i]
                p1+=1
            elif nums[i]==0:
                nums[i],nums[p0]=nums[p0],nums[i]
                if p0<p1:
                    nums[i],nums[p1]=nums[p1],nums[i]
                p0+=1
                p1+=1    

另外一种双指针:指针p0用来交换0,p2用来交换2。p0的初始位置为0(从左向右遍历,开区间),p2的初始位置为n-1(从右向左遍历,开区间)。在遍历的过程中,把找出的所有0交换至数组的头部,并且将找出的所有2交换至数组的尾部。需要注意的是:如果找到了0,将其与nums[p0]进行交换,并将p0向后移动一个位置,i++去遍历下一个数。这是对的,但是如果找到了2,那么将其与nums[p2]进行交换,并将p2向前移动一个位置,如果这个时候就i++,去遍历下一个数,这就可能会出错,因为交换后,新的nums[i]可能会是0或2,还没判断完要不要交换,就直接i++去判断下一个数,这样就不会再考虑nums[i]了,得出错误答案。所以当我们找到2时,需要不断的将这个nums[i]与nums[p2]进行交换,直到新的nums[i]不为0或2,因为结束这个循环后,会有判断有没有找到0的if语句,所以可以先不用在while语句中判断是不是0。

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n=len(nums)
        p0,p2=0,n-1
        i=0
        while i<=p2:#因为p2是开区间的,所以当i=p2时,还要继续判断一下
            while i<=p2 and nums[i]==2:
                nums[i],nums[p2]=nums[p2],nums[i]
                p2-=1
            if nums[i]==0:
                nums[i],nums[p0]=nums[p0],nums[i]
                p0+=1
            i+=1     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值