You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言实现区间堆,特定输入下前序/中序遍历打印结果异常

问题描述

抱歉我的英语不太好,我的母语是韩语。
我刚学习了区间堆(interval heap)的相关知识,正在用C语言实现区间堆。
我的代码预期逻辑为:从输入文件读取整数,若读取的整数为正,则将该值插入区间堆;若为负,则执行删除最小值操作。每次完成插入或删除操作后,需要将区间堆的前序(preorder)、中序(inorder)遍历结果打印到标准输出。
完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct ipair_{
    int min;    //inserted first
    int max;
    int cnt;    //number of data in node
} interval;

typedef struct{
    interval * h;
    int size;
    int cnt;    //number of node
} *iheap;

typedef enum{
    NO_EMPTY=0,
    EMPTY_HEAP
} isEmpty;

void init_heap(iheap h, int n)
{   //initialize heap;
    n++;    // 1_based indices
    h->h = calloc(n, sizeof *h->h);
    h->size = n;
    h->cnt = 0;
}

//parent, child for 1_based_index
inline static int prt(int i)
{
    return i/2;
}

inline static int lch(int i)
{
    return i*2;
}

inline static int rch(int i)
{
    return i*2+1;
}

void insert_aux(iheap h, int cur, int data)
{   //insert to h->h[cur] in accordance with state of node;
    interval *current = h->h + cur;
    if((*current).cnt == 0)
    {
        (*current).min = data;
        (*current).cnt++;
    }
    //else if(h->h[h->cnt].cnt == 1)
    else if((*current).cnt == 1)
    {
        int prev = (*current).min;
        if(data < prev)
        {
            (*current).min = data;
            (*current).max = prev;
        }
        else
        {
            (*current).max = data;
        }
        (*current).cnt++;
    }
    else// if((*current).cnt >= 2)
    {
        current++;
        (*current).min = data;
        (*current).cnt++;
        h->cnt++;
    }
}

void insert(iheap h, int data)
{
    int cur = h->cnt + 1;
    int parent = prt(cur);
    if(data > h->h[parent].max)
    {
        while(data > h->h[parent].max)
        {
            if(cur == 1)
            {
                break;
            }
            //h->h[cur].max = h->h[parent].max;
            insert_aux(h, cur, h->h[parent].max);
            cur = parent;
            parent = prt(parent);
        }
        //h->h[cur].max = data;
        insert_aux(h, cur, data);
    }
    else if(data < h->h[parent].min)
    {
        while(data < h->h[parent].min)
        {
            if(cur == 1)
            {
                break;
            }
            //h->h[cur].min = h->h[parent].min;
            insert_aux(h, cur, h->h[parent].min);
            cur = parent;
            parent = prt(parent);
        }
        //h->h[cur].min = data;
        insert_aux(h, cur, data);

    }
    else
    {
        insert_aux(h, cur, data);
    }
}

void preorder(iheap h, int i)
{
    if(h->h[i].cnt)
    {
        if(h->h[i].cnt == 1)
        {
            printf(" (%d)", h->h[i].min);
        }
        else
        {
            printf(" (%d,%d)", h->h[i].min, h->h[i].max);
        }
        preorder(h, lch(i));
        preorder(h, rch(i));
    }
}

void printPreorder(iheap h)
{
    printf("preorder:");
    preorder(h, 1);
    putchar(10);
}

void inorder(iheap h, int i)
{
    if(h->h[i].cnt)
    {
        inorder(h, lch(i));
        if(h->h[i].cnt == 1)
        {
            printf(" (%d)", h->h[i].min);
        }
        else
        {
            printf(" (%d,%d)", h->h[i].min, h->h[i].max);
        }
        inorder(h, rch(i));
    }
}

void printInorder(iheap h)
{
    printf("inorder:");
    inorder(h, 1);
    putchar(10);
}

isEmpty deleteMin(iheap h, int * buf)
{
    int p, last, cur, temp;
    _Bool rch_lower;
    if(!h->cnt)
    {
        if(h->h[0].cnt == 0)
        {
            return EMPTY_HEAP;
        }
        else if(h->h[0].cnt == 1)
        {
            *buf = h->h[0].min;
            h->h[0].cnt--;
            return NO_EMPTY;
        }
    }
    *buf = h->h[0].min;
    last = h->cnt + 1;
    p = h->h[last].min;
    h->h[last].cnt--;
    if(h->h[last].cnt == 0)
    {
        h->cnt--;
    }
    cur = 1;
    while(1)
    {
        if(!h->h[lch(cur)].cnt)
        {
            break;
        }
        int minch;
        rch_lower = 0;
        if(h->h[rch(cur)].cnt == 0)
        {
            minch = h->h[lch(cur)].min;
        }
        else
        {
            if(h->h[lch(cur)].min > h->h[rch(cur)].min)
            {
                rch_lower = 1;
                minch = h->h[rch(cur)].min;
            }
            else
            {
                minch = h->h[lch(cur)].min;
            }
        }
        if(!(p>minch))
        {
            break;
        }
        h->h[cur].min = minch;
        if(rch_lower)
        {
            cur = rch(cur);
        }
        else
        {
            cur = lch(cur);
        }
        if(p > h->h[cur].max)
        {
            temp = p;
            p = h->h[cur].max;
            h->h[cur].max = temp;
        }
    }
    h->h[cur].min = p;
    return NO_EMPTY;
}

