Поиск пропавших дуг для полного графа

454
04 января 2017, 02:29

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

Требуется:

Написать SQL запрос, который возвращает список дуг, при добавлении которых граф станет полным.

Для начала код для тестовой таблицы (Postgresql):

CREATE TEMPORARY TABLE graph
(
    start_point INTEGER NOT NULL,
    end_point INTEGER NOT NULL
)
ON COMMIT DROP;
INSERT INTO graph VALUES
(1,2),
(1,4),
(2,3),
(3,4);

И собственно, мой вариант решения:

-- Для начала нужно получить список всех-всех дуг для данного графа
WITH a AS (
WITH points AS(
    -- Получаем все вершины, преобразовывем в массив
    SELECT array(SELECT DISTINCT start_point FROM graph ORDER BY start_point) ||
           array(SELECT DISTINCT end_point FROM graph ORDER BY end_point)
     AS allp FROM student
     LIMIT 1
)
    SELECT DISTINCT i
    FROM unnest ((SELECT * FROM points)) AS s(i)
),
all_possible_edges AS(
SELECT *
FROM
    -- И затем все эти вершины комбинируем друг с другом
    a CROSS JOIN a AS b
WHERE
    -- Это условие означает, что пропускаем 1-1. Также оно означает, что будет только дуга 1-2, но не 2-1
    -- Хотя эти дуги одинаковы по смыслу
    a < b
ORDER BY a, b
)
-- Ну и наконец берем только те дуги, которые есть в комбинациях, но которых нет в оригинальной таблице
SELECT * FROM all_possible_edges EXCEPT SELECT * FROM graph

Ответ:

>> 2;4
>> 1;3

Проблема в том, что меня не покидает чувство, что решение переусложнено. Особенно омерзительно выглядит возня с массивами. Есть ли способы решить задачу проще?

Answer 1

Для postgersql

WITH ps AS (
SELECT /* DISTINCT */ start_point AS point FROM graph
UNION /* DISTINCT */
SELECT /* DISTINCT */ end_point FROM graph
),
pe AS (
SELECT /* DISTINCT */ start_point AS point FROM graph
UNION /* DISTINCT */
SELECT /* DISTINCT */ end_point FROM graph
)
SELECT ps.point start_point, pe.point end_point
FROM ps, pe
WHERE ps.point != pe.point
EXCEPT 
SELECT start_point, end_point
FROM graph;

http://www.sqlfiddle.com/#!15/6ef70/3/0

Для MySQL

SELECT ps.point start_point, pe.point end_point
FROM (
SELECT /* DISTINCT */ start_point AS point FROM graph
UNION /* DISTINCT */
SELECT /* DISTINCT */ end_point FROM graph
) ps, (
SELECT /* DISTINCT */ start_point AS point FROM graph
UNION /* DISTINCT */
SELECT /* DISTINCT */ end_point FROM graph
) pe
WHERE ps.point != pe.point
AND (ps.point, pe.point) NOT IN
(SELECT start_point, end_point
FROM graph);

http://www.sqlfiddle.com/#!9/6ef708/4/0

READ ALSO
Как осуществить скроллинг с блоками?

Как осуществить скроллинг с блоками?

Вот, к примеру, есть всего 10 достаточно больших блоков с каким-либо фоном, которые не умещаются на экранеВ строке по 2 таких блока

467
Jsoup с одними сайтами работает, с другими - нет

Jsoup с одними сайтами работает, с другими - нет

Здравствуйте! Стал интересен парсинг html-страниц, начал разбираться с Jsoup, почитал несколько довольно стареньких гайдов по этому поводу(там...

663
Mockito тестировние void метода

Mockito тестировние void метода

Тестируемый метод

744
Передача аргументов методу

Передача аргументов методу

При передаче объекта в качестве параметра методу ссылка должна копироватьсяТогда почему вывод 0 9 9, а не 9 9 9? Получается, s1 и s2 ссылаются на разные...

574