lintcode5 - Kth Largest Element - medium

基础知识补充
if:

就是一个判断的,如果满足后面的条件就继续运行if语句里面的东西的,要是不满足就跳出来,执行else语句或执行下面的语句的 。

while:

就是循环语句的,当满足while里面的条件时,就会执行里面的循环体的,直到条件不满足为止。

在这里插入图片描述
左右都取等号 避免 极端情况

在这里插入图片描述

在这里插入图片描述
Find K-th largest element in an array.
Example
In array [9,3,2,4,8], the 3rd largest element is 4.
In array [1,2,3,4,5], the 1st largest element is 5, 2nd largest element is 4, 3rd largest element is 3 and etc.
Challenge
O(n) time, O(1) extra memory.
Notice
You can swap elements in the array

Quick select. O(n)。一轮O(n)操作后把原问题化解为规模仅一半的问题(只接着搜其中一半了)
实现:指针+递归。指针分为双指针和三指针两种写法,递归分为改变k和不改变k的写法。

三指针:中间扫描,大扔左,小扔右,最后l左边的肯定全大的,r右边肯定全小的,lr加上中间的是==的。具体非对称写法还是“从左开始扫,左换动lm,右换动动r,中间只动m”。
双指针:左右都找不合格的,找完互换。最后大扔左,小扔右,lr中间可能夹一个数。

递归改变k:思路是return值的定义为这段里面第k大的数。所以如果去右段搜的话,新的k值要减去左边数的数量,因为很多更大的数已经不在我的考虑范围里了。
递归不改变k:思路是return值的定义为全局nums第k个位置。我一直只传这个数,pass下去,我只保证我一直挪动位置最后答应你能把全局第k大的数移到k那个位置,时机合适麻烦你就直接把那个位置的东西拿走。

不考虑这个了什么东西

