用小根堆(二根堆binary heap)实现优先队列(Priority queue)的初始化、插入、查找最小元素和删除操作

typedef struct HeapStruct *PriorityQueue;
int MinPQSize = 2;
struct HeapStruct
{
    int Capacity;
    int Size;
    int *Elements;
};

首先,定义一个结构体HeapStruct,表示堆的数据结构,其中包括堆的容量(Capacity)、当前大小(Size)以及一个整型数组Elements,用于存储堆的元素。然后,通过typedef定义了一个指向HeapStruct的指针类型PriorityQueue,用于引用和操作堆,并且规定了堆的最小元素个数MinPQSize是2。

PriorityQueue Initialize(int MaxElements)
{
    PriorityQueue H;
    if(MaxElements < MinPQSize)
    {
        cout << "Priority queue size is too small" << endl;
        return H;
    }
    H = new(HeapStruct);
    if(H == nullptr)
    {
        cout << "Out of space" << endl;
        return H;
    }
    H->Elements = new int[MaxElements+1];
    if(H->Elements == nullptr)
    {
        cout << "Out of space" << endl;
        return H;
    }
    H->Capacity = MaxElements;
    H->Size = 0;
    H->Elements[0] = 0;
    return H;
}

函数Initialize用于初始化一个最小堆优先队列。函数接受一个整型参数MaxElements,表示堆的最大容量。在函数内部,首先检查最小堆的容量是否小于最小要求,如果是,则输出错误信息并返回空指针。然后,分配堆结构体和元素数组的内存空间,并进行相应的初始化赋值操作。最后,返回指向堆的指针。

int IsFull(PriorityQueue H)
{
    return H->Size == H->Capacity;
}

函数IsFull用于判断堆是否已满。如果堆的大小等于容量,则返回 1;否则返回 0。

void Insert(int x, PriorityQueue H)
{
    int i;
    if(IsFull(H))
    {
        cout << "Priority queue is full" << endl;
    }
    for(i = ++H->Size; H->Elements[i / 2] > x; i /= 2)
    {
        H->Elements[i] = H->Elements[i / 2];
    }
    H->Elements[i] = x;
}

当我们向最小堆中插入一个新元素时,需要确保该元素被正确放置在合适的位置,以满足最小堆的性质。最小堆的性质是父节点的值小于或等于其子节点的值。

在上述代码中,通过上滤操作实现了将新元素插入到最小堆中的过程。下面是更详细的解释:

  1. i = ++H->Size:首先将堆的大小加1,并将其赋值给变量 i。这是因为在插入元素之前,堆的大小会增加,i 表示新元素在数组中的索引。

  2. H->Elements[i / 2] > x:该条件检查当前节点的父节点是否比要插入的元素 x 大。如果是,则需要将当前节点的父节点下移,为新元素腾出位置。

  3. i /= 2:将当前节点的索引除以2,即向上移动到父节点的位置。这是因为在最小堆中,父节点的索引是当前节点索引的一半。

  4. H->Elements[i] = H->Elements[i / 2]:将当前节点的父节点的值复制给当前节点。这样,父节点的值就被下移到了当前节点的位置。

  5. 继续循环执行步骤2和步骤3,直到插入的元素 x 找到了合适的位置,或者到达堆的根节点。在每一次循环中,我们比较当前节点的父节点与要插入的元素 x 的大小关系,如果父节点较大,则将父节点的值下移。

  6. H->Elements[i] = x:最后,将要插入的元素 x 放置在找到的合适位置上,完成上滤操作。

通过上述上滤过程,新元素会逐步上移,直到找到合适的位置,以保持最小堆的性质。最小堆的根节点即为最小的元素。

上滤操作的时间复杂度为 O(log n),其中 n 是堆的大小,因为在最坏情况下,新元素可能需要上移到堆的顶部。

这样的上滤操作保证了在插入新元素后,最小堆仍然保持了其性质。这样我们可以高效地插入和删除最小元素,以及查找最小元素。

int DeleteMin(PriorityQueue H)
{
    int i, child;
    if(H->Size == 0)
    {
        cout << "Priority queue is empty" << endl;
        return -1;
    }
    int minElement = H->Elements[1];
    int lastElement = H->Elements[H->Size--];
    for(i = 1; i*2 <= H->Size; i = child)
    {
        child = i*2;
        if(child != H->Size && H->Elements[child + 1] < H->Elements[child])
        {
            child++;
        }
        if(lastElement > H->Elements[child])
        {
            H->Elements[i] = H->Elements[child];
        }
        else
        {
            break;
        }
    }
    H->Elements[i] = lastElement;
    return minElement;
}

函数DeleteMin实现了从小根堆中删除最小元素的功能,并返回删除的最小的元素的值。

  1. if(H->Size == 0):首先检查堆是否为空。如果堆的大小为0,表示堆为空,输出错误信息并返回一个特定的错误值(-1)。

  2. int minElement = H->Elements[1]:将堆的根节点的值(最小元素)保存到变量 minElement 中。

  3. int lastElement = H->Elements[H->Size--]:将堆的最后一个元素的值保存到变量 lastElement 中,并将堆的大小减1。这样做是为了方便后面的下滤操作。

  4. for(i = 1; i*2 <= H->Size; i = child):开始进行下滤操作。循环条件是当前节点的左子节点的索引不超过堆的大小。

  5. child = i*2:将变量 child 设置为当前节点的左子节点的索引。

  6. if(child != H->Size && H->Elements[child + 1] < H->Elements[child]):检查当前节点是否有右子节点,并且右子节点的值是否比左子节点的值小。如果是,则将 child 设置为右子节点的索引。

  7. if(lastElement > H->Elements[child]):如果要删除的元素(lastElement)比子节点的值大,说明需要将子节点的值上移。

  8. H->Elements[i] = H->Elements[child]:将子节点的值复制给当前节点,即进行上移操作。

  9. else:如果要删除的元素比子节点的值小或相等,则退出循环,结束下滤操作。

  10. H->Elements[i] = lastElement:将要删除的元素放置在找到的合适位置上,完成下滤操作。

  11. return minElement:返回被删除的最小元素的值。

