M>Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
M>Notice that the solution set must not contain duplicate triplets.
Дошло. Я исходил из того, что дубликаты — это по индексам элементов, а на самом деле — по значениям элементов. Меня сбило с толку условие "i != j, i != k, and j != k", которое вообще-то говорит только о том, что это должны быть разные элементы, и не более того
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.
ЗЫ Я индексы упорядочиваю в обоих своих варантах, если не упорядочивать, то в первом кейсе вариантов становится еще больше, во втором тоже больше, но не на один, а на много
M>Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
M>Notice that the solution set must not contain duplicate triplets.
M>Что я не так понял?
M>ЗЫ Я индексы упорядочиваю в обоих своих варантах, если не упорядочивать, то в первом кейсе вариантов становится еще больше, во втором тоже больше, но не на один, а на много
А входные данные сортировать можно?
Я бы сделал так:
1. Упорядочить входные данные по возрастанию.
2. Три индекса — один указывает на левый элемент, третий — на правый, а второй — бегает между первым и третим (выполняет кастрированный двоичный поиск).
Псевдокод примерно такой:
// Входные данные после сортировки:
0 1 2 3 4 5 6 7 8 9 10
+--+--+--+--+--+--+--+--+--+--+--+
|-4|-3|-2|-1|-1| 0| 0| 1| 2| 3| 4|
+--+--+--+--+--+--+--+--+--+--+--+
int idx1 = 0,
idx2 = 5,
idx3 = 10;
while(idx1 < idx3) // Пока интервал не схлопнулся
{
idx3 = 10; // правую границу на исходную
idx2 = (idx1 + idx3)/2; // "бегунок" поставить куда-то в середину интервалаwhile(true) // Начинаем двигать правую границу интервала навстречу левой
{
// Найти положение idx2, при котором nums[idx1] + nums[idx2] + nums[idx3] == 0
...
assert(idx1 < idx2 && idx2 < idx3); // idx2 не выходит за границы
// Если нашел подходящий idx2, то добавить найденный набор в копилку.
--idx3; // Сдвинуть правую границу интервалаif (((nums[idx1] + nums[idx3]) > -nums[idx1]) || ((nums[idx1] + nums[idx3]) < -nums[idx3]))
break; // в интервале нет подходящего слагаемого, можно закончить перебор
}
++idx1; // Сдвинуть левую границу интервала
}
В триплетах элементы будут всегда упорядочены, как в тестовых наборах.
Если меня чюйка не подводит, то тут даже хеш не потребуется — достаточно перед добавлением в копилку проверить последний элемент (но это неточно!).
При отладке на бумажке получаются все триплеты, как в Expected.
Прикольная задачка.
_____________________
С уважением,
Stanislav V. Zudin
M>>Что я не так понял?
M>>ЗЫ Я индексы упорядочиваю в обоих своих варантах, если не упорядочивать, то в первом кейсе вариантов становится еще больше, во втором тоже больше, но не на один, а на много
SVZ>А входные данные сортировать можно? SVZ>Я бы сделал так: SVZ>1. Упорядочить входные данные по возрастанию. SVZ>2. Три индекса — один указывает на левый элемент, третий — на правый, а второй — бегает между первым и третим (выполняет кастрированный двоичный поиск).
Да, идея интересная.
Но получается, что тестовые данные подогнаны под решение, а не под условие. Вот в моем выводе, который я оставил — что с ним не так?
Здравствуйте, Marty, Вы писали:
M>Но получается, что тестовые данные подогнаны под решение, а не под условие.
Не совсем, это просто побочный эффект.
Если я правильно понял условие, то триплеты [-1,0,1], [0,-1,1] и [1,-1,0] эквивалентны.
На упорядоченных данных проще искать и сравнивать, видимо поэтому в тестовых результатах выходят упорядоченные триплеты.
_____________________
С уважением,
Stanislav V. Zudin
M>>https://leetcode.com/problems/3sum/
AD>Решил глянуть что вообще такое leetcode на этом примере
AD>Мой вариант на расте. PS: Возможно что-то неправильно понял в задании + не проверял
Не очень понял, а какая сложность в итоге?
Чтобы проверить, надо засабмитить на литкоде
AD>
AD>
AD>PPS: поправил форматирование, чтобы было красивее )
Блин, это какой-то птичий язык — одни крючки да закорючки. И потом кто-то ругает C++ и говорит, что Rust — это понятно и удобно
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>А входные данные сортировать можно?
Кстати, не факт. С другой стороны, хэш-мап с индексами займёт столько же места, так что, думаю, можно сделать копию.
SVZ>Я бы сделал так: SVZ>1. Упорядочить входные данные по возрастанию. SVZ>2. Три индекса — один указывает на левый элемент, третий — на правый, а второй — бегает между первым и третим (выполняет кастрированный двоичный поиск).
Но вот какая сложность твоего решения? Сортировка — это N*log(N), плюс log(N) — итого N*log(N)**2, так вроде. Ну, вроде побыстрее, чем N**2
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
memo = {}
result = set()
n = len(nums)
for i in range(0, n):
memo[-nums[i]] = i
for i in range(0, n):
for j in range(i + 1, n):
d = nums[i] + nums[j]
if d in memo and memo[d] != i and memo[d] != j:
t = tuple(sorted((nums[i], nums[j],-d)))
result.add(t)
return result
Здравствуйте, Marty, Вы писали:
SVZ>>1. Упорядочить входные данные по возрастанию. SVZ>>2. Три индекса — один указывает на левый элемент, третий — на правый, а второй — бегает между первым и третим (выполняет кастрированный двоичный поиск).
M>Но вот какая сложность твоего решения? Сортировка — это N*log(N), плюс log(N) — итого N*log(N)**2, так вроде. Ну, вроде побыстрее, чем N**2
С сортировкой всё ясно.
А в последующем переборе всё зависит от характера данных.
Если взять равномерный ряд (ИМХО худший случай), например, -3, -2, -1, 0, 1, 2, 3:
Левый индекс перебирает элементы от самого отрицательного до 0.
Средний и правый индексы перебирают примерно половину интервала.
Это получается (N/2) * (N/2) -> N^2.
В запущенных случаях будет чуть больше, чем O(N), т.к. итерации будут прерываться очень быстро.
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, Marty, Вы писали:
M>Не очень понял, а какая сложность в итоге?
O(n^2) в худшем случае. Но это не точно )
M>Чтобы проверить, надо засабмитить на литкоде
У меня там нету аккаунта. Я просто решил глянуть что там народ решает.
M>Блин, это какой-то птичий язык — одни крючки да закорючки. И потом кто-то ругает C++ и говорит, что Rust — это понятно и удобно
Раст — это боль, страдание и битьё по рукам. Те, кто так говорят — латентные мазохисты )
Здравствуйте, iriska2, Вы писали:
I>вот на питончике простой вариант за n^2
Я такое же решение предполагал, но вместо set можно использовать массив, если сначала в исходном массиве оставить только не более 2х дубликатов, а для 0 — вообще оставлять только в одном экземпляре. (если "0" — 3 и более, то добавлять в множество [0,0,0], как специальный случай)