Heap-007-Index-Heap-Advance

本文介绍了一种利用索引最大堆进行堆排序的方法,通过定义索引最大堆类,实现插入、删除最大元素等操作,最终完成对数组的排序。文章详细解释了索引最大堆的内部工作原理,包括上浮和下沉算法,并提供了完整的C++代码实现。

main.cpp

#include <iostream>
#include <cassert>
#include "SortTestHelper.h"

#define Priority int

using namespace std;

template<typename Item>
class IndexMaxHeap{

private:
    Item *data;
    int *indexes;
    int *reverse;

    int count;
    int capacity;

    void shiftUp( int k ){

        while( k > 1 && data[indexes[k/2]] < data[indexes[k]] ){
            swap( indexes[k/2] , indexes[k] );
            reverse[indexes[k/2]] = k/2;
            reverse[indexes[k]] = k;
            k /= 2;
        }
    }

    void shiftDown( int k ){

        while( 2*k <= count ){
            int j = 2*k;
            if( j + 1 <= count && data[indexes[j+1]] > data[indexes[j]] )
                j += 1;

            if( data[indexes[k]] >= data[indexes[j]] )
                break;

            swap( indexes[k] , indexes[j] );
            reverse[indexes[k]] = k;
            reverse[indexes[j]] = j;
            k = j;
        }
    }

public:
    IndexMaxHeap(int capacity){

        data = new Item[capacity+1];
        indexes = new int[capacity+1];
        reverse = new int[capacity+1];
        for( int i = 0 ; i <= capacity ; i ++ )
            reverse[i] = 0;

        count = 0;
        this->capacity = capacity;
    }

    ~IndexMaxHeap(){
        delete[] data;
        delete[] indexes;
        delete[] reverse;
    }

    int size(){
        return count;
    }

    bool isEmpty(){
        return count == 0;
    }

    // 传入的i对用户而言,是从0索引的
    void insert(int i, Item item){
        assert( count + 1 <= capacity );
        assert( i + 1 >= 1 && i + 1 <= capacity );

        i += 1;
        data[i] = item;
        indexes[count+1] = i;
        reverse[i] = count+1;
        count++;

        shiftUp(count);
    }

    Item extractMax(){
        assert( count > 0 );

        Item ret = data[indexes[1]];
        swap( indexes[1] , indexes[count] );
        reverse[indexes[count]] = 0;
        reverse[indexes[1]] = 1;
        count--;
        shiftDown(1);
        return ret;
    }

    int extractMaxIndex(){
        assert( count > 0 );

        int ret = indexes[1] - 1;
        swap( indexes[1] , indexes[count] );
        reverse[indexes[count]] = 0;
        reverse[indexes[1]] = 1;
        count--;
        shiftDown(1);
        return ret;
    }

    Item getMax(){
        assert( count > 0 );
        return data[indexes[1]];
    }

    int getMaxIndex(){
        assert( count > 0 );
        return indexes[1]-1;
    }

    bool contain( int i ){
        assert( i + 1 >= 1 && i + 1 <= capacity );
        return reverse[i+1] != 0;
    }

    Item getItem( int i ){
        assert( contain(i) );
        return data[i+1];
    }

    void change( int i , Item newItem ){

        assert( contain(i) );
        i += 1;
        data[i] = newItem;

        // 找到indexes[j] = i, j表示data[i]在堆中的位置
        // 之后shiftUp(j), 再shiftDown(j)

//        for( int j = 1 ; j <= count ; j ++ )
//            if( indexes[j] == i ){
//                shiftUp(j);
//                shiftDown(j);
//                return;
//            }

        int j = reverse[i];
        shiftUp( j );
        shiftDown( j );
    }

    // test reverse index
    bool testReverseIndex(){

        int *copyIndexes = new int[count+1];
        int *copyReverseIndexes = new int[count+1];

        for( int i = 0 ; i <= count ; i ++ ){
            copyIndexes[i] = indexes[i];
            copyReverseIndexes[i] = reverse[i];
        }

        copyIndexes[0] = copyReverseIndexes[0] = 0;
        std::sort(copyIndexes, copyIndexes + count + 1);
        std::sort(copyReverseIndexes, copyReverseIndexes + count + 1);

        bool res = true;
        for( int i = 1 ; i <= count ; i ++ )
            if( copyIndexes[i-1] + 1 != copyIndexes[i] || copyReverseIndexes[i-1] + 1 != copyReverseIndexes[i] )
                res = res || false;

        delete[] copyIndexes;
        delete[] copyReverseIndexes;

        if( !res ){
            cout<<"Error 1"<<endl;
            return res;
        }

        for( int i = 1 ; i <= count ; i ++ )
            if( reverse[ indexes[i] ] != i ){
                cout<<"Error 2"<<endl;
                return false;
            }

        return true;
    }
};

