Хобрук: Ваш путь к мастерству в программировании

Как отсортировать набор точек, чтобы они устанавливались друг за другом?

У меня есть ArrayList, который содержит координаты точек:

class Point
{
   int x, y;
}
ArrayList<Point> myPoints;

такого изображения, например:

введите здесь описание изображения

Проблема в том, что эти точки заданы хаотично в ArrayList и хотелось бы отсортировать их так, чтобы 2 точки, лежащие рядом на изображении, были и в ArrayList друг за другом. Я не могу придумать какой-нибудь хорошей идеи или алгоритма для решения такой сортировки... Есть ли какие-то известные методы решения таких задач?

редактировать: форма не может пересекать сама себя, и давайте предположим, что могут встречаться только формы, похожие на это.


  • Что вы пробовали? Это единственный ввод, который вы когда-либо получите? Что должно произойти, если фигура пересечет сама себя? Этот вопрос слишком широк для StackOverflow и, вероятно, будет закрыт. 13.08.2014
  • Форма не может пересечься сама с собой, и давайте предположим, что могут встречаться только формы, похожие на эту. Как это может быть слишком широким?... Слишком широкий алгоритм программирования? 13.08.2014
  • С такими формами, как показано выше, вы, вероятно, можете выбрать произвольную точку, затем найти ближайшую точку (в евклидовом пространстве) и так далее. Для более сложных форм этого может быть недостаточно, и в этом случае вам может потребоваться учитывать текущий градиент. Даже это будет иметь пределы. 13.08.2014
  • @Ms.Nobody: Алгоритм Дейкстры касается коротких путей в графах, верно? Как это связано с этим? 13.08.2014
  • Да ладно, близкие избиратели, я был за одну минуту до того, как опубликовать ответ, содержащий MVCE... :-( 13.08.2014
  • ... Почему небольшая группа людей, мыслящих иначе, может закрыть ветку, которая на самом деле вызвала интерес у многих людей? это просто неправильно 13.08.2014
  • Выбираю начальную точку P(0), затем на каждом шаге ищу P(i+1) такую, чтобы она была геометрически ближайшей к P(i), исключая точки, которые уже перенесены в отсортированный массив. Сложность O (n ^ 2). 13.08.2014
  • Я проголосовал, потому что думаю, что это будет хороший вопрос для интервью. Мне нравится задавать вопросы на интервью, когда информация немного неполная, но заставляет обсуждать и думать, основанные на предположениях (надеюсь, они подтвердят свои предположения вместе со мной). 14.08.2014

Ответы:


1

Я думаю, что вам сначала нужно математическое определение вашего заказа. Я предлагаю (обратите внимание, это определение не было ясно в исходном вопросе, оставленном здесь для полноты):

Начиная с размещения любой точки в последовательности, затем постоянно добавляйте к последовательности точку, ближайшую к текущей точке и еще не добавленную к последовательности, пока все точки не будут добавлены к последовательности.

Таким образом, с этим определением порядка вы можете вывести простой алгоритм для этого

ArrayList<point> orderedList = new ArrayList<point>();

orderedList.add(myList.remove(0)); //Arbitrary starting point

while (myList.size() > 0) {
   //Find the index of the closest point (using another method)
   int nearestIndex=findNearestIndex(orderedList.get(orderedList.size()-1), myList);

   //Remove from the unorderedList and add to the ordered one
   orderedList.add(myList.remove(nearestIndex));
}

Приведенное выше довольно универсально (независимо от алгоритма нахождения следующей точки). Тогда метод «findNearestIndex» может быть определен как:

//Note this is intentially a simple algorithm, many faster options are out there
int findNearestIndex (point thisPoint, ArrayList listToSearch) {
    double nearestDistSquared=Double.POSITIVE_INFINITY;
    int nearestIndex;
    for (int i=0; i< listToSearch.size(); i++) {
        point point2=listToSearch.get(i);
        distsq = (thisPoint.x - point2.x)*(thisPoint.x - point2.x) 
               + (thisPoint.y - point2.y)*(thisPoint.y - point2.y);
        if(distsq < nearestDistSquared) {
            nearestDistSquared = distsq;
            nearestIndex=i;
        }
    }
    return nearestIndex;
}

Обновление: поскольку вопрос был пересмотрен, чтобы в значительной степени принять определение, которое я использовал, я убрал некоторые предостережения.

13.08.2014

2

Вот возможное решение для вас: наша цель состоит в том, чтобы построить путь, который посещает каждую из точек в вашем списке ровно один раз, прежде чем он вернется обратно. Мы можем создавать пути рекурсивно: мы можем выбрать любую точку из исходного списка в качестве начальной точки и создать тривиальный путь, состоящий только из одного узла. Затем мы можем расширить уже построенный путь, добавив еще не посещенную точку.

Затем мы предполагаем, что можем найти хороший порядок исходного списка точек, убедившись, выбрав путь с наименьшей длиной. Здесь под длиной я подразумеваю не количество точек на пути, а общую сумму евклидовых расстояний между каждой парой соседних точек на пути.

Единственная проблема: учитывая такой путь, какую точку мы должны добавить следующей? Теоретически нам придется перепробовать все возможности, чтобы увидеть, какая из них ведет к наилучшему общему пути.

Главный трюк, который использует приведенный ниже код, заключается в том, что он использует следующую эвристику: на каждом шаге, где мы должны добавить новую точку к построенному пути, выбираем точку, которая минимизирует среднее расстояние между двумя соседними точками.

Следует отметить, что было бы плохой идеей включать в это «петлевое расстояние» между последней точкой пути и первой точкой: по мере того, как мы продолжаем добавлять точки, мы удаляемся от первой точки пути все больше и больше. Если бы мы включили расстояние между двумя конечными точками, это серьезно повлияло бы на среднее расстояние между всеми соседними парами и, таким образом, повредило бы нашей эвристике.

Вот простой вспомогательный класс для реализации конструкции пути, описанной выше:

/**
 * Simple recursive path definition: a path consists 
 * of a (possibly empty) prefix and a head point.
 */
class Path {
    private Path prefix;
    private Point head;
    private int size;
    private double length;

    public Path(Path prefix, Point head) {
        this.prefix = prefix;
        this.head = head;

        if (prefix == null) {
            size = 1;
            length = 0.0;
        } else {
            size = prefix.size + 1;

            // compute distance from head of prefix to this new head
            int distx = head.x - prefix.head.x;
            int disty = head.y - prefix.head.y;
            double headLength = Math.sqrt(distx * distx + disty * disty);

            length = prefix.length + headLength;
        }
    }
}

А вот собственно алгоритм эвристического поиска.

/**
 * Implements a search heuristic to determine a sort
 * order for the given <code>points</code>.
 */
public List<Point> sort(List<Point> points) {
    int len = points.size();

    // compares the average edge length of two paths
    Comparator<Path> pathComparator = new Comparator<Path>() {
        public int compare(Path p1, Path p2) {
            return Double.compare(p1.length / p1.size, p2.length / p2.size);
        }
    };

    // we use a priority queue to implement the heuristic
    // of preferring the path with the smallest average
    // distance between its member points
    PriorityQueue<Path> pq = new PriorityQueue<Path>(len, pathComparator);
    pq.offer(new Path(null, points.get(0)));

    List<Point> ret = new ArrayList<Point>(len);
    while (!pq.isEmpty()) {
        Path path = pq.poll();

        if (path.size == len) {
            // result found, turn path into list
            while (path != null) {
                ret.add(0, path.head);
                path = path.prefix;
            }
            break;
        }

        loop:
        for (Point newHead : points) {
            // only consider points as new heads that
            // haven't been processed yet
            for (Path check = path; check != null; check = check.prefix) {
                if (newHead == check.head) {
                    continue loop;
                }
            }

            // create new candidate path
            pq.offer(new Path(path, newHead));
        }
    }

    return ret;
}

Если вы запустите этот код на выборочных точках вашего вопроса, а затем соедините каждую соседнюю пару точек из возвращенного списка, вы получите следующую картину:

введите здесь описание изображения

13.08.2014
  • вау, похоже на еще одно сложное решение! Много испытаний впереди меня. Кстати, что такое newPath в вашем коде? Я думал, что это просто упущение, и замена его указанным выше путем показалась мне разумной, но код дает тогда исключение памяти. Я постараюсь разобраться и завтра более подробно рассмотрю все решения, а также проведу некоторые тесты производительности. 14.08.2014
  • Да, вы совершенно правы: я внес некоторые изменения в свой ответ, но на самом деле не скомпилировал его, поэтому появился артефакт newPath. Я исправил код соответственно. 14.08.2014
  • Однако, если подумать, я думаю, вам следует согласиться с ответом Jared. Я думал, что дополнительные накладные расходы, которые вносит мой ответ по сравнению с его, были оправданы, потому что моя версия могла лучше обрабатывать определенные особые случаи. Но теперь я думаю, что это того не стоит, и вам лучше с его подходом. 14.08.2014

  • 3

    Это не алгоритм Sort — это скорее перестановка для минимизации метрики (расстояния между последовательными точками).

    Я бы попробовал какой-то эвристический алгоритм - что-то вроде:

    1. Выберите три последовательные точки a, b, c.
    2. Если расстояние (a, c) ‹ расстояние (a, b), то поменять местами (a, b).
    3. Повторить с 1.

    Должна быть возможность рассчитать, сколько раз вам нужно будет выполнить цикл, чтобы достичь минимального расположения, или, возможно, вы могли бы определить минимальное расположение, не обнаружив никаких свопов во время прогона.

    Возможно, вам придется изменить направление ваших движений, как при классической оптимизации пузырьковой сортировки.

    Добавлено

    Эксперимент показывает, что этот алгоритм не работает, но я нашел такой, который работает. По сути, для каждой записи в списке найдите ближайшую другую точку и переместите ее в следующее место.

    private static class Point {
    
        final int x;
        final int y;
    
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    
        public String toString() {
            return "(" + x + "," + y + ")";
        }
    
        public double distance(Point b) {
            int dx = x - b.x;
            int dy = y - b.y;
            // Simple cartesian distance.
            return Math.sqrt(dx * dx + dy * dy);
        }
    }
    
    // Sample test data - forms a square.
    Point[] points = new Point[]{
        new Point(0, 0),
        new Point(0, 1),
        new Point(0, 2),
        new Point(0, 3),
        new Point(0, 4),
        new Point(0, 5),
        new Point(0, 6),
        new Point(0, 7),
        new Point(0, 8),
        new Point(0, 9),
        new Point(1, 9),
        new Point(2, 9),
        new Point(3, 9),
        new Point(4, 9),
        new Point(5, 9),
        new Point(6, 9),
        new Point(7, 9),
        new Point(8, 9),
        new Point(9, 9),
        new Point(9, 8),
        new Point(9, 7),
        new Point(9, 6),
        new Point(9, 5),
        new Point(9, 4),
        new Point(9, 3),
        new Point(9, 2),
        new Point(9, 1),
        new Point(9, 0),
        new Point(8, 0),
        new Point(7, 0),
        new Point(6, 0),
        new Point(5, 0),
        new Point(4, 0),
        new Point(3, 0),
        new Point(2, 0),
        new Point(1, 0),};
    
    public void test() {
        System.out.println("Hello");
        List<Point> test = Arrays.asList(Arrays.copyOf(points, points.length));
        System.out.println("Before: " + test);
        Collections.shuffle(test);
        System.out.println("Shuffled: " + test);
        List<Point> rebuild = new ArrayList<>(test);
        rebuild.add(0, new Point(0, 0));
        rebuild(rebuild);
        rebuild.remove(0);
        System.out.println("Rebuilt: " + rebuild);
    }
    
    private void rebuild(List<Point> l) {
        for (int i = 0; i < l.size() - 1; i++) {
            Point a = l.get(i);
            // Find the closest.
            int closest = i;
            double howClose = Double.MAX_VALUE;
            for (int j = i + 1; j < l.size(); j++) {
                double howFar = a.distance(l.get(j));
                if (howFar < howClose) {
                    closest = j;
                    howClose = howFar;
                }
            }
            if (closest != i + 1) {
                // Swap it in.
                Collections.swap(l, i + 1, closest);
            }
        }
    }
    

    печатает:

    Before: [(0,0), (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7), (0,8), (0,9), (1,9), (2,9), (3,9), (4,9), (5,9), (6,9), (7,9), (8,9), (9,9), (9,8), (9,7), (9,6), (9,5), (9,4), (9,3), (9,2), (9,1), (9,0), (8,0), (7,0), (6,0), (5,0), (4,0), (3,0), (2,0), (1,0)]
    Shuffled: [(9,6), (0,9), (0,8), (3,9), (0,5), (9,4), (0,7), (1,0), (5,0), (9,3), (0,1), (3,0), (1,9), (8,9), (9,8), (2,0), (2,9), (9,5), (5,9), (9,7), (6,0), (0,3), (0,2), (9,1), (9,2), (4,0), (4,9), (7,9), (7,0), (8,0), (6,9), (0,6), (0,4), (9,0), (0,0), (9,9)]
    Rebuilt: [(0,0), (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7), (0,8), (0,9), (1,9), (2,9), (3,9), (4,9), (5,9), (6,9), (7,9), (8,9), (9,9), (9,8), (9,7), (9,6), (9,5), (9,4), (9,3), (9,2), (9,1), (9,0), (8,0), (7,0), (6,0), (5,0), (4,0), (3,0), (2,0), (1,0)]
    

    который выглядит как то, что вы ищете.

    Эффективность алгоритма не очень хорошая - где-то около O(n log n) - надеюсь, вам не нужно делать это миллионы раз.

    Если вы хотите, чтобы точки отображались в предсказуемом порядке (скажем, самая левая в начале), вы можете добавить поддельную точку в начало списка перед его перестройкой и удалить ее после. Алгоритм всегда будет оставлять первую точку в покое.

    13.08.2014

    4

    Я начал это вскоре после вопроса, но это было отложено из-за того, что вопрос был отложен. Это простой подход, который также упоминался в комментариях и других ответах, но я все равно опубликую его здесь:

    Вот MCVE, показывающий самый простой и понятный подход. Подход просто состоит в том, чтобы выбрать произвольную точку, а затем продолжить, всегда выбирая точку, ближайшую к предыдущей. Конечно, это имеет ограничения:

    • Он может выбрать неправильную точку, когда есть острые углы или выступы.
    • Это не очень эффективно, потому что он постоянно ищет ближайшую точку.

    Одним из подходов к ускорению может быть сортировка точек по координате x, а затем использование этого частичного упорядочения для пропуска большинства точек при поиске следующего соседа. Но если вы не хотите применять это к десяти тысячам точек в критическом по времени контексте, это не должно быть проблемой.

    Возможные неоднозначности, в свою очередь, могут быть проблемой, но, учитывая это, нужно сказать, что проблема в любом случае недоопределена. В некоторых случаях даже человек не мог решить, какая точка является подходящей «следующей» точкой — по крайней мере, когда проблема не расширена для обнаружения «внутренней/внешней» формы (это чем-то похоже на проблему неоднозначности в алгоритме марширующего куба: вы просто не знаете каков предполагаемый путь).

    Обратите внимание, что большая часть кода не очень важна для вашего фактического вопроса, но... вы не предоставили такую ​​реализацию "заглушки". Соответствующая часть === marked ===

    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.Shape;
    import java.awt.geom.Area;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.Path2D;
    import java.awt.geom.PathIterator;
    import java.awt.geom.Point2D;
    import java.awt.geom.Rectangle2D;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Iterator;
    import java.util.List;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class SortShapePoints
    {
        public static void main(String[] args)
        {
            SwingUtilities.invokeLater(new Runnable()
            {
                @Override
                public void run()
                {
                    createAndShowGUI();
                }
            });
        }
    
        private static void createAndShowGUI()
        {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            Shape shape = createExampleShape();
            List<Point2D> points = computePoints(shape, 6);
            Collections.shuffle(points);
    
            List<Point2D> sortedPoints = sortPoints(points);
            Path2D path = createPath(sortedPoints, true);
            f.getContentPane().add(new ShapePanel(points, path));
    
            f.setSize(800, 800);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    
        //=== Relevant part starts here =========================================
    
        private static List<Point2D> sortPoints(List<Point2D> points)
        {
            points = new ArrayList<Point2D>(points);
            List<Point2D> sortedPoints = new ArrayList<Point2D>();
            Point2D p = points.remove(0);
            sortedPoints.add(p);
            while (points.size() > 0)
            {
                int index = indexOfClosest(p, points);
                p = points.remove(index);
                sortedPoints.add(p);
            }
    
            return sortedPoints;
        }
    
        private static int indexOfClosest(Point2D p, List<Point2D> list)
        {
            double minDistanceSquared = Double.POSITIVE_INFINITY;
            int minDistanceIndex = -1;
            for (int i = 0; i < list.size(); i++)
            {
                Point2D other = list.get(i);
                double distanceSquared = p.distanceSq(other);
                if (distanceSquared < minDistanceSquared)
                {
                    minDistanceSquared = distanceSquared;
                    minDistanceIndex = i;
                }
            }
            return minDistanceIndex;
        }
    
        //=== Relevant part ends here ===========================================
    
    
        private static Shape createExampleShape()
        {
            Area a = new Area();
            a.add(new Area(new Ellipse2D.Double(200, 200, 200, 100)));
            a.add(new Area(new Ellipse2D.Double(260, 160, 100, 500)));
            a.add(new Area(new Ellipse2D.Double(220, 380, 180, 60)));
            a.add(new Area(new Rectangle2D.Double(180, 520, 260, 40)));
            return a;
        }
    
        private static List<Point2D> computePoints(Shape shape, double deviation)
        {
            List<Point2D> result = new ArrayList<Point2D>();
            PathIterator pi = shape.getPathIterator(null, deviation);
            double[] coords = new double[6];
            Point2D newPoint = null;
            Point2D previousMove = null;
            Point2D previousPoint = null;
            while (!pi.isDone())
            {
                int segment = pi.currentSegment(coords);
                switch (segment)
                {
                case PathIterator.SEG_MOVETO:
                    previousPoint = new Point2D.Double(coords[0], coords[1]);
                    previousMove = new Point2D.Double(coords[0], coords[1]);
                    break;
    
                case PathIterator.SEG_CLOSE:
                    createPoints(previousPoint, previousMove, result, deviation);
                    break;
    
                case PathIterator.SEG_LINETO:
                    newPoint = new Point2D.Double(coords[0], coords[1]);
                    createPoints(previousPoint, newPoint, result, deviation);
                    previousPoint = new Point2D.Double(coords[0], coords[1]);
                    break;
    
                case PathIterator.SEG_QUADTO:
                case PathIterator.SEG_CUBICTO:
                default:
                    // Should never occur
                    throw new AssertionError("Invalid segment in flattened path!");
                }
                pi.next();
            }
            return result;
        }
    
        private static void createPoints(Point2D p0, Point2D p1,
            List<Point2D> result, double deviation)
        {
            double dx = p1.getX() - p0.getX();
            double dy = p1.getY() - p0.getY();
            double d = Math.hypot(dx, dy);
            int steps = (int) Math.ceil(d / deviation);
            for (int i = 0; i < steps; i++)
            {
                double alpha = (double) i / steps;
                double x = p0.getX() + alpha * dx;
                double y = p0.getY() + alpha * dy;
                result.add(new Point2D.Double(x, y));
            }
        }
    
        public static Path2D createPath(Iterable<? extends Point2D> points,
            boolean close)
        {
            Path2D path = new Path2D.Double();
            Iterator<? extends Point2D> iterator = points.iterator();
            boolean hasPoints = false;
            if (iterator.hasNext())
            {
                Point2D point = iterator.next();
                path.moveTo(point.getX(), point.getY());
                hasPoints = true;
            }
            while (iterator.hasNext())
            {
                Point2D point = iterator.next();
                path.lineTo(point.getX(), point.getY());
            }
            if (close && hasPoints)
            {
                path.closePath();
            }
            return path;
        }
    
    }
    
    class ShapePanel extends JPanel
    {
        private final List<Point2D> points;
        private final Shape shape;
    
        public ShapePanel(List<Point2D> points, Shape shape)
        {
            this.points = points;
            this.shape = shape;
        }
    
        @Override
        protected void paintComponent(Graphics gr)
        {
            super.paintComponent(gr);
            Graphics2D g = (Graphics2D) gr;
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            g.setColor(Color.RED);
            g.draw(shape);
            g.setColor(Color.BLACK);
            for (Point2D p : points)
            {
                g.fill(new Ellipse2D.Double(p.getX() - 1, p.getY() - 1, 2, 2));
            }
        }
    }
    
    14.08.2014
  • Чем это отличается от ответа Jared? 14.08.2014
  • @Thomas Как упоминалось выше: я начал (и уже почти закончил) это, когда появился баннер «Этот вопрос был закрыт». (Довольно раздражает, учитывая, что вопрос уже получил 3 голоса тогда и 9 сейчас!). Я не хотел, чтобы это потерялось, хотя аналогичные ответы были опубликованы за это время. Это MVCE, возможно, это также побуждает других опробовать другие (лучшие) подходы путем соответствующей реализации метода sortPoints. 14.08.2014

  • 5

    Это довольно открытый вопрос, но если вы хотите, чтобы они хранились определенным образом, вам нужно определить порядок больше, чем «Чтобы они были рядом друг с другом в массиве». Вам нужна функция, в которой вы можете взять две точки и скажем, точка А меньше точки Б или наоборот, или они равны.

    Если у вас это есть, то алгоритм, который вам нужен для их сортировки, уже реализован, и вы можете использовать его, внедрив компаратор, как сказал SANN3.

    В качестве примечания: вы можете не захотеть хранить фигуру в виде набора точек. Я думаю, вы могли бы сохранить их в виде строки? Вы можете использовать кубический сплайн, чтобы получить почти любую форму, которую вы хотите, тогда вы сможете сэкономить место в памяти...

    13.08.2014

    6

    У меня была задача отсортировать точки для представления линии. Я решил сохранить полный вес пути и соответственно обновить его при стандартных Collection операциях. Решение должно работать и в вашем случае. Просто возьмите элементы этого LinkedList ps и соедините его начало и конец. Кроме того, вы можете добавить больше операций, таких как PointXY get(int index) и т. д., с немного большей переадресацией в базовый LinkedList в этой композиции. Наконец, вы должны охранять коллекцию с помощью чрезмерного количества защитных копий, где это необходимо. Я старался быть простым для краткости.

    import java.util.Collection;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.Iterator;
    import java.util.LinkedList;
    
    public class ContinuousLineSet implements Collection<PointXY> {
      LinkedList<PointXY> ps = new LinkedList<>(); // Exposed for simplicity
      private int fullPath = 0; // Wighted sum of all edges in ps
    
      @Override
      public int size() {
        return ps.size();
      }
    
      @Override
      public boolean isEmpty() {
        return ps.isEmpty();
      }
    
      @Override
      public boolean contains(Object o) {
        return ps.contains(o);
      }
    
      @Override
      public Iterator<PointXY> iterator() {
        return ps.iterator();
      }
    
      @Override
      public Object[] toArray() {
        return ps.toArray();
      }
    
      @Override
      public <T> T[] toArray(T[] a) {
        return ps.toArray(a);
      }
    
      private int dist(PointXY a, PointXY b) {
        return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
      }
    
      @Override
      public boolean add(PointXY e) {
        if (isEmpty())
          return ps.add(e);
    
        if (ps.getFirst().equals(e))
          return false;
    
        Iterator<PointXY> it = ps.iterator();
        PointXY previous = it.next();
        int asFirst = fullPath + dist(e, previous);
        int minPath = asFirst;
        int iMin = 0;
        int i = 0;
        while (it.hasNext()) {
          i++;
          PointXY next = it.next();
          if (next.equals(e))
            return false;
    
          int asBetween = fullPath - dist(previous, next) + dist(previous, e) + dist(e, next);
          if (asBetween < minPath) {
            iMin = i;
            minPath = asBetween;
          }
    
          previous = next;
        }
    
        int asLast = fullPath + dist(e, previous);
        if (asLast < minPath) {
          minPath = asLast;
          iMin = size();
        }
    
        fullPath = minPath;
        ps.add(iMin, e);
        return true;
      }
    
      public void reverse() {
        Collections.reverse(ps);
      }
    
      @Override
      public boolean remove(Object o) {
        PointXY last = null;
        for (Iterator<PointXY> it = iterator(); it.hasNext();) {
          PointXY p = it.next();
          if (o.equals(p)) {
            int part1 = last != null ? dist(last, p) : 0;
            int part2 = it.hasNext() ? dist(p, it.next()) : 0;
            fullPath -= part1 + part2;
            break;
          }
    
          last = p;
        }
    
        return ps.remove(o);
      }
    
      @Override
      public boolean containsAll(Collection<?> c) {
        return ps.containsAll(c);
      }
    
      @Override
      public boolean addAll(Collection<? extends PointXY> c) {
        boolean wasAdded = false;
        for (PointXY p : c) {
          wasAdded |= add(p);
        }
    
        return wasAdded;
      }
    
      @Override
      public boolean removeAll(Collection<?> c) {
        boolean wasRemoved = false;
        for (Object o : c) {
          if (o instanceof PointXY) {
            PointXY p = (PointXY) o;
            wasRemoved |= remove(p);
          }
        }
    
        return wasRemoved;
      }
    
      @Override
      public boolean retainAll(Collection<?> c) {
        ContinuousLineSet cls = new ContinuousLineSet();
    
        for (Object o : c) {
          if (o instanceof PointXY && ps.contains(o)) {
            PointXY p = (PointXY) o;
            cls.add(p);
          }
        }
    
        int oldSize = ps.size();
        ps = cls.ps;
        fullPath = cls.fullPath;
    
        return size() != oldSize;
      }
    
      @Override
      public void clear() {
        ps.clear();
        fullPath = 0;
      }   
    }
    
    class PointXY { 
      public static PointXY of(int x, int y) {
        return new PointXY(x, y);
      }
    
      public final int x, y;
      private int hash;
      private boolean wasHashInit = false;
    
      private PointXY(int x, int y) {
        this.x = x;
        this.y = y;
      }
    
      @Override
      public boolean equals(Object obj) {
        if (!(obj instanceof PointXY))
          return false;
        PointXY p = (PointXY) obj;
        return x == p.x && y == p.y;
      }
    
      @Override
      public int hashCode() {
        if (!wasHashInit) {
          hash = 17;
          hash = 31 * hash + y;
          hash = 31 * hash + x;
          wasHashInit = true;
        }
    
        return hash;
      }
    
      @Override
      public String toString() {
        return String.format("(%d, %d)", x, y);
      }
    }
    
    20.12.2019

    7

    открытый класс Point реализует Comparable

    { ...

    ... @Переопределить

    общественный интервал сравнения с (Pointarg0) {

        ....
    

    }

    ... }

    ...

    14.08.2014
    Новые материалы

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

    Как построить любой стол
    Я разработчик программного обеспечения. Я люблю делать вещи и всегда любил. Для меня программирование всегда было способом создавать вещи, используя только компьютер и мое воображение...

    Обзор: Машинное обучение: классификация
    Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

    Разработка расширений Qlik Sense с qExt
    Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..