- 第k个最大元素
- 根据字符出现频率排序
- 荷兰国旗问题-按颜色排序
1. 第k个最大元素
方法一:基于堆排序的选择排序
建立一个大根堆,做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.根据字符出现频率排序
第一反应就是用字典的键存储字符串的字符,值存储该字符出现的频率。对值用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.荷兰国旗问题-按颜色排序
提示:
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