【Leetcode&C语言】973. K Closest Points to Origin

目录

问题描述

举例说明

限制

实现

Heapify最大堆O(nlogk)

关于时间复杂度

关于使用最小堆解决的弊端


 

问题描述

We have a list of points on the plane.  Find the K closest points to the origin (0, 0).

(Here, the distance between two points on a plane is the Euclidean distance.)

You may return the answer in any order.  The answer is guaranteed to be unique (except for the order that it is in.)

 在平面上有一些点。找到K个距离(0,0)最近的点(在这道题,平面中两点之间距离是欧式距离)。

你可以以任意顺序返回答案。保证答案是唯一的(除了顺序)。

举例说明

Example 1:

Input: points = [[1,3],[-2,2]], K = 1
Output: [[-2,2]]
Explanation: 
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
We only want the closest K = 1 points from the origin, so the answer is just [[-2,2]].

Example 2:

Input: points = [[3,3],[5,-1],[-2,4]], K = 2
Output: [[3,3],[-2,4]]
(The answer [[-2,4],[3,3]] would also be accepted.)

例1:
输入:点=[[1,3],[-2,2]],K=1
输出:[[-2,2]]
说明:
(1,3)与原点之间的距离为sqrt(10)。
(-2,2)与原点之间的距离为sqrt(8)。
由于sqrt(8)<sqrt(10),(-2,2)更接近原点。
我们只需要离原点最近的K=1点,所以答案是[[-2,2]]。


例2:
输入:点=[[3,3],[5,-1],-2,4]],K=2
输出:[[3,3],[-2,4]]
(答案[[-2,4],[3,3]]也可以接受。)

限制

  1. 1 <= K <= points.length <= 10000
  2. -10000 < points[i][0] < 10000
  3. -10000 < points[i][1] < 10000

实现

Heapify最大堆O(nlogk)

 声明新的结构体Dist用来存储索引和对应索引的一维数组各元素平方和(即距离)。

之后创建长度为pointsSize(点的个数)的Dist类型的数组,存储所有点的索引和与(0,0)相差的距离。

关于时间复杂度

然后创建长度为K的Dist类型数组maxHeap,用来将所给数组的前K个点元素制成最大堆(添加,sift down,添加,sift down.... 这样的时间复杂度为O(klogk))。之后再遍历n-k次,如果距离小于maxHeap的第一个元素就进行replace的操作(单个replace的时间复杂度为O(logk))。那么这样得到的时间复杂度就是nlogk。但是!!如果使用heapify制成最大堆的情况又是怎样的呢?heapify的时间复杂度为O(k),而replace操作的时间复杂度为O((n-k)logk),哪个数量级更大?如果不好判断又该如何合并呢?

解答:如果考虑时间复杂度的上边界,则有O(K) + O((n-k)logK) <  O(K) + O(nlogk) ,所以时间复杂度为 O(K) + O(nlogk) ,进而是O(nlogk)

源自:https://leetcode.com/problems/k-closest-points-to-origin/discuss/835544/could-anyone-tell-me-the-time-complexity-of-this-code/688340

关于使用最小堆解决的弊端

我们创建了一个最多包含M个元素的堆。在这个堆上的基本操作是O(logM)的。对于每一个元素,我们都可能要在这个堆上进行一次操作,所以时间复杂度是O(NlogM)的。

 

如果你一上来对数组中的所有元素组织成一个堆,这个过程是O(N)的,之后在这个巨大的堆中取前M个元素,时间复杂度是O(MlogN)的,综合这个过程,时间复杂度是:O(N + MlogN)的。

 

后一种方法有一个缺点,或者说是限制,就是需要在算法计算初始,就已知所有的数据。在Leetcode上这个问题中,这个条件可以满足。但是在实际中,数据很有可能是一点一点流进来的,而不是一次性给出的(比如用户的下单数据);同时,由于N巨大,很有可能维持一个包含N个元素的堆是不现实的,而维持一个包含M个元素的堆,是很简单的。比如要实时统计单笔订单金额最高的前100个订单,前一种方法只需要维护一个包含100个元素的堆,而后一种方法需要维护一个包含订单总量那么多元素的堆,而订单总量可能是一个天文数字

