一、环境说明
- 本文是 LeetCode 215题 : 数组中的第K个最大元素,使用c语言实现。
- 快速选择。查找无序数组的利器!
- 测试环境:Visual Studio 2019。
二、代码展示
void swap(int *a,int *b){
int temp = *a;
*a = *b;
*b = temp;
}
int partition(int *nums,int left,int right){
int j = left;
for(int i = left;i<right;i++){
if(nums[right]>nums[i]){
swap(nums+i,nums+j);
j++;
}
}
swap(nums+j,nums+right);
return j;
}
int randomPartition(int *nums,int left, int right){
int i = rand()%(right - left + 1)+left;
int temp = nums[right];
nums[right] = nums[i];
nums[i] = temp;
return partition(nums,left,right);
}
int quickSelect(int *nums,int left,int right,int index){
int q = randomPartition(nums,left,right);
if(index == q){
return nums[q];
}else if(q<index){
return quickSelect(nums,q+1,right,index);
}else{
return quickSelect(nums,left,q-1,index);
}
}
int findKthLargest(int* nums, int numsSize, int k){
srand(time(0));
return quickSelect(nums,0,numsSize-1,numsSize - k);
}
三、思路分析
- 快速选择,选择无序数组中的元素。思想基于快速排序与随机分治。
- 如果读者手撕过快排,对分治有所了解,快选就好懂了:
- 我们知道,快排第一步是选取一个数组中的确定数,比如最右侧元素
n
u
m
s
[
r
i
g
h
t
]
nums[right]
nums[right],所有小于
n
u
m
s
[
r
i
g
h
t
]
nums[right]
nums[right]的,放在它左边;大于
n
u
m
s
[
r
i
g
h
t
]
nums[right]
nums[right]的,放在它右边。这样我们就唯一确定了
n
u
m
s
[
r
i
g
h
t
]
nums[right]
nums[right]的下标。
- 快选只关心当前
n
u
m
s
[
r
i
g
h
t
]
nums[right]
nums[right]的下标
q
q
q和目标
i
n
d
e
x
index
index的大小,
i
n
d
e
x
=
n
u
m
s
S
i
z
e
?
k
index=numsSize-k
index=numsSize?k,如果
q
q
q<
i
n
d
e
x
index
index就去
[
q
+
1
,
r
i
g
h
t
]
[q+1,right]
[q+1,right]递归分治。分治结束,
n
u
m
s
nums
nums的倒数第
k
k
k个数,即为所求。
- 快选和快排的分治
p
a
r
t
i
t
i
o
n
partition
partition实现是一样的,区别在于,快排递归到了数组的最细粒度,即双元素排序,乃至单元素排序。
- 随机分治是一种分治策略,使得快选的平均时间复杂度在数学证明上趋于线性时间。
四、代码分析
- 理解思路很重要!
- 博主欢迎读者在评论区留言,作为日更博主,看到就会回复的。
五、AC
六、复杂度分析
- 时间复杂度:
O
(
n
)
O(n)
O(n) ,
n
n
n是
n
u
m
s
nums
nums数组的大小。快选
n
u
m
s
nums
nums的平均时间复杂度是
O
(
n
)
O(n)
O(n),算法导论有严格的数学证明,追求所以然的同学看书学习嗷。
- 空间复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn),递归调用函数占用了栈空间,递归的空间复杂度是
O
(
l
o
g
n
)
O(logn)
O(logn)。
|