Java 使用树集计算磁盘交点

Java 使用树集计算磁盘交点,java,Java,在我把真正的考试作为求职申请的一部分之前,我正在尝试一个关于可变性的演示问题。他们演示的一个问题是计算磁盘阵列的磁盘交叉数 任务描述为 给定一个由N个整数组成的数组,我们在一个二维平面上画N个圆盘,如 第I个圆盘以(0,I)为中心,半径为[I]。我们 假设第J个圆盘和第K个圆盘相交,如果J≠ K和J-th和 第K个光盘至少有一个公共点。编写一个函数:class 解{public int number_of_disc_crossions(int[]A)}表示, 如上所述,给定一个描述N个光盘的数组

在我把真正的考试作为求职申请的一部分之前,我正在尝试一个关于可变性的演示问题。他们演示的一个问题是计算磁盘阵列的磁盘交叉数

任务描述为

给定一个由N个整数组成的数组,我们在一个二维平面上画N个圆盘,如 第I个圆盘以(0,I)为中心,半径为[I]。我们 假设第J个圆盘和第K个圆盘相交,如果J≠ K和J-th和 第K个光盘至少有一个公共点。编写一个函数:class 解{public int number_of_disc_crossions(int[]A)}表示, 如上所述,给定一个描述N个光盘的数组,返回 交叉盘的对数

您可以查看测试

有一些明显的O(n^2)时间复杂度解决方案,但目标是O(n*log(n))

我提出了这个,它适用于我提供的任何示例,以及Codibility([1,5,2,1,4,0])给出的简单测试用例,但Codibility告诉我它在大多数其他示例中都失败了,但我不太明白为什么

它当然应该是O(n log n),因为将n个磁盘中的每一个添加到树集就是log n,然后我们将遍历每个磁盘,只使用O(1)操作TreeSet.header()