以上文字源于https://coding.imooc.com/learn/questiondetail/145066.html

/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */

typedef struct Dist Dist;
struct Dist{
  int index;
    int distance;
};

void siftDown(Dist **dist,int size,int index)
{
    while(index*2+1<size){
        int j = index*2+1;
        if(j+1<size && dist[j+1]->distance>dist[j]->distance)
            j++;
        if(dist[index]->distance>=dist[j]->distance)
            break;
        Dist *tmp = dist[index];
        dist[index]=dist[j];
        dist[j]=tmp;
        index=j;
    }
}

int** kClosest(int** points, int pointsSize, int* pointsColSize, int K, int* returnSize, int** returnColumnSizes){
    *returnSize = K;
    Dist **dist = malloc(sizeof(Dist)*pointsSize);
    for(int i=0;i<pointsSize;i++){
        dist[i]=malloc(sizeof(Dist));
    }
    
    for(int i=0;i<pointsSize;i++)
    {
        dist[i]->index = i;
        dist[i]->distance = points[i][0]*points[i][0]+points[i][1]*points[i][1];
    }
    
    Dist **maxHeap = malloc(sizeof(Dist)*K);
    for(int i=0;i<K;i++){
        maxHeap[i]=malloc(sizeof(Dist));
        maxHeap[i]->index = dist[i]->index;
        maxHeap[i]->distance = dist[i]->distance;
    }
    for(int i=(K<=2)?1:(K-2)/2;i>=0;i--)
    {
        siftDown(maxHeap,K,i);
    }
    
    for(int i=K;i<pointsSize;i++){
        if(dist[i]->distance < maxHeap[0]->distance)
        {
            maxHeap[0]=dist[i];
            siftDown(maxHeap,K,0);
        }
    }

    *returnColumnSizes = malloc(sizeof(int)*K);
    for (int i = 0; i < K; i++)
    {
        returnColumnSizes[0][i] = 2;
    }
    
    int **ret = malloc(sizeof(int*)*K);
    for(int i=0; i < K; i++){
        ret[i] = malloc(sizeof(int)*2);
    }
    
    for(int i=0;i<K;i++){
        ret[i][0]=points[maxHeap[i]->index][0];
        ret[i][1]=points[maxHeap[i]->index][1];
    }
    
    return ret;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于问题中提到的LeetCode 1338题,这个题目是关于计算一个数组的“不可行的最小子集”,即找到一个子集,使得该子集的长度不大于原数组长度的一半,并且子集中的每个元素的出现次数都不超过原数组长度的一半。 根据引用中给出的代码,这是一个关于二叉树的中序遍历问题。其中函数inorderTraversal实现了对二叉树的中序遍历,并返回一个数组作为结果。这个数组即为题目中的原数组arr。 而引用是一个示例,提供了一个输入数组arr和对应的输出。题目要求从输入数组中选择一个子集,使得子集的长度不大于原数组长度的一半,并且子集中的每个元素的出现次数都不超过原数组长度的一半。在示例中,选择{3,7}作为子集,满足了题目的要求。 结合题目要求和示例,你可能需要根据给出的代码和示例来完成题目的求解。你可以参考代码中的中序遍历函数和示例的思路来实现对于原数组的遍历和选择子集的操作。具体实现的细节还需要你进行进一步的思考和编码。希望这些信息对你有所帮助。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [【LeetcodeC语言 94. Binary Tree Inorder Traversal](https://blog.csdn.net/LYYF177/article/details/121554117)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [LeetCode刷题记录--1338. 数组大小减半](https://blog.csdn.net/zhuyinghe/article/details/104886488)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值