K的最接近原点的点(K个最小的元素),hoare的分区未给出特定输入的正确答案

时间:2019-03-20 17:05:25

标签: c# algorithm quicksort

我正在解决以下问题:https://leetcode.com/problems/k-closest-points-to-origin/

简而言之,给定点列表,返回最接近原点的K,而K内的顺序无关紧要。我正在尝试使用Hoare的分区使用quickselect变体来解决此问题,但是对于特定的输入,它无法给出正确的答案,我无法弄清原因。 Hoare的分区逻辑本身是从维基百科复制的。

点:[[68,97],[34,-84],[60,100],[2,31],[-27,-38],[-73,-74],[-55,- 39],[62,91],[62,92],[-57,-67]] K:5

namespace N
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    public class Solution
    {
        private readonly Random rand = new Random();
        public int[][] KClosest(int[][] points, int K)
        {
            var result = new List<int[]>();
            KClosestHelper(points, 0, points.Count() - 1, K);
            return points.Take(K).ToArray();
        }

        private void KClosestHelper(int[][] points,
                                        int left,
                                        int right,
                                        int k)
        {
            if (left >= right) return;

            var partitionIndex = Partition(points, left, right);
            int leftLength = partitionIndex - left + 1;
            if (k < leftLength)
            {
                KClosestHelper(points, left, partitionIndex - 1, k);
            }
            else if (k > leftLength)
            {
                KClosestHelper(points, partitionIndex + 1, right, k - leftLength);
            }
        }

        private int Partition(int[][] arr, int left, int right)
        {
            //var randomIndex = rand.Next(left, right+1);
            //swap(arr, left, randomIndex);
            var pivot = arr[left];
            left--;
            right++;

            while (true)
            {
                do
                {
                    left++;
                } while (AIsCloserThanB(arr[left], pivot));

                do
                {
                    right--;
                } while (AIsFartherThanB(arr[right], pivot));

                if (left >= right) return right;

                swap(arr, left, right);
            }
        }

        private bool AIsCloserThanB(int[] a, int[] b)
        {
            return a[0] * a[0] - b[0] * b[0] + a[1] * a[1] - b[1] * b[1] < 0;
        }

        private bool AIsFartherThanB(int[] a, int[] b)
        {
            return a[0] * a[0] - b[0] * b[0] + a[1] * a[1] - b[1] * b[1] > 0;
        }

        private void swap(int[][] arr, int i, int j)
        {
            var temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    public class MainClass
    {
        public static void Main()
        {
            var arr = new int[10][];
            arr[0] = new[] { 68, 97 };
            arr[1] = new[] { 34, -84 };
            arr[2] = new[] { 60, 100 };
            arr[3] = new[] { 2, 31 };
            arr[4] = new[] { -27, -38 };
            arr[5] = new[] { -73, -74 };
            arr[6] = new[] { -55, -39 };
            arr[7] = new[] { 62, 91 };
            arr[8] = new[] { 62, 92 };
            arr[9] = new[] { -57, -67 };
            var s = new Solution();
            var closest = s.KClosest(arr, 5);
            foreach (var item in closest)
            {
                Console.Out.WriteLine(string.Join(",", item));
            }
        }
    }

}

在使用left == 3和right == 7的Partition调用中进行调试时,首先交换3和7(所以现在left为3,right为7)。然后,left一直到7,由于do而right变成6,它作为分区返回。但这是不正确的,因为points [5]为[-73,74],points [6]为[-57,67](所以points [5]> points [6])。我认为应该将7作为分区返回。这导致最终溶液包含[-73,-74]而不是[-57,67]。有人可以帮我理解算法的哪一部分不正确/由于维基百科逻辑必须正确而不适用于此问题?

2 个答案:

答案 0 :(得分:0)

定义Point

public class Point
{
    public int X;
    public int Y;
}

现在将您的观点放入Point[] points中并执行简单的linq查询

var closest = points.OrderBy(p => p.X * p.X + p.Y * p.Y).Take(k);

.NET的最新版本足够聪明,可以在不需要完整排序的集合时使用QuickSelect,因此预期的复杂度应为O(k log N)

答案 1 :(得分:0)

不知道结果为何,但是既然是c#,为什么不简化代码。 定义返回距离的方法

$b64image = base64_encode(file_get_contents($file));
echo '<div style="display:none;"><div id="image'.$count.'"><img src="data:image/jpg;base64,'.$b64image.'"/><p>'.$image.'</p></div></div>'; 
echo '<li><span style="color:#666; font-weight:bold"> '.$count.' </span><a href="#" id="featherlight-image" data-featherlight="#image'.$count.'" download="'.$entry.'"><span style="color:#777AFF; font-weight:bold">'.$entry.'</span></a></li>';

然后使用linq:

private static double DistanceToCenter(int[] p)
    {
        return Math.Sqrt(p[0] ^ 2 + p[1] ^ 2);
    }

这将为您提供按距离0.0排序的列表,然后首先选择k以打印出来。