跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

堆数据结构基础与常用字符串处理算法实战

堆作为完全二叉树的高效实现,支持 O(logn) 插入与删除。最大最小堆原理、数组映射关系及建堆排序流程。结合 C++ 代码演示核心操作,并涵盖字母异位词判断、回文串验证等经典字符串算法,通过双指针与哈希计数优化性能。附带堆相关选择题解析,帮助巩固理论理解与实际应用。

奇形怪状发布于 2026/3/30更新于 2026/10/1683 浏览
堆数据结构基础与常用字符串处理算法实战

堆数据结构基础与常用字符串处理算法实战

一、堆数据结构基础

堆(Heap)本质上是一种特殊的完全二叉树,根据节点大小关系可分为最大堆和最小堆。在最大堆中,父节点始终大于或等于子节点;最小堆则相反。这种结构天然适合实现优先队列。

1. 数组映射关系

堆通常使用数组存储。对于下标为 i 的节点:

  • 左子节点下标:2 * i + 1
  • 右子节点下标:2 * i + 2
  • 父节点下标:(i - 1) / 2

2. 核心操作实现

插入与删除

插入新元素时,将其置于末尾并执行上浮(heapify-up)操作恢复堆序;删除堆顶时,将末尾元素移至顶部并下沉(heapify-down)。

下面是一个最大堆的 C++ 完整实现,包含建堆、插入及打印功能:

#include <vector>
#include <iostream>
using namespace std;

class MaxHeap {
private:
    vector<int> heap;

    // 上浮操作
    void heapifyUp(int index) {
        int parent = (index - 1) / 2;
        if (index > 0 && heap[index] > heap[parent]) {
            swap(heap[index], heap[parent]);
            heapifyUp(parent);
        }
    }

    // 下沉操作
    void heapifyDown(int index) {
        int largest = index;
        int left = 2 * index + 1;
        int right = 2 * index + 2;
        int size = heap.size();

        if (left < size && heap[left] > heap[largest]) {
            largest = left;
        }
        if (right < size && heap[right] > heap[largest]) {
            largest = right;
        }

        if (largest != index) {
            swap(heap[index], heap[largest]);
            heapifyDown(largest);
        }
    }

public:
    void insert(int value) {
        heap.push_back(value);
        heapifyUp(heap.size() - 1);
    }

    int extractMax() {
        if (heap.empty()) throw out_of_range("Heap is empty");
        int max = heap[0];
        heap[0] = heap.back();
        heap.pop_back();
        if (!heap.empty()) heapifyDown(0);
        return max;
    }

    void buildHeap(const vector<int>& array) {
        heap = array;
        for (int i = heap.size() / 2 - 1; i >= 0; i--) {
            heapifyDown(i);
        }
    }

    void printHeap() {
        for (int val : heap) cout << val << " ";
        cout << endl;
    }
};
堆排序

堆排序利用建堆后堆顶即为最大值的特点,反复取出堆顶并调整剩余部分。时间复杂度 O(nlogn),空间复杂度 O(1)。

void heapSort(vector<int>& arr) {
    int n = arr.size();
    // 构建最大堆
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }
    // 逐个交换堆顶到末尾
    for (int i = n - 1; i > 0; i--) {
        swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}

void heapify(vector<int>& arr, int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < n && arr[left] > arr[largest]) largest = left;
    if (right < n && arr[right] > arr[largest]) largest = right;

    if (largest != i) {
        swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}

二、字符串处理算法

1. 有效的字母异位词

判断两个字符串是否包含相同字符且频次一致。思路是统计字符频次差值,若全为零则为异位词。

class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.length() != t.length()) return false;
        vector<int> char_counts(26, 0);
        for (int i = 0; i < s.length(); ++i) {
            char_counts[s[i] - 'a']++;
            char_counts[t[i] - 'a']--;
        }
        for (auto count : char_counts) {
            if (count != 0) return false;
        }
        return true;
    }
};

解析:这里用了一个小技巧,遍历一次同时增减计数,最后检查数组即可。空间上固定为 26,属于 O(1)。

2. 判断字符串的两半是否相似

给定偶数长度字符串,比较前后两半元音数量是否相等。

class Solution {
public:
    bool isVowel(char c) {
        c = tolower(c);
        return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
    }

    bool halvesAreAlike(string s) {
        int begin = 0, end = s.size() / 2;
        int count1 = 0, count2 = 0;
        while (end < s.size()) {
            if (isVowel(s[begin])) count1++;
            if (isVowel(s[end])) count2++;
            begin++; end++;
        }
        return count1 == count2;
    }
};

解析:双指针同步移动,一边统计前半段,一边统计后半段,逻辑清晰且无需额外空间。

3. 字符串最后一个单词的长度

