почему siftdown работает в heapsort, а не siftup?

У меня есть задание по программированию: вам нужно будет преобразовать массив в кучу, используя только O(n) свопов, как было описано в лекциях. Обратите внимание, что в этой задаче вам нужно будет использовать минимальную кучу вместо максимальной кучи. В первой строке вывода должно быть одно целое число m — общее количество свопов. m должно удовлетворять условиям 0 ≤ m ≤ 4n. Следующие m строк должны содержать операции подкачки, используемые для преобразования массива a в кучу. Каждый обмен описывается парой целых чисел i,j — индексами элементов, подлежащих обмену, отсчитываемых от 0.

Я реализовал решение, используя технику просеивания, сравнивая с родительским значением, которое давало решения для небольших текстовых случаев, когда количество целых чисел в массиве меньше 10, проверено ручной проверкой, но не смог пройти тестовый пример с 100000 целых чисел как вход. это код для этого


class HeapBuilder:
    def __init__(self):
        self._swaps = [] #array of tuples or arrays
        self._data = []

    def ReadData(self):
        n = int(input())
        self._data = [int(s) for s in input().split()]
        assert n == len(self._data)

    def WriteResponse(self):
        print(len(self._swaps))
        for swap in self._swaps:
            print(swap[0], swap[1])

    def swapup(self,i):
        if i !=0:
            if self._data[int((i-1)/2)]> self._data[i]:
                self._swaps.append(((int((i-1)/2)),i))
                self._data[int((i-1)/2)], self._data[i] = self._data[i],self._data[int((i-1)/2)]
                self.swapup(int((i-1)/2))

    def GenerateSwaps(self):
        for i in range(len(self._data)-1,0,-1):
            self.swapup(i)

    def Solve(self):
        self.ReadData()
        self.GenerateSwaps()
        self.WriteResponse()

if __name__ == '__main__':
    heap_builder = HeapBuilder()
    heap_builder.Solve()

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

class HeapBuilder:
    def __init__(self):
        self._swaps = [] #array of tuples or arrays
        self._data = []

    def ReadData(self):
        n = int(input())
        self._data = [int(s) for s in input().split()]
        assert n == len(self._data)

    def WriteResponse(self):
        print(len(self._swaps))
        for swap in self._swaps:
            print(swap[0], swap[1])

    def swapdown(self,i):
        n = len(self._data)
        min_index = i
        l = 2*i+1 if (2*i+1<n) else -1 
        r = 2*i+2 if (2*i+2<n) else -1 

        if l != -1 and self._data[l] < self._data[min_index]:
            min_index = l

        if r != - 1 and self._data[r] < self._data[min_index]:
            min_index = r

        if i != min_index:
            self._swaps.append((i, min_index))
            self._data[i], self._data[min_index] = \
                self._data[min_index], self._data[i]
            self.swapdown(min_index)

    def GenerateSwaps(self):
        for i in range(len(self._data)//2 ,-1,-1):
            self.swapdown(i)

    def Solve(self):
        self.ReadData()
        self.GenerateSwaps()
        self.WriteResponse()

if __name__ == '__main__':
    heap_builder = HeapBuilder()
    heap_builder.Solve()

может кто-нибудь объяснить, что не так с методом просеивания/подкачки?


person sudheer naidu    schedule 23.06.2019    source источник
comment
Я не спрашиваю о временной сложности, я хорошо знал тот факт, что просеивание работает за O (n), тогда как просеивание занимает O (nlogn). Что я хочу знать, так это причину, по которой просеивание дает неправильный ответ (не проблема ограничения времени, а проблема неточности)   -  person sudheer naidu    schedule 25.06.2019


Ответы (1)


Попытка создать кучу путем «подкачки» снизу не всегда будет работать. Результирующий массив не обязательно будет допустимой кучей. Например, рассмотрим этот массив: [3,6,2,4,5,7,1]. Рассматривается как дерево, которое:

       3
    4     2
   6 5   7 1

Ваш алгоритм начинается с последнего элемента и перемещается вверх к корню. Итак, вы меняете 1 на 2, а затем меняете 1 на 3. Это дает вам:

       1
    4     3
   6 5   7 2

Затем вы продолжаете с остальными элементами, ни один из которых не нужно перемещать.

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

Ключевым моментом здесь является то, что метод замены предполагает, что когда вы обработали a[i], то элемент, который оказывается в этой позиции, находится на своем последнем месте. Сравните это с методом свопинга вниз, который позволяет повторять настройку элементов, находящихся ниже в куче.

person Jim Mischel    schedule 25.06.2019