堆 之 Sift-up和Sift-down

本文详细介绍了堆数据结构中的两种关键操作:Sift-up和Sift-down。Sift-up主要用于插入操作,确保新元素放置于合适位置;Sift-down则用于删除最大(或最小)元素后,重新调整堆结构以保持其特性。

 

1.sift-up

假定对于某个i>1,H[i]变成了键值大于它父节点键值的元素,这样就违反了堆的特性。我们需要使用Sift-up运算把新的数据项移到在二叉树中适合它的位置上。

Sift-up的运算沿着从H[i]到根节点的唯一一条路径,把H[i]移到适合它的位置上。在沿着路径的每一步上,都将H[i]键值和它父节点的键值H[⌊i/2⌋]相比较。

常用于:插入操作

代码:


void sift-up ( MaxHeap H)
{
    i = H->size;
    item = H->Element [i];
    for ( ; H -> Element [ i/2 ] < item; i /= 2 ) // 与父结点做比较,i / 2 表示的就是父结点的下标
        H -> Element [ i ] = H -> Element [ i/2 ]; // 向下过滤结点
    H -> Element [ i ] = item ; //若for循环完成后,i更新为父节点i,然后将 item 插入
}

放在堆的插入里就是: 


void Insert ( MaxHeap H,int item )
{
    // 将元素item插入最大堆H,其中H -> Elements [0] 已经定义为哨兵
    int i ;
    if ( H->Size ==1000 )
    {
        printf ( "最大堆已满" );
        return ;
    }
    i = ++H -> Size ; // i 指向插入后堆中的最后一个元素的位置
    for ( ; H -> Element [ i/2 ] < item; i /= 2 ) // 与父结点做比较,i / 2 表示的就是父结点的下标
        H -> Element [ i ] = H -> Element [ i/2 ]; // 向下过滤结点
    H -> Element [ i ] = item ; //若for循环完成后,i更新为父节点i,然后将 item 插入
    for(i = 1 ; i <= H->Size; i++)
    {
        printf("%d", H->Element[i]);
    }
    printf("\n");
}

2.Sift-down  

假定对于i ≤ ⌊n/2⌋,存储在H[i]中元素的键值变成小于H[2i]和H[2i+1]中的最大值(如果2i+1存在的话),这样也违反了堆的特性。Sift-down运算使H[i]“渗”到二叉树中适合它的位置上,沿着这条路径的每一步,都把H[i]的键值和存储在它子节点(如果存在)中的两个键值里最大的那个相比较。

 

过程  Sift-down

输入  数组H[1...n]和位于1和n之间的索引i

输出  下移H[i](如果需要),以使它不小于子节点

常用于删除操作,调整堆

 这里以我上篇博客为例,代码如下: 


sift-down(i)
{
    int  j, item;
    for(; i*2<=m ;i = j)
    {
        j = 2 * i;
        if(j!=m&&h[j]>h[j+1])
        {
            j++;
        }
        if(h[i]>h[j])
            swap(i, j);
        else
        {
            break;
        }
    }
}

删除代码为:(代码比较冗长,可以进行优化)

int DeleteMax( MaxHeap H )
{
    /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
    int Parent, Child;
    int MaxItem, temp;
    if (H->Size == 0)
    {
        printf("最大堆已为空");
        return;
    }
    MaxItem = H->Element[1]; /* 取出根结点最大值 */
    /* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
    H->Element[1] = H->Element[H->Size--];
    temp = H->Element[1];
    for( Parent=1; Parent*2<=H->Size; Parent=Child )
    {
        Child = Parent * 2;//child指向根下层元素的左节点
        if( (Child!= H->Size) &&
                (H->Element[Child] < H->Element[Child+1]) )
            Child++; //Child指向左右子结点的较大者
        if( temp >= H->Element[Child] ) break;//如果比较大的节点元素还大,就可以将temp元素放在树根
        else //移动temp元素到下一层
            swap(H, Parent,Child);//将儿子元素移到树根,temp再和下层元素作比较
    }
    int i;
    for(i = 1 ; i <= H->Size; i++)
    {
        printf("%d", H->Element[i]);
    }
    printf("\n");
    return MaxItem;
}

Sift-down  可以用于由数组建立堆(最小最大)

 详情请看我上一篇博客https://blog.youkuaiyun.com/Kaka_chake/article/details/81626385

### 最小(Min-Heap)概念 最小是一种特殊的完全二叉树结构,其特点是父节点的值总是小于或等于子节点的值。这种特性使得顶始终存储着整个集合中的最小值[^1]。 在实际应用中,最小常用于实现优先队列、调度算法以及各种优化问题。由于插入删除操作的时间复杂度均为 \(O(\log n)\),因此它非常适合处理动态数据集。 --- ### Python 实现 Min-Heap 以下是基于 Python 的 `min_heap` 最小实现: ```python class MinHeap: def __init__(self): self.heap = [] def insert(self, value): """向中插入新元素""" self.heap.append(value) self._sift_up(len(self.heap) - 1) def extract_min(self): """提取并返回中的最小值""" if not self.heap: return None if len(self.heap) == 1: return self.heap.pop() root_value = self.heap[0] self.heap[0] = self.heap[-1] del self.heap[-1] self._sift_down(0) return root_value def _parent_index(self, index): """获取父节点索引""" return (index - 1) // 2 def _left_child_index(self, index): """获取左子节点索引""" return 2 * index + 1 def _right_child_index(self, index): """获取右子节点索引""" return 2 * index + 2 def _sift_up(self, index): """上浮调整""" parent_idx = self._parent_index(index) while index > 0 and self.heap[parent_idx] > self.heap[index]: self.heap[parent_idx], self.heap[index] = self.heap[index], self.heap[parent_idx] index = parent_idx parent_idx = self._parent_index(index) def _sift_down(self, index): """下沉调整""" size = len(self.heap) while True: smallest = index left_child = self._left_child_index(index) right_child = self._right_child_index(index) if left_child < size and self.heap[left_child] < self.heap[smallest]: smallest = left_child if right_child < size and self.heap[right_child] < self.heap[smallest]: smallest = right_child if smallest != index: self.heap[smallest], self.heap[index] = self.heap[index], self.heap[smallest] index = smallest else: break # 测试代码 if __name__ == "__main__": min_heap = MinHeap() elements = [3, 1, 6, 5, 2, 4] for elem in elements: min_heap.insert(elem) result = [] while True: val = min_heap.extract_min() if val is None: break result.append(val) print(result) # 输出应为升序序列 [1, 2, 3, 4, 5, 6] ``` 上述代码实现了基本的最小功能,包括插入 (`insert`) 提取最小值 (`extract_min`) 操作,并通过 `_sift_up` `_sift_down` 方法维护性质。 --- ### 应用场景 #### 1. **优先队列** 使用最小可以高效地管理一组具有不同优先级的任务。每次从队列中取出的是优先级最高的任务(即数值最小的任务),从而满足实时系统的调度需求。 #### 2. **K 小/大元素查找** 利用最小可以在大规模数据集中快速找到前 K 大或前 K 小的元素。例如,在海量日志分析中筛选出访问量最大的页面[^3]。 #### 3. **Dijkstra 算法加速** Dijkstra 是一种经典的最短路径算法,而使用最小作为辅助数据结构可显著降低更新距离的操作成本,使整体性能提升至 \(O((V+E)\log V)\)[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值