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

Project Euler第29题:链表+二分查找实现触发核心转储求助

Project Euler第29题:链表二分查找优化引发的核心转储问题

我正在解决Project Euler的「不同幂问题(第29题)」,目标是找出当2≤a≤100且2≤b≤100时,a^b的不同结果数量。最初的算法能完成因式分解与重复检查,但尝试用链表结合二分查找优化性能时,程序触发核心转储,无法定位逻辑缺陷。以下是相关代码:

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

#define A_MIN 2
#define A_MAX 100
#define B_MIN 2
#define B_MAX 100

struct Node {
    int *data;
    struct Node *next;
    struct Node *previous;
};

void primeFactor(int *arr, int num) {
    for (int k = 2; k <= num; ++k) {
        while (num % k == 0) {
            num /= k;
            arr[k] += 1;
        }
    }
}

int compareArrays(const int *arr1, const int *arr2, int size) {
    for (int k = 0; k < size; ++k) {
        if (arr2[k] < arr1[k]) {
            return -1; // arr2在arr1左侧
        } else if (arr1[k] < arr2[k]) {
            return 1; // arr2在arr1右侧
        }
    }
    return 0; // 两个数组相等
}

void insertSorted(struct Node **head, const int *arr, int size, int *listLength) {
    struct Node *current = *head;

    int pos = 0;
    int left = 0;
    int right = *listLength;

    int comparison = -3;
    
    while (current != NULL && left < right) {
        int mid = (left + right) / 2;
        while (pos < mid) {
            current = current->next;
            pos++;
        }
        while (pos > mid) {
            current = current->previous;
            pos--;
        }
        comparison = compareArrays(current->data, arr, size);
        if (comparison == 1) { // arr2在arr1右侧,往右找
            left = mid + 1;
        } else if (comparison == -1) { // arr2在arr1左侧,往左找
            right = mid;
        } else {
            break;
        }
    }

    struct Node *prev = (current != NULL) ? current->previous : NULL;
    struct Node *nxt = (current != NULL) ? current->next : NULL;

    if (comparison != 0) {
        struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
        if (newNode == NULL) {
            perror("Memory allocation error");
            exit(EXIT_FAILURE);
        }

        newNode->data = (int *)malloc(size * sizeof(int));
        if (newNode->data == NULL) {
            perror("Memory allocation error");
            exit(EXIT_FAILURE);
        }

        memcpy(newNode->data, arr, size * sizeof(int));

        if (comparison == -1) { // 插入到current左侧
            newNode->next = current;
            if (current != NULL) {
                current->previous = newNode;
            }
            if (prev == NULL) {
                *head = newNode;
            } else {
                prev->next = newNode;
                newNode->previous = prev;
            }
        } else if (comparison == 1) { // 插入到current右侧
            newNode->next = nxt;
            if (current != NULL) {
                current->next = newNode;
            }
            if (nxt != NULL) {
                nxt->previous = newNode;
            }
        } else { // 链表为空,设为头节点
            printf("lol");
            *head = newNode;
            
        }

        (*listLength)++;
    }
}

void freeLinkedList(struct Node *head) {
    while (head != NULL) {
        struct Node *temp = head;
        head = head->next;
        free(temp->data);
        free(temp);
    }
}

int main() {
    struct Node *link_list = NULL;
    int size = A_MAX;
    int listLength = 0;

    for (int i = A_MIN; i <= A_MAX; ++i) {
        int arr[A_MAX] = {0};
        primeFactor(arr, i);

        for (int j = B_MIN; j <= B_MAX; ++j) {
            int *arr2 = (int *)malloc(size * sizeof(int));
            if (arr2 == NULL) {
                perror("Memory allocation error");
                exit(EXIT_FAILURE);
            }

            for (int k = 0; k < size; ++k) {
                arr2[k] = arr[k] * j;
            }

            insertSorted(&link_list, arr2, size, &listLength);
        }
    }

    printf("%d\n", listLength);

    freeLinkedList(link_list);

    return 0;
}

问题根源与修复方案

1. 二分查找的指针移动逻辑错误

原代码中,二分循环依赖上一次的current和pos来定位mid位置,当二分区间变化时,指针没有从链表头部重新定位,会导致指针越界(比如mid超过链表实际长度时),触发段错误。

修复:每次计算mid后,从head重新遍历到对应位置:

while (left < right) {
    int mid = (left + right) / 2;
    // 从头部重新定位到mid节点
    current = *head;
    for (int i = 0; i < mid; i++) {
        current = current->next;
    }
    comparison = compareArrays(current->data, arr, size);
    if (comparison == 1) {
        left = mid + 1;
    } else if (comparison == -1) {
        right = mid;
    } else {
        return; // 元素重复,无需插入
    }
}

2. 空链表初始插入逻辑缺失

当链表为空时,current为NULL,循环不会执行,comparison保持初始值-3,插入分支的逻辑会陷入未定义状态。

修复:在函数开头单独处理空链表情况:

if (*head == NULL) {
    struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
    if (newNode == NULL) { perror("Memory allocation error"); exit(EXIT_FAILURE); }
    newNode->data = (int *)malloc(size * sizeof(int));
    if (newNode->data == NULL) { perror("Memory allocation error"); exit(EXIT_FAILURE); }
    memcpy(newNode->data, arr, size * sizeof(int));
    newNode->next = newNode->previous = NULL;
    *head = newNode;
    (*listLength)++;
    return;
}

3. 内存泄漏问题

main函数中每次分配的arr2,如果元素重复未插入,会没有被释放,造成内存泄漏。

修复:调用insertSorted后立即释放arr2:

insertSorted(&link_list, arr2, size, &listLength);
free(arr2); // 无论是否插入都释放

4. 插入位置的边界处理错误

原代码中插入到右侧的逻辑没有正确维护newNode的previous指针,导致链表断裂。

修复:统一插入逻辑,根据二分得到的left位置确定插入点:

// 定位到插入位置的前驱节点
current = *head;
if (left > 0) {
    for (int i = 0; i < left - 1; i++) {
        current = current->next;
    }
}

struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
// 内存分配逻辑...

if (left == 0) {
    // 插入头部
    newNode->next = *head;
    (*head)->previous = newNode;
    *head = newNode;
} else if (left == *listLength) {
    // 插入尾部
    current->next = newNode;
    newNode->previous = current;
} else {
    // 插入中间
    newNode->next = current->next;
    newNode->previous = current;
    current->next->previous = newNode;
    current->next = newNode;
}

额外建议

对于该题,总共有99×99=9801个候选值,重复数量有限,完全可以用哈希表(如C语言的uthash库)替代链表存储因式分解结果,实现更简单且性能足够,无需使用链表+二分查找(链表本身不适合二分,每次定位mid都需要遍历,效率低下)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 22:34:59