Ответ
Чтобы удалить элемент из слайса за константное время O(1), можно пожертвовать порядком элементов. Этот метод заключается в замене удаляемого элемента последним элементом слайса с последующим укорачиванием слайса на один элемент.
Решение для удаления первого элемента:
Если нужно удалить именно первый элемент (i=0), как в вопросе:
s := []int{1, 2, 3, 4, 5}
// 1. Копируем значение последнего элемента на место первого
s[0] = s[len(s)-1]
// 2. Укорачиваем слайс, отбрасывая последний элемент
s = s[:len(s)-1]
// Результат: s = []int{5, 2, 3, 4} (порядок нарушен)
Эта операция имеет сложность O(1), так как она не зависит от размера слайса. Происходит только одно копирование значения и изменение заголовка слайса (его длины), а не перемещение всех элементов.
Обобщенная функция для удаления по индексу i:
Этот подход легко обобщить для удаления элемента по любому индексу i.
func removeUnordered[T any](slice []T, i int) []T {
// Заменяем элемент по индексу i последним элементом
slice[i] = slice[len(slice)-1]
// Возвращаем слайс без последнего элемента
return slice[:len(slice)-1]
}
s := []int{10, 20, 30, 40, 50}
s = removeUnordered(s, 2) // Удаляем элемент с индексом 2 (значение 30)
// Результат: s = []int{10, 20, 50, 40}
Важное замечание о памяти
Если слайс содержит указатели или структуры с указателями, то после удаления старый последний элемент все еще будет доступен в базовом массиве, и на него будет сохраняться ссылка, что может привести к утечке памяти. Чтобы этого избежать, нужно явно обнулить значение последнего элемента перед укорачиванием слайса.
func removeUnorderedWithGC[T any](slice []*T, i int) []*T {
// Заменяем удаляемый элемент последним
slice[i] = slice[len(slice)-1]
// Обнуляем ссылку на старый последний элемент для сборщика мусора
slice[len(slice)-1] = nil
// Укорачиваем слайс
return slice[:len(slice)-1]
}
Для сравнения: удаление с сохранением порядка
Если порядок важен, используется append, что приводит к сдвигу элементов и имеет сложность O(N):
i := 2
s = append(s[:i], s[i+1:]...)
// Результат: s = []int{10, 20, 40, 50} (порядок сохранен) Ответ 18+ 🔞
А, слушай, тут один умник спрашивает, как из слайса элемент удалить за O(1), да ещё и первый, блядь. Ну, типа, чтобы не двигать всю эту хуйню, как обычно делают. Так вот, есть способ, но он, сука, порядок ломает в хлам, как мою жизнь после пятницы. Но зато быстро, ёпта!
Смотри, идея проще, чем объяснять бабе, почему ты не дома ночевал. Берёшь этот самый первый элемент, который надо удалить, и на его место хуяришь последний элемент слайса. А потом просто отрезаешь конец, как гнилой хвост. Всё, операция сделана, порядок нихуя не сохранился, но кто его, блядь, вообще хранит в таких случаях?
Вот, смотри на этот кусок кода, он как раз это и делает:
s := []int{1, 2, 3, 4, 5}
// 1. Копируем значение последнего элемента на место первого
s[0] = s[len(s)-1]
// 2. Укорачиваем слайс, отбрасывая последний элемент
s = s[:len(s)-1]
// Результат: s = []int{5, 2, 3, 4} (порядок нарушен)
Видишь? Было [1,2,3,4,5], а стало [5,2,3,4]. Единичку нахуй выкинули, пятёрку вперёд поставили. Порядок, конечно, ебнулся, но зато скорость — овердохуища, O(1), потому что мы только один элемент скопировали и длину поменяли. Не то что этот твой append, который всю хуйню сдвигает, как мудак на катке.
А если тебе надо не первый, а какой-то другой элемент выпилить, то тоже легко. Вот, смотри, обобщённая функция:
func removeUnordered[T any](slice []T, i int) []T {
// Заменяем элемент по индексу i последним элементом
slice[i] = slice[len(slice)-1]
// Возвращаем слайс без последнего элемента
return slice[:len(slice)-1]
}
s := []int{10, 20, 30, 40, 50}
s = removeUnordered(s, 2) // Удаляем элемент с индексом 2 (значение 30)
// Результат: s = []int{10, 20, 50, 40}
Тут мы вырезали 30, а на её место пришла 50. Опять порядок пошёл лесом, но зато быстро, блядь!
А теперь, внимание, важный момент, чтобы не облажаться. Если в слайсе у тебя указатели сидят, то после такой операции старый последний элемент так и будет висеть в памяти, как призрак прошлого. Чтобы сборщик мусора не офигел, его надо обнулить, вот так:
func removeUnorderedWithGC[T any](slice []*T, i int) []*T {
// Заменяем удаляемый элемент последним
slice[i] = slice[len(slice)-1]
// Обнуляем ссылку на старый последний элемент для сборщика мусора
slice[len(slice)-1] = nil
// Укорачиваем слайс
return slice[:len(slice)-1]
}
Иначе будет утечка, а потом придётся объяснять, куда делась память, как объяснять, куда делась зарплата после бара.
Ну а если тебе порядок священен, как мои носки после недели носки, то придётся использовать append и смириться с O(N):
i := 2
s = append(s[:i], s[i+1:]...)
// Результат: s = []int{10, 20, 40, 50} (порядок сохранен)
Но это уже для перфекционистов, которые верят, что в жизни всё должно быть по полочкам. А в реальности, блядь, часто похуй на порядок, лишь бы работало быстро. Вот так вот, чих-пых тебя в сраку!