0

我必须在链表上创建一个快速排序(在 C 中)。我的第一个和最后一个指针是一个枢轴(在这段代码中,它是列表的第一个元素)。我必须使用的结构:

typedef struct list_element list_element;

struct list_element {
    char *password;
    int count;
    list_element* next;
};

typedef struct list list;

struct list {
    list_element* first;
    list_element* last;
};

我有一个包含 100 个密码和计数的文件。像这样:password1 123(下一行)password2 435(下一行)password3 133 ...密码必须在这个程序的末尾进行排序(根据他们的计数)。左右列表不需要任何额外的内存分配,因为我只需要使用下一个指针。(这就是练习中的提示所说的。)

给定的主要功能:

int main(int argc, char** args)
{
    if (argc != 2)
    {
        printf("Nutzung: %s <Dateiname>\n",args[0]);
        return 1;
    }
    list mylist;
    init_list(&mylist);
    read_data(args[1],&mylist);
    qsort_list(&mylist);
    printf("Sortierte Liste:\n");
    print_list(&mylist);
    free_list(&mylist);
    return 0;
}

我已经初始化了我的列表:

void init_list(list* mylist)
{
    mylist->first = NULL;
    mylist->last = NULL;
}

并在末尾插入一个新元素(密码 = 文件中的密码,hauefigkeit = 文件中的计数):

void insert_list(list_element* le, list* mylist)
{
    if (mylist->first != NULL) {
        le->next = mylist->last;
        mylist->last = le;
        le->next= NULL;
    }
    else {
        mylist->last->next = le;
        mylist->last = le;
        mylist->last->next = NULL;
    }
}

从文件中读取数据:

void read_data(char* filename, list* mylist)
{
    FILE *file_in = fopen(filename, "r"); 

    if (file_in == NULL) {
        perror("Could not open input file!");
        exit(1);
    }

    char buffer[999] = "0";

    char *passwort = (char*) calloc(1,sizeof(passwort));
    int haeufigkeit = 0;

    while (fgets(buffer, sizeof(buffer), file_in) != NULL) {
        sscanf(buffer, "%s %d", passwort, &haeufigkeit);

        list_element* le = (list_element*)calloc(1,sizeof(list_element));

        for(int i = 0; i <=100; i++) {
            le->password[i] = passwort[i];
        }
        le->count = haeufigkeit;
        le->next = NULL;

        insert_list(le, mylist);
    }
    fclose(file_in);
}

列表分区:

list_element* partition( list* input, list* left, list* right )
{
    list_element* pivot = NULL;
    if (input->first != NULL) {
        list_element* temp;
        pivot = input->first;
        input->first = input->first->next;
        pivot->next = NULL;
        left->first = NULL;
        right->first = NULL;

        while (input->first != NULL) {
            if((pivot->count)>(input->first->count)){
                temp=input->first->next;
                insert_list(input->first, left);
                input->first=temp;
            } 
            else {
                temp = input->first->next;
                insert_list(input->first, right);
                input->first = temp;
            }
        }
    }
    return pivot;
}

实际的快速排序:

void qsort_list(list* mylist)
{
    if(mylist->first == mylist->last){
    }

    else{
        list* left = calloc(1,sizeof(list));
        list* right= calloc(1,sizeof(list));
        list_element* pivot = partition(mylist, left, right);

        qsort_list(left);
        qsort_list(right);

        if(left->first == NULL){
            mylist->first = pivot;
        }
        else{
            mylist->first = left->first;
            left->last->next = pivot;
        }

        if(right->first == NULL){
            pivot->next = NULL;
            mylist->last = pivot;
        }
        else{
            pivot->next = right->first;
            mylist->last = right->last;
        }

        free(right);
        free(left);

    }
}

最后打印列表:

void print_list(list* mylist)
{
    list_element *elem = mylist->first;
    while (elem != NULL) {
        printf("%s %d\n", elem->password, elem->count);
        elem = elem->next;
    }     
}

和免费清单:

void free_list(list* mylist)
{
    list_element *current;
    list_element *second;
    current = mylist->first;
    while (current != NULL) {
        second = current->next;
        free(current);
        current = second;
    }
}

语法应该没问题。GCC (c99, Wall) 编译没有任何问题。

但是存在分段错误。我一直在寻找几个小时,但我不知道问题可能出在哪里。也许你可以帮我解决这个问题。