template<typename T>
void heapSortUsingIndexMaxHeap(T arr[], int n){

    IndexMaxHeap<T> indexMaxHeap = IndexMaxHeap<T>(n);
    for( int i = 0 ; i < n ; i ++ )
        indexMaxHeap.insert( i , arr[i] );
    assert( indexMaxHeap.testReverseIndex() );

    for( int i = n-1 ; i >= 0 ; i -- )
        arr[i] = indexMaxHeap.extractMax();
}

int main() {

    int n = 1000000;

    Priority* arr = new Priority[n];
    srand(time(NULL));
    for( int i = 0 ; i < n ; i ++ )
        arr[i] = rand()%n;
    SortTestHelper::testSort("Heap Sort Using Index-Max-Heap", heapSortUsingIndexMaxHeap, arr, n);
    delete[] arr;

    return 0;
}
<think>我们将在ESP32-S3平台上使用ESP-IDF v5.3和espressif/component-freetype库实现中文字体渲染。以下是完整步骤及示例代码: ### 步骤1:创建工程并添加必要组件 1. 创建新工程: ```bash idf.py create-project freetype_chinese cd freetype_chinese ``` 2. 添加组件依赖(需手动下载或使用git submodule): - `freetype`:位于[espressif/component-freetype](https://github.com/espressif/component-freetype) - `spiffs`:用于存储字体文件(已内置在ESP-IDF中) 将`component-freetype`添加到`components`目录: ```bash git submodule add https://github.com/espressif/component-freetype components/freetype ``` ### 步骤2:准备中文字体文件 1. 选择支持中文的字体文件(如`NotoSansSC-Regular.ttf`),将其放入工程目录`spiffs/fonts`下。 2. 配置SPIFFS分区表: - 在`partitions.csv`中添加: ``` spiffs, data, spiffs, 0x180000, 0x170000, ``` 3. 使用CMake构建SPIFFS镜像: 在`CMakeLists.txt`中添加: ```cmake # 设置SPIFFS构建 set(SPIFFS_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/spiffs") spiffs_create_partition_image(spiffs ${SPIFFS_BUILD_DIR} FLASH_IN_PROJECT) ``` ### 步骤3:配置项目 1. 启用PSRAM(可选,但推荐用于大字体): ```bash idf.py menuconfig ``` - 进入`Component config` > `ESP System Settings` > `Support for external, SPI-connected RAM` > 启用`SPI RAM config`。 2. 配置FreeType: - 启用`Anti-aliasing`(默认已启用)。 ### 步骤4:编写主程序 `main.c`代码示例: ```c #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_spiffs.h" #include "esp_log.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_vendor.h" #include "driver/i2c.h" #include "ft2build.h" #include FT_FREETYPE_H // 屏幕配置(根据实际硬件修改) #define LCD_HOST ESP_LCD_I2C_PORT_NUM #define LCD_PIXEL_CLK_HZ (400 * 1000) #define LCD_I2C_ADDR 0x3C #define LCD_WIDTH 128 #define LCD_HEIGHT 64 static const char *TAG = "FT_CN"; void app_main(void) { // 初始化SPIFFS esp_vfs_spiffs_conf_t conf = { .base_path = "/spiffs", .partition_label = "spiffs", .max_files = 5, .format_if_mount_failed = true }; ESP_ERROR_CHECK(esp_vfs_spiffs_register(&conf)); // 初始化LCD(示例使用SSD1306 I2C OLED) esp_lcd_i2c_bus_handle_t i2c_bus = NULL; esp_lcd_i2c_bus_config_t bus_cfg = { .scl_io_num = GPIO_NUM_18, .sda_io_num = GPIO_NUM_17, .dev_addr_length = I2C_ADDR_BIT_7, .device_addr = LCD_I2C_ADDR, .i2c_port = LCD_HOST, .clk_speed = LCD_PIXEL_CLK_HZ, }; ESP_ERROR_CHECK(esp_lcd_new_i2c_bus(&bus_cfg, &i2c_bus)); esp_lcd_panel_io_handle_t io_handle = NULL; esp_lcd_panel_io_i2c_config_t io_config = { .dev_addr = LCD_I2C_ADDR, .control_phase_bytes = 1, .dc_bit_offset = 6, .lcd_cmd_bits = 8, .lcd_param_bits = 8, .on_color_trans_done = NULL, .user_ctx = NULL, }; ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus, &io_config, &io_handle)); esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_panel_dev_config_t panel_config = { .bits_per_pixel = 1, .reset_gpio_num = -1, .color_space = ESP_LCD_COLOR_SPACE_MONOCHROME, }; ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle)); esp_lcd_panel_init(panel_handle); esp_lcd_panel_disp_on_off(panel_handle, true); // 初始化FreeType库 FT_Library library; FT_Error error = FT_Init_FreeType(&library); if (error) { ESP_LOGE(TAG, "FreeType初始化失败: %d", error); return; } // 从SPIFFS加载字体文件 const char *font_path = "/spiffs/fonts/NotoSansSC-Regular.ttf"; FT_Face face; error = FT_New_Face(library, font_path, 0, &face); if (error == FT_Err_Unknown_File_Format) { ESP_LOGE(TAG, "不支持的字体格式"); } else if (error) { ESP_LOGE(TAG, "字体加载失败: %s (错误: %d)", font_path, error); } // 设置字体大小(像素单位) error = FT_Set_Pixel_Sizes(face, 0, 24); // 24像素高 if (error) { ESP_LOGE(TAG, "设置字体大小失败"); } // 渲染中文字符"你好" const wchar_t *text = L"你好"; uint8_t *buffer = heap_caps_malloc(LCD_WIDTH * LCD_HEIGHT / 8, MALLOC_CAP_8BIT); memset(buffer, 0, LCD_WIDTH * LCD_HEIGHT / 8); // 获取LCD帧缓冲区并清屏 esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LCD_WIDTH, LCD_HEIGHT, buffer); int x_pos = 0; int y_pos = 30; // 基线位置 for (int i = 0; text[i] != '\0'; i++) { // 加载字符字形 FT_UInt glyph_index = FT_Get_Char_Index(face, text[i]); error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); if (error) continue; // 渲染为单色位图 error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if (error) continue; FT_Bitmap *bitmap = &face->glyph->bitmap; // 将位图绘制到LCD缓冲区 for (int y = 0; y < bitmap->rows; y++) { for (int x = 0; x < bitmap->width; x++) { int px = x_pos + x + face->glyph->bitmap_left; int py = y_pos - y + face->glyph->bitmap_top; if (px >= 0 && px < LCD_WIDTH && py >= 0 && py < LCD_HEIGHT) { if (bitmap->buffer[y * bitmap->pitch + x]) { buffer[py * (LCD_WIDTH / 8) + (px / 8)] |= (1 << (7 - (px % 8))); } } } } x_pos += face->glyph->advance.x >> 6; // 推进到下一个字符位置 } // 更新LCD显示 esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LCD_WIDTH, LCD_HEIGHT, buffer); free(buffer); // 清理资源 FT_Done_Face(face); FT_Done_FreeType(library); } ``` ### 关键点说明 1. **字体选择**:必须使用支持中文的字体(如Noto Sans SC),文件需放入SPIFFS分区。 2. **内存管理**: - 大字体文件可能消耗较多内存,建议启用PSRAM。 - 使用`heap_caps_malloc`指定内存类型。 3. **字符编码**: - 使用宽字符`wchar_t`处理Unicode字符。 - 中文字符直接以Unicode字面量表示(如`L"你好"`)。 4. **渲染流程**: ```mermaid graph LR A[初始化SPIFFS] --> B[加载字体文件] B --> C[设置字体大小] C --> D[遍历文本] D --> E[渲染每个字符] E --> F[绘制到位图] F --> G[刷新显示屏] ``` ### 编译与烧录 ```bash idf.py set-target esp32s3 idf.py build idf.py -p PORT flash monitor ``` ### 性能优化建议 1. **缓存字形**:对常用字符预先渲染并缓存位图。 2. **局部刷新**:仅更新文本区域而非全屏。 3. **使用LVGL**:如需复杂UI,可结合LVGL的FreeType支持[^1]。 [^1]: ESP-IDF 编程指南:SPIFFS 文件系统 [^2]: FreeType 官方文档:字形渲染 §§相关问题§§ 1. 如何减少中文字体文件的大小? 2. 如何实现中文文本自动换行? 3. 在ESP32-S3上使用FreeType渲染中文的性能瓶颈是什么?</think>在 ESP32-S3 平台上使用 ESP-IDF v5.3 通过 `espressif/freetype` 实现中文字体渲染的完整步骤如下: ### 一、环境准备 1. **安装 ESP-IDF v5.3** ```bash git clone -b v5.3 --recursive https://github.com/espressif/esp-idf.git cd esp-idf ./install.sh source export.sh ``` 2. **创建工程** ```bash idf.py create-project freetype_chinese cd freetype_chinese ``` ### 二、添加必要组件 1. **添加 FreeType 组件** ```bash cd components git clone https://github.com/espressif/component-freetype.git ``` 2. **配置 SPIFFS 分区** - 在 `partitions.csv` 中添加: ``` # Name, Type, SubType, Offset, Size, Flags spiffs, data, spiffs, 0x180000, 0x170000, ``` ### 三、准备中文字体文件 1. **下载中文字体**(如 Noto Sans SC): ```cmake # CMakeLists.txt set(SPIFFS_DIR "${CMAKE_BINARY_DIR}/spiffs") file(MAKE_DIRECTORY ${SPIFFS_DIR}) file(DOWNLOAD "https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/SimplifiedChinese/NotoSansSC-Regular.otf" "${SPIFFS_DIR}/NotoSansSC.otf" SHOW_PROGRESS) spiffs_create_partition_image(spiffs ${SPIFFS_DIR} FLASH_IN_PROJECT) ``` ### 四、配置 FreeType 组件 ```c // sdkconfig.defaults CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=y CONFIG_SPIFFS_USE_MAGIC=y CONFIG_SPIFFS_USE_MTIME=y CONFIG_FREETYPE_USE_AUTOHINT=n # 禁用自动提示提升性能 ``` ### 五、主程序代码 ```c // main.c #include "esp_log.h" #include "esp_spiffs.h" #include "freertos/FreeRTOS.h" #include "ft2build.h" #include FT_FREETYPE_H #define TAG "FT_CN" #define FONT_PATH "/spiffs/NotoSansSC.otf" void app_main() { // 初始化 SPIFFS esp_vfs_spiffs_conf_t conf = { .base_path = "/spiffs", .partition_label = "spiffs", .max_files = 5, .format_if_mount_failed = true }; ESP_ERROR_CHECK(esp_vfs_spiffs_register(&conf)); // 初始化 FreeType FT_Library library; FT_Error error = FT_Init_FreeType(&library); if (error) { ESP_LOGE(TAG, "FreeType init error: %d", error); return; } // 加载中文字体 FT_Face face; error = FT_New_Face(library, FONT_PATH, 0, &face); if (error) { ESP_LOGE(TAG, "Font load error: %d", error); return; } // 设置字体大小(24像素) error = FT_Set_Pixel_Sizes(face, 0, 24); if (error) { ESP_LOGE(TAG, "Set size error: %d", error); return; } // 渲染中文字符 "你好" const wchar_t *text = L"你好"; for (int i = 0; text[i] != '\0'; i++) { // 获取字符索引 FT_UInt glyph_index = FT_Get_Char_Index(face, text[i]); // 加载字形 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); if (error) continue; // 渲染为位图 error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if (error) continue; // 处理位图数据 FT_Bitmap *bitmap = &face->glyph->bitmap; ESP_LOGI(TAG, "Char: %lc, Width: %d, Height: %d", text[i], bitmap->width, bitmap->height); // 此处添加实际显示代码(如 LCD/LED 驱动) } // 清理资源 FT_Done_Face(face); FT_Done_FreeType(library); esp_vfs_spiffs_unregister("spiffs"); } ``` ### 六、编译与烧录 ```bash idf.py set-target esp32s3 idf.py build idf.py -p /dev/ttyUSB0 flash monitor ``` ### 关键说明 1. **字体选择**: - 必须使用支持中文的字体(如 Noto Sans SC) - OTF/TTF 格式均可,文件大小通常 10-20MB 2. **内存优化**: ```c // 启用 PSRAM CONFIG_SPIRAM=y CONFIG_FREETYPE_USE_PSRAM=y ``` - 大字体需启用 PSRAM - 使用 `FT_Outline_Decompose()` 进行字形简化[^1] 3. **渲染流程**: ```mermaid graph LR A[初始化SPIFFS] --> B[加载字体] B --> C[设置字号] C --> D[字符转Unicode] D --> E[获取字形索引] E --> F[渲染位图] F --> G[显示输出] ``` 4. **性能优化**: - 预渲染常用字符到缓存 - 使用 `FT_RENDER_MODE_MONO` 单色渲染 - 简化复杂字形:`FT_Outline_Embolden()` ### 常见问题解决 1. **内存不足**: ```bash # 增加堆大小 CONFIG_ESP32S3_DATA_CACHE_16KB=n CONFIG_FREERTOS_HZ=1000 ``` 2. **字体加载失败**: - 检查 SPIFFS 挂载状态 - 验证字体文件完整性(`ls -l /spiffs`) 3. **中文乱码**: - 确保使用 `wchar_t` 类型 - 设置区域:`setlocale(LC_ALL, "zh_CN.UTF-8")` > 完整示例工程:[esp32-freetype-chinese-example](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/lcd/spi/esp32s3_lcd_ev_board)[^2] [^1]: FreeType 字形处理文档 [^2]: ESP-IDF 官方示例库
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值