Как исключить из списка максимальный элемент?
Как исключить из списка максимальный элемент?
Ответы (7 шт):
- найти максимальный элемент и индекс в массиве
2.1) собрать новый список из старого без максимального элемента
2.2) удалить максимальный элемент по индексу через
del arr[index_max]
your_list.pop(your_list.index(max(your_list)))
your_list = [i for in your_list if i != max(yourlist)]
m_idx = 0
m_item = li[0]
for i, x in enumerate(li):
if x > m_item:
m_item = x
m_idx = i
del li[m_idx]
Случайно код получился как из другого ответа от nomnoms12, но хочу отправить ;)
def del_max_fast(arr):
biggest = arr[0]
index_of_biggest = 0
for index, element in enumerate(arr):
if element > biggest:
biggest, index_of_biggest = element, index
del arr[index_of_biggest]
return arr
Так как спрашивали про самый быстрый, то рассмотрим все предложенные варианты плюс ещё один.
1. Первый предложенный вариант. Один из самых быстрых, несмотря на то что делается два прохода по списку и одно копирование хвоста после удаления:
def remove_max_1(a):
a.pop(a.index(max(a)))
2. Самый худший вариант. Максимум отыскивается N раз. Квадратичная сложность, последнее место:
def remove_max_2(a):
return [i for i in a if i != max(a)]
3. Оптимизация предыдущего варианта. Сложность стала линейной снова. Скорость не самая высокая, так как изготавливается копия:
def remove_max_3(a):
v = max(a)
return [i for i in a if i != v]
4. Вариация на тему первого. Вместо pop тут del:
def remove_max_4(a):
del a[a.index(max(a))]
5. Самый элегантный вариант. Два прохода, одно копирование, как и у первого:
def remove_max_5(a):
a.remove(max(a))
6. Максимальная оптимизация. Один проход, копирования нет. Вместо него последний элемент записывается на место максимального, затем список укорачивается на единицу. Не самый быстрый, как оказалось:
def remove_max_6(a):
_, i = max((v, i) for i, v in enumerate(a))
a[i] = a[-1]
a.pop()
7. Отчаяная попытка сделать первый вариант ещё быстрее. Убрано копирование хвоста:
def remove_max_7(a):
a[a.index(max(a))] = a[-1]
a.pop()
8. Ещё один однопроходный способ. Этот лучше окровенно неудачного номера шестого, хотя есть копирование при удалении:
def remove_max_8(a):
m_idx = 0
m_item = a[0]
for i, x in enumerate(a):
if x > m_item:
m_item = x
m_idx = i
del a[m_idx]
9. А можно найти максимум сортировкой за NlogN. Зато потом максимумы можно будет удалять без повторных сортировок, за константу:
def remove_max_9(a):
a.sort()
a.pop()
Таблица результатов:
n 10^3 10^4 10^5 10^6 10^7 10^8
remove_max_1 0.000 0.000 0.002 0.025 0.230 1.939
remove_max_2 0.016 1.483 147.792 - - -
remove_max_3 0.000 0.000 0.006 0.055 0.554 5.377
remove_max_4 0.000 0.000 0.002 0.026 0.231 1.964
remove_max_5 0.000 0.000 0.002 0.025 0.230 1.946
remove_max_6 0.000 0.001 0.013 0.121 1.215 12.128
remove_max_7 0.000 0.000 0.002 0.027 0.228 1.878
remove_max_8 0.000 0.000 0.005 0.045 0.480 4.659
remove_max_9 0.000 0.001 0.016 0.251 3.543 50.671
Хуже всех № 2 - О-большое не обманешь. № 9 - сортировка начинает бодро но отстаёт, опять О-большое. Оптимальный по количеству проходов № 6 не самый быстрый - один проход на Питоне хуже двух проходов на C. № 3 отстаёт так как создаёт копию списка. Лучшый из однопроходных № 8. Плотной группой идут №№ 1, 4, 5 - два прохода, одно копирование. В самом конце их немного обходит № 7 за счёт отказа от копирования.
Как проводились измерения
def elapsed_time(f):
start = time.time()
result = f()
finish = time.time()
return result, finish - start
def test(n, remove_max):
r = random.Random(n)
a = [r.random() for _ in range(n)]
_, t = elapsed_time(lambda: remove_max(a))
print(remove_max.__name__, '{:10d}'.format(n), '{:6.3f}'.format(t))
for p in range(1, 9):
n = 10 ** p
for rm in (remove_max_1, remove_max_2, remove_max_3, remove_max_4, remove_max_5, remove_max_6, remove_max_7, remove_max_8):
test(n, rm)
Все приведенные ответы неявно предполагают, что максимум в списке один.
Если это не так, они просто не решают поставленную задачу.
Вот решение.
li = [1,3,7,7,5,4,2,1,7]
li.sort()
index = li.index(li[-1])
del li[index:]
наслаждайтесь.
Да, мы проигрываем на первом этапе сортировки. Это n*ln(n) операций. Однако, если максимумов 2 или более в списке, то поиск очередного из них в несортированном списке оценивается как n/2.
То есть нам надо выбирать между n*ln(n) как в приведенном здесь алгоритме и предыдущими алгоритмами n + n/2*k, где k - это количество максимумов в исходном списке )