在前两个答案之后,没有任何分段错误。但是read_data函数还是有问题。程序无法正确读取密码。也许我误解了你关于读取功能的答案。这是当前的功能:

void read_data(char* filename, list* mylist)
{
    FILE *file_in = fopen(filename, "r"); 

    if (file_in == NULL) { 
        perror("Could not open input file!");
        exit(1);
    }

    char buffer[999] = "0";

    int haeufigkeit = 0;

    while (fgets(buffer, sizeof(buffer), file_in) != NULL) {

        char passwort[100];

        sscanf(buffer, "%s %d", passwort, &haeufigkeit);

        list_element* le = (list_element*)    

    calloc(1,sizeof(list_element));


        le->password = passwort;
        le->count = haeufigkeit;
        le->next = NULL;

        insert_list(le, mylist);
    }
    fclose(file_in);
}
4

2 回答 2

1

您的程序遇到的第一个问题是read_data()没有为passwort. 实际上,尚不清楚为什么要动态分配它,但是鉴于您正在这样做,sizeof(passwort)是一个指向 char 的指针的大小(因为这就是它的大小passwort)-可能是 4 或 8 个字节。稍后,当您(尝试)将其内容复制到列表元素中时,您似乎假设分配的空间长度为 100 字节。为什么不简单地将其声明为 100 字符数组?

char passwort[100];

实际上,如果您也list_element.passwort以相同的方式声明,那么循环内的密码复制代码将是正确的,尽管有点不习惯。

事实上,正如@Derlin 所观察到的那样,该代码是有问题的。然而,他提出的解决方案是不正确的。passwort只要为整个例程分配一次,就不能使列表元素指向本地。然后所有列表元素将具有相同的密码字符串,这不是您想要的。如果您希望列表元素像现在一样包含指向密码的指针,那么您需要移动循环passwort 的声明和分配,以便为每个列表元素分配单独的密码空间。那么分配的建议le->password = passwort是正确的。


另一个早期问题是您的insert_list()功能严重损坏。

首先考虑当您尝试将一个元素插入到一个空列表中时会发生什么,如init_list(). 列表nextlast成员都将为空,insert_list()因此将尝试执行此代码:

    mylist->last->next = le;
    mylist->last = le;
    mylist->last->next = NULL;

观察它mylist->last是空的,因此第一行通过尝试取消引用空指针来调用未定义的行为。分段错误是一个非常合理的观察结果。您可以通过将第一行更改为

    mylist->next = le;

现在考虑当您尝试插入非空列表时会发生什么。在这种情况下,您执行以下几行:

    le->next = mylist->last;
    mylist->last = le;
    le->next= NULL;

由于您的意图是在末尾插入新元素(即追加它),因此将新元素的next指针设置为列表的最后一个元素是很奇怪的。以后用 覆盖该值尤其奇怪NULL。您似乎倒退了:您希望将初始last元素设置为指向新元素作为其下一个元素,而不是相反:

    mylist->last->next = le;

事实上,这正是空列表情况下的错误代码,但当列表非空时它很好。

总体而言,您的函数还存在奇怪的缺乏并行性和一些隐藏的代码重复的问题。我可能会编写更像这样的整体功能:

void append_to_list(list_element* le, list* mylist)
{
    le->next= NULL;
    if (mylist->first != NULL) {
        mylist->last->next = le;
        mylist->last = le;
    }
    else {
        mylist->first = le;
        mylist->last = le;
    }
}
于 2017-01-30T19:23:33.500 回答
1

正如 Leonardo Alves Machado 指出的那样,当 C/C++ 程序遇到问题时,第一个反应就是使用gdb. 以下是基础知识:

gcc -g main.c -o main
gdb main
(gdb) run

注意-g编译标志:这会将调试信息添加到可执行文件中。


read_data, 行

for(int i = 0; i <=100; i++) {
    le->password[i] = passwort[i];
}

真的烦我。您为其分配空间passwort(顺便说一句,您从未释放)并尝试将其复制到le->password,这是一个简单的指针(未分配空间)。你真正需要的是le->password 指出 passwort,即

le->password = passwort;

free_list中,不要忘记在释放passwort空间之前释放list_element空间:

while (current != NULL) {
    second = current->next;
    free(current->password);
    free(current);
    current = second;
}
于 2017-01-30T19:22:10.683 回答