Leetcode算法题215:数组中第k大元素

2020-05-12  本文已影响0人  Persistence__

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

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

思路1:快排思想
快速排序是对冒泡的改进,降低冒泡的递归深度, 使时间复杂度降低到O(nlgn)。因为快排每次将数组划分为两组加一个基准元素,每一趟划分你只需要将k与基准元素的下标进行比较。
如果比基准元素下标大就从右边的子数组中找,如果比基准元素下标小从左边的子数组中找,如果一样则就是基准元素,找到,
如果需要从左边或者右边的子数组中再查找的话,只需要递归一边查找即可,无需像快排一样两边都需要递归,所以复杂度必然降低。

- (int)findKthLargest:(NSMutableArray *)array k:(int)k {
    int i = 0;
    int j = (int)array.count - 1;
    while (true) {
        int temp = [self partition:array i:i j:j];//执行一遍后,把最大的放最前面,再比较temp和k的关系
        if (k == temp + 1) {
            return [array[temp] intValue];
        }else if(k < temp + 1)
        {
            j = temp-1;//这里每次要把基准元素去掉,否则会死循环
        }
        else
        {
            i = temp + 1;
        }
    }
}

- (int)partition:(NSMutableArray *)array i:(int)i j:(int)j {
    int pivot = [array[i] intValue];
    while (i != j) {
        while (i < j && [array[j] intValue] < pivot) {
            j--;
        }
        while (i < j && [array[i] intValue] > pivot) {
            i++;
        }
        if (i < j) {
            [array exchangeObjectAtIndex:i withObjectAtIndex:j];
        }
    }
    return i;
}

思路2:堆排序思想
建立最小根堆,用数组的前k个数,建立最小根堆。然后从第k+1开始,和堆顶比较,如果比堆顶大,就交换,交换完重新维护成最小根堆,直到结束。这样堆里存放的就是数组中前k个最大的元素,而堆顶就是这里面最小的,也就是第k大的元素。

- (int)findKthLargest:(NSMutableArray *)array k:(int)k {
    
    // 1.前k个数建立最小根堆,最小的数在堆顶
    NSMutableArray *newArr = [self buildHeap:[array subarrayWithRange:NSMakeRange(0, k)].mutableCopy n:k];
    // 2.从第k+1个开始,和堆顶比较
    for (NSInteger i = k; i < array.count; i++) {
        // 3.比堆顶大,则交换
        if ([array[i] intValue] > [newArr[0] intValue]) {
            newArr[0] = array[i];
            // 4.再对数组进行堆排序
            newArr = [self buildHeap:newArr n:k];
        }
    }

    return [newArr[0] intValue];
}

- (NSMutableArray *)buildHeap:(NSMutableArray *)array n:(NSInteger)n {
    NSInteger last = n - 1;
    NSInteger parent = (last - 1) / 2;
    for (NSInteger i = parent; i >= 0; i--) {
        [self heapify:array n:n i:i];
    }
    return array;
}

// heapify:小根堆思想,最小的放根位置
- (void)heapify:(NSMutableArray *)array n:(NSInteger)n i:(NSInteger)i {
    if (i >= n) {
        return;//递归出口
    }
    
    // 完全二叉树,根节点位置为i
    // 左子节点
    NSInteger c1 = 2*i + 1;
    NSInteger c2 = 2*i + 2;
    NSInteger min = i;
    
    if (c1 < n && [array[c1] intValue] < [array[min] intValue]) {
        min = c1;
    }
    if (c2 < n && [array[c2] intValue] < [array[min] intValue]) {
        min = c2;
    }
    if (min != i) {
        [array exchangeObjectAtIndex:min withObjectAtIndex:i];
        [self heapify:array n:n i:min];
    }
}

思路3:java的优先队列PriorityQueue
优先级队列,常用来代替堆,每次直接加入新数即可,自动维护大小顺序,最小的数在堆顶。数组中前k个元素加入到队列中,当k+1个进入后,最小的出队,这样数组遍历完后,队列中刚好剩下k个元素,返回堆顶元素就是第k大的元素

 public int findKthLargest(int[] nums, int k) {
     PriorityQueue<Integer> queue = new PriorityQueue<>();
     for (num:nums) {
         queue.offer(num);//入队
         if (queue.size() > k) {
             queue.poll();//堆顶最小的元素出队
         }
     }
     return queue.peak();//返回堆顶最小的元素
 }
上一篇下一篇

猜你喜欢

热点阅读