def insert(heap_low, heap_high, num):
    hl = heap_low.copy()
    hh = heap_high.copy()

    if len(hl) == 0 or num < hl[0]:
        my_max_heap.heappush(hl, num)
    else:
        my_min_heap.heappush(hh, num)

    return hl, hh
def rebalance_heaps(heap_low, heap_high):
    rebalanced_heap_low = heap_low
    rebalanced_heap_high = heap_high

    if len(rebalanced_heap_low) - len(rebalanced_heap_high) > 1:
        element = my_max_heap.heappop(rebalanced_heap_low)
        my_min_heap.heappush(rebalanced_heap_high, element)

    if len(rebalanced_heap_high) - len(rebalanced_heap_low) > 1:
        element = my_min_heap.heappop(rebalanced_heap_high)
        my_max_heap.heappush(rebalanced_heap_low, element)

    return rebalanced_heap_low, rebalanced_heap_high
def dijkstra_heapbased(source_node, graph_adjacency_list):
    """
    >>> dijkstra_heapbased(1, {1: [(2, 1), (3, 4)], 2: [(3, 2), (4, 6)], 3: [(1, 4), (2, 2), (4, 3)], 4: [(2, 6), (3, 3)]})
    {1: 0, 2: 1, 3: 3, 4: 6}

    >>> dijkstra_heapbased(1, {1: [(2, 1), (3, 4), (5, 0.5)], 2: [(3, 2), (4, 6)], 3: [(1, 4), (2, 2), (4, 3)], 4: [(2, 6), (3, 3)], 5: [(1, 0.5)]})
    {1: 0, 2: 1, 3: 3, 4: 6, 5: 0.5}

    :param source_node:
    :param graph_adjacency_list:
    :return:
    """
    heap = []
    shortest_distances = {}
    explored = []

    for node in graph_adjacency_list.keys():
        if node == source_node:
            my_min_heap.heappush(heap, (0, node))
        else:
            my_min_heap.heappush(heap, (float('inf'), node))

    while len(heap):
        current_distance, current_node = my_min_heap.heappop(heap)

        shortest_distances[current_node] = current_distance
        explored.append(current_node)

        # udpate heap
        adjacent_nodes = graph_adjacency_list.get(current_node, [])
        unexplored_nodes = list(filter(lambda adjacent_node: not operator.contains(explored, adjacent_node[0]), adjacent_nodes))

        for adjacent_node, adjacent_node_dist in unexplored_nodes:
            possible_dist = current_distance + adjacent_node_dist
            node_idx_in_heap = get_node_idx(heap, adjacent_node)

            # do we need to update this particular node?
            if node_idx_in_heap >= 0:
                node_dist = heap[node_idx_in_heap][0]
                if possible_dist < node_dist:
                    my_min_heap.remove(heap, node_idx_in_heap)
                else:
                    continue

            my_min_heap.heappush(heap, (possible_dist, adjacent_node))

    return shortest_distances