int main(int argc, char * * argv)
{
    int * buf;
    int cnt;
    int temp;

    iheap h = malloc(sizeof *h);

    if(argc != 2)
    {
        printf("usage: %s <filename>", argv[0]);
        exit(EXIT_FAILURE);
    }

    FILE *fp = NULL;
    if((fp = fopen(argv[1], "r")) == NULL)
    {
        //file open failed
        fprintf(stderr, "file open error\n");
        exit(EXIT_FAILURE);
    }

    //file open succeeded

    for(cnt=0; ; cnt++)
    {
        if((fscanf(fp, "%i", &temp)) == EOF)
        {
            break;
        }
    }
    rewind(fp);
    buf = calloc(cnt, sizeof *buf);
    for(int i=0; i<cnt; i++)
    {
        fscanf(fp, "%i", buf+i);
    }

    init_heap(h, cnt);

    for(int i=0; i<cnt; i++)
    {
        if(buf[i] < 0)
        {
            if(deleteMin(h, &temp))
            {
                puts("공백 트리");
            }
            else
            {
                printPreorder(h);
                printInorder(h);
            }
        }
        else
        {
            insert(h, buf[i]);
            printPreorder(h);
            printInorder(h);
        }
    }

    return 0;
}

短输入场景下代码运行看似正常,但输入如2 40 3 17 4 32 4 12 3 11 5 10 6 30 4 10 5 11 5 9 4 7 8 8 7 9 15 15 -1的长序列时,会输出异常结果:

...
(skipped)
...
preorder: (2,40) (3,17) (4,12) (4,10) (5,11) (3,11) (5,9) (4,7) (4,32) (5,10) (8,8) (7,9) (15,30) (10)
inorder: (4,10) (4,12) (5,11) (3,17) (5,9) (3,11) (4,7) (2,40) (8,8) (5,10) (7,9) (4,32) (10) (15,30)
preorder: (2,40) (3,17) (4,12) (4,10) (5,11) (3,11) (5,9) (4,7) (4,32) (5,10) (8,8) (7,9) (15,30) (10) (15) (1041,0) (1919247474,841490490)
inorder: (4,10) (4,12) (5,11) (3,17) (5,9) (3,11) (4,7) (2,40) (8,8) (5,10) (7,9) (4,32) (10) (15,30) (1041,0) (15) (980575588,741615648)
preorder: (3,40) (3,17) (4,12) (4,10) (5,11) (4,15) (5,9) (7,11) (4,32) (5,10) (8,8) (7,9) (15,30) (10)
inorder: (4,10) (4,12) (5,11) (3,17) (5,9) (4,15) (7,11) (3,40) (8,8) (5,10) (7,9) (4,32) (10) (15,30)

我无法定位代码中的逻辑错误,运行时无编译报错、也没有触发段错误,但运行行为不符合预期,希望能得到帮助排查问题。


问题排查与修复

1. 堆数组越界(垃圾值直接诱因)

你采用1基索引存储堆节点,初始化时仅为输入总数cnt分配了cnt+1个节点空间,但每个interval节点最多存2个元素,插入逻辑中当前节点存满后会直接访问下一个节点,当元素总数较多时会超出申请的内存范围,读取未初始化的随机值。
修复方案:初始化时扩大节点分配量,改为n = (n/2) + 4即可覆盖所有存储需求,也可直接临时分配2倍输入长度的节点空间验证问题。

2. 根节点索引错误

你的整个堆设计为1基索引,根节点是下标1的位置,但deleteMin函数中全部用下标0访问根节点,读取的是堆数组前未使用的无效内存,导致删除最小值逻辑完全错误。
修复方案:将deleteMin函数中所有h->h[0]替换为h->h[1]。

3. 首次插入计数逻辑缺失

插入第一个元素时,h->cnt没有从0更新为1,导致后续父节点计算的起始位置错误,插入逻辑的上浮流程异常。
修复方案:在insert_aux函数的空节点插入分支中,新增判断如果是根节点首次插入,将h->cnt置为1。

4. 遍历逻辑无边界校验

前序、中序遍历函数仅判断节点cnt是否为0,没有校验当前下标是否超出堆数组的最大长度,下标越界后访问的无效内存如果cnt值非0,会递归输出垃圾值。
修复方案:在preorder和inorder函数的第一行新增判断if (i >= h->size) return;,避免越界访问。

内容的提问来源于stack exchange,提问作者k9wan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 22:48:01