通过上述下滤过程,堆的最后一个元素会逐步下移,直到找到合适的位置,以保持最小堆的性质。这样我们就删除了最小元素,并保持了最小堆的性质。

下滤操作的时间复杂度为 O(log n),其中 n 是堆的大小。在每次循环中,我们比较当前节点与其子节点的值,最坏情况下,下滤操作需要将元素下移到堆的叶子节点。

int FindMin(PriorityQueue H)
{
    if(H->Size == 0)
    {
        cout << "Priority queue is empty" << endl;
        return -1;
    }
    return H->Elements[1];
}

函数FindMin用于查找堆中的最小元素,并返回它的值。如果堆为空,则输出错误信息并返回一个特定的错误值。函数直接返回堆的根节点的值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 二叉搜索树(BST)是一种特殊的树结构,它具有特殊的性质:每个节点的左孩子节点的值小于其父节点的值,而每个节点的右孩子节点的值大于父节点的值。查找插入删除节点的操作过程如下:查找节点:从根节点开始,如果要查找的值大于节点值,则向右子树查找;如果要查找的值小于节点值,则向左子树查找;直到找到要查找的值 ### 回答2: 二叉搜索树(Binary Search Tree,BST)是一种特殊的二叉树,其中每个节点的值都大于其左子树中的任何节点,同时小于其右子树中的任何节点。这个特性使得二叉搜索树在进行查找插入删除节点操作时效率较高。 1. 查找节点操作:从根节点开始,根据目标值与当前节点值的比较结果选择向左子树或右子树移动,直到找到目标值或者遍历到叶子节点为止。 2. 插入节点操作:与查找类似,根据目标值与当前节点值的比较结果选择向左子树或右子树移动,直到找到空位置,然后创建新节点并将其连接到该空位置。 3. 删除节点操作: - 当待删除节点没有子节点时,直接将其父节点对应的子节点指针置为空即可。 - 当待删除节点只有一个子节点时,将该子节点替代待删除节点的位置。 - 当待删除节点有两个子节点时,可以选择以其左子树中的最大节点或右子树中的最小节点进行替代,然后递归删除该最大或最小节点,以保持二叉搜索树的结构。 程序实现过程可以采用递归或迭代的方式。在递归实现中,可以通过函数的递归调用来实现节点的查找插入删除操作。在迭代实现中,可以利用栈或队列等数据结构来辅助节点的操作过程。 以上是对二叉搜索树概念及查找插入删除节点操作过程的简要介绍。实际应用中,还需要考虑一些特殊情况,并进行错误处理,以确保二叉搜索树的实现正常运行。 ### 回答3: 二叉搜索树(Binary Search Tree,简称BST)是一种特殊的二叉树,它满足以下性质:对于任意节点,左子树的键值小于它的键值,右子树的键值大于它的键值。 查找节点的操作过程如下: 1. 若树为空,则返回空。 2. 若当前节点的键值与目标键值相等,则返回当前节点。 3. 若当前节点的键值大于目标键值,则继续在当前节点的左子树中进行查找。 4. 若当前节点的键值小于目标键值,则继续在当前节点的右子树中进行查找插入节点的操作过程如下: 1. 若树为空,则将待插入节点设为根节点。 2. 若待插入节点的键值小于当前节点的键值,则继续在当前节点的左子树中插入。 3. 若待插入节点的键值大于当前节点的键值,则继续在当前节点的右子树中插入删除节点的操作过程如下: 1. 若待删除节点为叶子节点,则直接删除。 2. 若待删除节点只有一个子节点,则将其子节点替换为待删除节点。 3. 若待删除节点有两个子节点,则找到它的后继节点或前驱节点来替换该节点。后继节点即右子树中最小的节点,前驱节点即左子树中最大的节点。 BST的程序实现过程如下(以Python为例): ```python class TreeNode: def __init__(self, val): self.val = val self.left = None self.right = None class BST: def __init__(self): self.root = None def search(self, target): curr = self.root while curr: if curr.val == target: return curr elif curr.val > target: curr = curr.left else: curr = curr.right return None def insert(self, val): if not self.root: self.root = TreeNode(val) return curr = self.root while curr: if curr.val > val: if not curr.left: curr.left = TreeNode(val) return else: curr = curr.left else: if not curr.right: curr.right = TreeNode(val) return else: curr = curr.right def delete(self, val): def find_min(node): while node.left: node = node.left return node def delete_node(node, target): if not node: return None if node.val > target: node.left = delete_node(node.left, target) elif node.val < target: node.right = delete_node(node.right, target) else: if not node.left and not node.right: return None elif not node.left: return node.right elif not node.right: return node.left else: successor = find_min(node.right) node.val = successor.val node.right = delete_node(node.right, successor.val) return node self.root = delete_node(self.root, val) ``` 以上代码实现了二叉搜索树的插入查找删除操作

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不爱敲代码的数学分子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值