【20190327】【每天一道算法题】数组中的第K个最大元素(分治算法)(快速选择算法)

问题:

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

假设: k 总是有效的,且 1 ≤ k ≤ 数组的长度。


思路及代码:

1. C 语言实现

/* 思路:Divide and conquer
 * 第一步:快速排序(qsort函数);
 * 第二步:qsort排好的是从小到大,第k大的元素索引为numsSize-k。
*/
#include
#include

int findKthLargest(int* nums, int numsSize, int k);
int cmp(const void *a, const void *b);

void main(void)
{
	int k = 1;
	int result;
	int nums[6] = {4,2,4,3,6,0};
	int numsSize = sizeof(nums) / sizeof(nums[0]);
	result = findKthLargest(nums, numsSize, k);
	printf("第%d大的数是%d\n",k,result); 

	system("pause");
}

int findKthLargest(int* nums, int numsSize, int k) 
{
    qsort(nums, numsSize, sizeof(nums[0]), cmp);
	return nums[numsSize-k];
}

int cmp(const void *a, const void *b)
{
     return (*(int *)a - *(int *)b);
}

2. Python 实现

(参考:数组中的第K个最大元素)

# 方法一:快速排序之后取第k个大的数,从后往前的第k个,所以索引是-k。
# 时间复杂度:O(NlgN)
# 空间复杂度:O(1)
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return sorted(nums)[-k]
		

# 方法二:快速选择排序,不对所有元素进行排序,随机的选取pivot,将比它小的放在它的前面,比它大的放在它的后面;看这个pivot的索引是否和len(nums)-k相同,若相同则返回该索引;若小于len(nums)-k,则在pivot之后的元素中再重新进行选择;若大于,则在pivot之前的元素重新选择。
# 时间复杂度:平均O(N),最差O(N2)		
# 空间复杂度:O(1)
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        def select(left, right, k_smallest):
            if left == right:
                return nums[left]
            pivot_index = random.randint(left, right)
            pivot_index = partition(left, right, pivot_index)
            if k_smallest == pivot_index:
                return nums[k_smallest]
            elif k_smallest > pivot_index:
                return select(pivot_index+1, right, k_smallest)
            else:
                return select(left, pivot_index-1, k_smallest)
            
        def partition(left, right, pivot_index):
            pivot = nums[pivot_index]
            store_index = left
            nums[pivot_index], nums[right] = nums[right], nums[pivot_index]
            for i in range(left, right):
                if nums[i] < pivot:
                    nums[store_index], nums[i] = nums[i], nums[store_index]
                    store_index += 1
            nums[store_index], nums[right] = nums[right], nums[store_index]
            return store_index
        return select(0, len(nums)-1, len(nums)-k)


# 官方代码
class Solution:
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        def partition(left, right, pivot_index):
            pivot = nums[pivot_index]
            # 1. move pivot to end
            nums[pivot_index], nums[right] = nums[right], nums[pivot_index]  
            
            # 2. move all smaller elements to the left
            store_index = left
            for i in range(left, right):
                if nums[i] < pivot:
                    nums[store_index], nums[i] = nums[i], nums[store_index]
                    store_index += 1

            # 3. move pivot to its final place
            nums[right], nums[store_index] = nums[store_index], nums[right]  
            
            return store_index
        
        def select(left, right, k_smallest):
            """
            Returns the k-th smallest element of list within left..right
            """
            if left == right:       # If the list contains only one element,
                return nums[left]   # return that element
            
            # select a random pivot_index between 
            pivot_index = random.randint(left, right)     
                            
            # find the pivot position in a sorted list   
            pivot_index = partition(left, right, pivot_index)
            
            # the pivot is in its final sorted position
            if k_smallest == pivot_index:
                 return nums[k_smallest]
            # go left
            elif k_smallest < pivot_index:
                return select(left, pivot_index - 1, k_smallest)
            # go right
            else:
                return select(pivot_index + 1, right, k_smallest)

        # kth largest is (n - k)th smallest 
        return select(0, len(nums) - 1, len(nums) - k)

知识点

1. 寻找无序数组中第 K 大的元素,时间复杂度可以降为:O(N)。(用快速选择算法)

(参考:在N个乱序数字中查找第k大的数字)

快速排序:O(NlgN)

堆排序:O(KlgN)

快速选择:O(N)

2. 快速选择算法:是快速排序的变形,不同的是,快速选择算法不对两边进行递归,而是只对一边进行递归,这样就降低了时间复杂度,但最差依旧为 O(N2)。

(参考:经典算法之快速选择算法)

你可能感兴趣的:(每天一道算法题)