import java.util.*;
类循环实现了可比性{
长边;
整数指数;
圆(长e,内i){
边缘=e;
指数=i;
}
long getRightAssumingEdgeIsLeft(){
返回(长)(2*索引-边缘+1);
}
@凌驾
公共整数比较(圈出其他){
返回Long.valueOf(edge).compareTo(其他.edge);
}
}
类解决方案{
交叉口的公共整数编号(整数[]A){
int N=A.长度;

if(N第一件事:您定义了compareTo()而不是equals()。TreeSet JavaDoc说:“集合维护的顺序(无论是否提供显式比较器)必须与equals一致”

其他奇怪之处:我不明白什么是
edge
字段,也不明白为什么要将它设置为
I-A[I]

一个不同的算法(
O(N log N)
):

这是一幅糟糕的场景图:

可以转换为范围列表:(不完全相同的场景)

图2

O(N logn):我们首先对标记进行排序,如果我们想将相切圆盘计算为重叠,则要注意绿色标记出现在红色标记之前

O(N):我们从左到右扫描,初始值为
total
=0
,初始值为
=0
。每次点击绿色标记时,
total+=1
,在每个红色标记处,
total-=1
。此外,在每个绿色标记处,
如果total>0,则重叠+=total

图2中的黑色数字是每一步的总数;橙色是重叠的

然后,
重叠
应该是答案


请参见此处的粗略实现:

有一种更简单的方法

  • 创建两个包含N个元素的数组(leftEdge、rightEdge)
  • 对于每个元素,计算左边缘和右边缘(索引-/+值),并将其设置为数组
  • 排序数组
  • 对于rightEdge数组中的每个元素,通过leftEdge数组循环查找第一个较大或相等的元素。保存剩余元素的数量和当前索引。对于下一个元素,从保存的索引开始循环

  • 这样,我们实际上只在每个排序数组中循环一次,因此算法的复杂度为O(N logn)。

    假设j总是大于i,为了满足两个圆的相互作用,下面的不等式应该始终有效:

    |R(i) - R(j)| <= j - i <= R(i) + R(j)
    

    | R(i)-R(j)|我做了同样的演示,为一个编程职位做准备。我没有及时开发解决方案,结果得到了一个糟糕的分数(十几岁的时候)。然而,我对这个问题很感兴趣,我继续自己完成了它。以下是我的解决方案:

     ============================================================================
     Name        : cFundementalsTest.c
     Copyright   : Your copyright notice
     Description : Hello World in C, Ansi-style
     ============================================================================
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    
    int main(void) {
    
        int N = 5;
        int A[6] = {1, 5, 2, 1, 4, 0 };
    
            int pos_1, pos_2;
            int total;
            for(pos_1=0;pos_1<=N;pos_1++)
            {
                for(pos_2=pos_1+1;pos_2<=N;pos_2++)
                {
                    if(A[pos_1] + A[pos_2] >= abs(pos_1 - pos_2))
                    { // they share a common point
                        total++;
                        printf("%d and %d\n",pos_1, pos_2);
                        if(total > 10000000)
                            return(-1);
                    }
                }
            }
            printf ("\n\n the total is %d",total);
    }
    

    此方法不需要任何特殊类(如圆形)或复杂容器(如PriorityQueue或TreeSet)。只需要简单的整数数组。它是O(N*logN)。语言为Java

    public int numberOfDiscIntersections(int [] A) {
        // 0 <= A.length <= 100,000
        // 0 <= A[i] <= 2147483647
        int [] leftEdge = new int[A.length];
        int [] rightEdge = new int[A.length];
    
        int maxLength = 100000;
        // maxLength is used to prevent integers > 2147483647
        // and integers < -2147483647
        for (int i = 0; i < A.length; i++) {
            leftEdge[i] = i - A[i];
            rightEdge[i] = i - maxLength + A[i];
        }
        Arrays.sort(leftEdge);
        Arrays.sort(rightEdge);
    
        int sum = mergeAndCountOverlaps(leftEdge,rightEdge, maxLength);
        return sum;
    }
    
    Disc交叉口的公共int编号(int[]A){
    //0上,您需要使用排序来解决它,对吗?下面是JavaScript解决方案通过100%测试

    我们在轴
    x
    上使用两个范围数组。两个数组中的值相同,但首先按
    参数排序,其次按
    (两者均为升序)。然后我们同时扫描两个数组并跟踪重叠以计数不同的对

    您可以在中使用此代码

    功能解决方案(arr){
    常量r1=arr.map((n,i)=>({from:i-n,to:n+i})).sort((a,b)=>a.from-b.from)
    常量r2=r1.slice().sort((a,b)=>a.to-b.to)
    设i=0,j=0
    设重叠=0
    设对=0
    而(i
    类解决方案{
    公共int解决方案(int[]A){
    //用JavaSE8编写代码
    long[]leftpoint=新的long[A.length];
    long[]rigthpoint=新长[A.长度];
    
    如果(A.length)边缘有点不清楚。该字段通常表示光盘的左边缘-它是i-A[i],因为i是中心,A[i]是半径。我也会尝试实现equals,但我没有想到。我认为TreeSet只使用compareTo,而实现equals是使代码经得起未来考验的良好实践。如果磁盘的坐标是(0,I)(传统上是(x,y)坐标),那么它们是垂直对齐的,所以我不明白为什么要计算左边缘。即使这只是一个词汇,我也不明白按边缘位置对圆排序如何解决相交问题(应该在半径之和小于圆之间的距离时解决)。这是真的,我说
    #include <stdlib.h>
    
    public int number_of_disc_intersections(int[] A){
    
        int len = A.length;
        int intersections = 0;
    
        for(int i = 0; i < len - 1; i++){
    
            if(A[i] <= 0){
                continue;
            }
    
            for(int j = i + 1; j < len; j++){
    
                if(A[j] <= 0){
                    continue;       
                }
    
                if(abs((A[i] - A[j]) <= j - i) && (j - i <= A[i] + A[j])){
                    intersections ++;
                }
            }
        }
    
        return intersections;
    }
    
     ============================================================================
     Name        : cFundementalsTest.c
     Copyright   : Your copyright notice
     Description : Hello World in C, Ansi-style
     ============================================================================
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    
    int main(void) {
    
        int N = 5;
        int A[6] = {1, 5, 2, 1, 4, 0 };
    
            int pos_1, pos_2;
            int total;
            for(pos_1=0;pos_1<=N;pos_1++)
            {
                for(pos_2=pos_1+1;pos_2<=N;pos_2++)
                {
                    if(A[pos_1] + A[pos_2] >= abs(pos_1 - pos_2))
                    { // they share a common point
                        total++;
                        printf("%d and %d\n",pos_1, pos_2);
                        if(total > 10000000)
                            return(-1);
                    }
                }
            }
            printf ("\n\n the total is %d",total);
    }
    
    0 and 1
    0 and 2
    0 and 4
    1 and 2
    1 and 3
    1 and 4
    1 and 5
    2 and 3
    2 and 4
    3 and 4
    4 and 5
    
     the total is 11
    
    public int numberOfDiscIntersections(int [] A) {
        // 0 <= A.length <= 100,000
        // 0 <= A[i] <= 2147483647
        int [] leftEdge = new int[A.length];
        int [] rightEdge = new int[A.length];
    
        int maxLength = 100000;
        // maxLength is used to prevent integers > 2147483647
        // and integers < -2147483647
        for (int i = 0; i < A.length; i++) {
            leftEdge[i] = i - A[i];
            rightEdge[i] = i - maxLength + A[i];
        }
        Arrays.sort(leftEdge);
        Arrays.sort(rightEdge);
    
        int sum = mergeAndCountOverlaps(leftEdge,rightEdge, maxLength);
        return sum;
    }
    
    private int mergeAndCountOverlaps(int[] leftEdge, int [] rightEdge, int maxLength) {
        int leftIndex = 0;
        int rightIndex = 0;
        int sum = 0;
        int total = 0;
        while ((leftIndex < leftEdge.length) || (rightIndex < rightEdge.length)) {
            if ((leftIndex < leftEdge.length) && (rightIndex < rightEdge.length)) {
                boolean compareLeftEdgeandRightEdge;
                if (leftEdge[leftIndex] < -2147483647 + maxLength) {
                    compareLeftEdgeandRightEdge = leftEdge[leftIndex] <= rightEdge[rightIndex] + maxLength;
                } else {
                    compareLeftEdgeandRightEdge = leftEdge[leftIndex] - maxLength <= rightEdge[rightIndex];
                }
                if (compareLeftEdgeandRightEdge) {
                    // a new left edge
                    sum += total;
                    if (sum > 10000000) {
                        return -1;
                    }
                    total++;
                    leftIndex++;
                } else {
                    // a new right edge
                    total--;
                    rightIndex++;
                }
            } else if (leftIndex < leftEdge.length) {
                // a new left edge
                sum += total;
                if (sum > 10000000) {
                    return -1;
                }
                total++;
                leftIndex++;
            } else if (rightIndex < rightEdge.length) {
                // a new right edge
                total--;
                rightIndex++;
            }
        }
        return sum;
    }
    
    function solution(arr) {
      const r1 = arr.map((n, i) => ({from: i - n, to: n + i})).sort((a, b) => a.from - b.from)
      const r2 = r1.slice().sort((a, b) => a.to - b.to)
      let i = 0, j = 0
      let overlaps = 0
      let pairs = 0
    
      while (i < r1.length || j < r2.length) {
        if (i < r1.length && r1[i].from <= r2[j].to) {
          pairs += overlaps
          if (pairs > 10000000) {
            return -1
          }
          overlaps++
          i++
        } else {
          overlaps--
          j++
        }
      }
      return pairs
    }
    
    class Solution {
        public int solution(int[] A) {
            // write your code in Java SE 8
            long[] leftpoint=new long[A.length];
            long[] rigthpoint=new long[A.length];
            if(A.length<2)return 0;
        
            for(int i=0;i<A.length;++i){
                leftpoint[i]=-A[i];
                leftpoint[i]+=i;
                rigthpoint[i]=A[i];
                rigthpoint[i]+=i;
            }
            Arrays.sort(leftpoint);
            Arrays.sort(rigthpoint);
            int intersection =0;
            int j=0;
            int i=0;
            int open=0;
            while(i<A.length&&j<A.length){
                 if(rigthpoint[i]>=leftpoint[j]){
                       intersection+=open; 
                       open++; 
                       j++;
                 }else{
                     open--;
                     i++;
                 }
                 if(intersection>10000000)return -1;
                }
            return intersection;
        }
    }