其实就是上面的图 有可能两个指针 L和R之间有=pivot的数
(细节:
1.双指针写法里面的=加不加:指针对比要加=:全用l<=r的逻辑写,避免可能死循环(可能其中一根指针从没动过)。而数字对比不要加=:比如nums[l] > pivot,别担心==的情况没动指针,会在下面swap的时候动的。这个真的有点难记,这题三指针比双指针好写,没有这么detail。
2.指针的问题最后检查时候都要看一下会不会有一轮过后,只有一根指针挪动全程的问题,因为这样会导致递归参数不变产生死循环的问题。尽量写法要让两根指针都动过,否则很危险,while循环or递归组合上不动指针,想想都恐怖。
3.pivot直接把值取出来,是nums[(start+end)/2]而不是记录位置(start+end)/2。因为实现过程中动过各个数字了,可能下一轮在那个位置上的东西就不是最开始的对比对象了。

4.题目定义第k大,有一个1的offset,要处理一下。比如找第一大的其实是计算机语言里的第0大。毕竟数组里第k个数跟着计算机语言的index定义,所以你最开始传进去要传k-1.

public class Solution {
    /**
     * @param n: An integer
     * @param nums: An array
     * @return: the Kth largest element
     */
    public int kthLargestElement(int n, int[] nums) {
        // write your code here
        if (nums == null || nums.length == 0) {
            return -1;
        }
        return partitionAndFind(n, nums, 0, nums.length - 1);
    }
    
    private int partitionAndFind(int k, int[] nums, int start, int end) {
        
        if (start == end) {
            return nums[start];
        }
        
        int l = start, r = end;
        int pivot = nums[(start + end) / 2];
        while (l <= r) {
            while (l <= r && nums[l] > pivot) {
                l++;
            }
            while (l <= r && nums[r] < pivot) {
                r--;
            }
            if (l <= r) {
                int temp = nums[l];
                nums[l] = nums[r];
                nums[r] = temp;
                l++;
                r--;
            }
        }
        //这个是为了判断想等值 到底是在那一块
        if (start + k - 1 <= r) {//说明左边够了
            return partitionAndFind(k, nums, start, r);//囊括中间值
        } else if (start + k - 1 >= l) {//说明左边不够
            return partitionAndFind(k - (l - start), nums, l, end);//同样不是右边考虑,囊括中间值
        } else {
            return nums[r + 1];//两种情况都不满足,返回中间段lmr
        }
    }
}

三个指针,跟着不同的人写
虽然思路一样但是写法不一样
快晕了

public class Solution {
    /**
     * @param n: An integer
     * @param nums: An array
     * @return: the Kth largest element
     */
    public int kthLargestElement(int n, int[] nums) {
        // write your code here
        if (nums == null || nums.length == 0) {
            return -1;
        }
        return partitionAndFind(n - 1, nums, 0, nums.length - 1);
    }
    
    private int partitionAndFind(int k, int[] nums, int start, int end) {
        
        if (start == end) {
            return nums[k];
        }
        
        int l = start, m = start, r = end;
        int pivot = nums[(start + end) / 2];
        while (m <= r) {
            if (nums[m] > pivot) {
                swap(nums, l, m);
                l++;
                m++;
            } else if (nums[m] < pivot) {
                swap(nums, m, r);
                r--;
            } else {
                m++;
            }
        }
        
        if (k < l) {
            return partitionAndFind(k, nums, start, l - 1);
        } else if (k > r) {
            return partitionAndFind(k, nums, r + 1, end);
        } else {
            return nums[k];
        }
    }
    
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 这道题目需要在二叉搜索树中查找第k大的元素。可以采用中序遍历的方式,将节点的值从小到大添加到数组中,然后返回数组中的倒数第k个元素即为答案。时间复杂度为O(N),空间复杂度为O(N)。也可以利用二叉搜索树的性质,从根节点开始遍历,每个节点记录其右子树的大小,根据大小可以找到第K大的元素。时间复杂度为O(log N),空间复杂度为O(1)。 ### 回答2: BST 是二叉搜索树,它是一种有序的二叉树,其中每个节点都存储一个键值,且左子树的值小于等于当前节点的值,右子树的值大于等于当前节点的值。因此,如果我们要找到 BST 中的第 k 大元素,我们可以利用 BST 的这种有序性质来帮助我们寻找。 一种有效的解决办法是使用中序遍历算法,在遍历的过程中维护一个计数器 count,记录当前已经遍历的节点数,如果 count 等于 k,直接返回当前节点的值即可。因为中序遍历算法遍历的顺序是左-中-右,所以返回的节点值就是第 k 大元素。 以下是实现该算法的例子,我们假设 BST 中不存在相同的值。 ``` class TreeNode: def __init__(self, val=None, left=None, right=None): self.val = val self.left = left self.right = right class Solution: def kthLargest(self, root: TreeNode, k: int) -> int: # 栈用于模拟中序遍历 stack = [] count = 0 # 记录当前已经遍历过的节点数 node = root while node or stack: while node: stack.append(node) node = node.right node = stack.pop() count += 1 if count == k: return node.val node = node.left ``` 该算法时间复杂度为 O(H+k),其中 H 为树的高度,k 为要查找的元素的下标。由于 BST 的性质,树的高度 H 最多为树中节点数 N(最坏情况下,BST 变成了一个链表),因此时间复杂度为 O(N+k)。 ### 回答3: 题目描述: 给定一棵二叉搜索树(BST)和一个整数k,找到其中第k大的元素。 解题思路: 首先,我们可以利用BST的性质,即左子树所有节点的值小于根节点的值,根节点的值小于右子树所有节点的值,来确定BST中某个节点的排名。 具体地,我们可以首先通过BST的中序遍历得到一个按照升序排列的节点值列表,然后按照降序遍历该列表并记录已经遍历的节点个数,当遍历到第k个节点时,就得到了所求的第k大节点的值。 但是,这种方法需要遍历整棵BST,时间复杂度为O(n),其中n是BST中节点的个数。而这题我们要求的是第k大节点,因此我们可以不必遍历整棵BST,可以在遍历BST时维护一个计数变量和一个全局变量,分别记录已经遍历的节点个数和第k大节点的值。 具体地,我们可以利用BST中序遍历得到一个按照升序排列的节点值列表。在遍历列表时,我们从最大的值开始,每次访问一个值,计数器就加1,当计数器达到k时,就求出了第k大节点的值。 代码实现: class Solution: def kthLargest(self, root: TreeNode, k: int) -> int: def inorder(node): if not node: return [] return inorder(node.left) + [node.val] + inorder(node.right) nums = inorder(root) return nums[-k]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值