从末尾向前查找第一个空格,计算剩余长度。

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    getline(cin, s);
    size_t pos = s.rfind(' ');
    if (pos != string::npos) {
        cout << s.size() - pos - 1 << endl;
    } else {
        cout << s.size() << endl;
    }
    return 0;
}

注意:使用 getline 才能读取含空格的整行输入,rfind 从右向左搜索更高效。

4. 验证回文串

只考虑字母数字,忽略大小写,使用双指针向中间靠拢。

class Solution {
public:
    bool isPalindrome(string s) {
        int begin = 0, end = s.size() - 1;
        while (begin < end) {
            while (begin < end && !isalnum(s[begin])) begin++;
            while (begin < end && !isalnum(s[end])) end--;
            if (tolower(s[begin]) != tolower(s[end])) return false;
            begin++; end--;
        }
        return true;
    }
};

解析:关键是跳过非有效字符后再比较。如果直接比较可能会因为标点符号导致误判。

三、堆相关选择题解析

题目 1

下列哪个序列是堆? A. 94,31,53,23,16,72 B. 94,53,31,72,16,23 C. 16,53,23,94,31,72 D. 16,31,23,94,53,72

解析:堆需满足完全二叉树性质且父子节点有序。

  • A 项中 53 < 72,不满足最大堆;
  • B 项中 53 < 72,不满足最大堆;
  • C 项中 16 < 53,不满足最大堆,也不满足最小堆;
  • D 项 16 < 31, 16 < 23, 31 < 94, 31 < 53, 23 < 72,符合最小堆定义。 答案:D

题目 2

序列 50, 40, 95, 20, 15, 70, 60, 45, 80 调整为小根堆后结构为 15,20,60,45,40,70,95,50,80,说法正确吗? 解析:画出二叉树检查每个非叶子节点是否小于子节点。15<20/60, 20<45/40, 60<70/95, 45<50/80,全部满足。 答案:A

题目 3

已知大根堆 25, 13, 10, 12, 9,尾部插入 18,调整过程中比较次数? 解析:插入 18 后位置在索引 5。父节点为索引 2 (值 10)。18 > 10,交换。新父节点为索引 0 (值 25)。18 < 25,停止。共比较 2 次。 答案:B

题目 4

小根堆 5,8,12,19,28,20,15,22 插入 3,调整后结果? 解析:3 插入末尾,依次上浮:3 < 19 (换), 3 < 8 (换), 3 < 5 (换)。最终 3 在根节点,原 5 下沉至其左子节点位置。 答案:A

题目 5

堆排序中建堆的时间复杂度? 解析:虽然单次下沉是 O(logn),但大部分节点靠近底部,下沉距离短。数学推导证明总复杂度为 O(n)。 答案:A

总结

堆通过数组模拟完全二叉树,实现了高效的优先级管理。掌握上浮下沉机制是理解优先队列的关键。字符串处理方面,哈希计数与双指针是解决频次与对称性问题的利器。实际编码时注意边界条件与字符转换细节,能避免不少潜在 Bug。

目录

  1. 堆数据结构基础与常用字符串处理算法实战
  2. 一、堆数据结构基础
  3. 1. 数组映射关系
  4. 2. 核心操作实现
  5. 插入与删除
  6. 堆排序
  7. 二、字符串处理算法
  8. 1. 有效的字母异位词
  9. 2. 判断字符串的两半是否相似
  10. 3. 字符串最后一个单词的长度
  11. 4. 验证回文串
  12. 三、堆相关选择题解析
  13. 题目 1
  14. 题目 2
  15. 题目 3
  16. 题目 4
  17. 题目 5
  18. 总结

更多推荐文章

查看全部
  • Java 量化实战:股票详细信息实时获取与多数据源架构
  • Java LeetCode 热门算法精讲
  • OpenClaw 自动化与定时任务实战指南
  • Python 的抽象基类、协议和鸭子类型
  • Python 全栈开发:MySQL 与 Redis 数据库集成指南
  • 文心一言开源版测评:能力、易用性与价值
  • C++ 模板编程基础:函数与类模板实战指南
  • 2024年大模型技术发展及治理实践报告
  • Stable Diffusion 3.5 FP8 发布:显存降 40%,推理提速近半
  • Git 核心知识体系:从原理到实战演进
  • 渗透测试入门书单与章节脉络
  • 渗透测试实战:SRC 漏洞挖掘与提交流程指南
  • VSCode + GitHub Copilot AI 编程实战教程
  • VMware 中安装配置 Ubuntu 系统
  • EasyOCR 使用指南:Python 开源 OCR 工具图文识别实战
  • 大模型落地:从拼性能到拼赋能
  • 音乐文件解密技术:开源方案架构与核心算法分析
  • ChatGPT 实用技巧:文本与数据结构化方法解析
  • OpenClaw 抓取仿真环境搭建实录
  • AgentScope Java 智能体开发指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online