C中的插入排序链接列表

时间:2017-03-10 08:00:16

标签: c linked-list singly-linked-list insertion-sort

因此,对于我的一类,我需要在C中编写一个链接列表,它在从文件中读取时添加或删除项目,并且在插入它们时,它们通过插入排序按顺序放置。文件中的每一行都包含一个名称,后跟a或d,表示是否添加或删除该名称。

我理解链表的概念,并在之前用Java实现过。出于某种原因,我无法让它在C中工作。任何帮助都将非常感激。

第一个值现在进入列表,但还有另一个问题如下所示。

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

struct node
{
    char name[42];
    struct node *next;
};

void insertNode(char name[42], struct node *head)
{
    struct node *new = (struct node*)malloc(sizeof(struct node));
    strcpy(new->name, name);

    struct node *curr = head;
    int finished = 0;

    if(!curr)
    {
         head = new;
         return;
    }

    while(curr->next != NULL) //This loop right here is not working
    //it doesn't make it into the loop, curr somehow equals null
    //not sure why this isn't working
    {
        if((strcmp(curr->name, new->name) < 0))
        {
                new->next = curr->next;
                curr->next = new;
                finished = 1;
                break;
        }
        curr = curr->next;
    }

    if(finished = 0)
    {
        new->next = curr->next;
        curr->next = new;
    }
}

void removeNode(char name[42], struct node *head)
{
    struct node *temp = (struct node*)malloc(sizeof(struct node));
    strcpy(temp->name, name);
    struct node *curr = head;

    while(curr->next != NULL)
    {
        if(curr->next == temp)
        {
            curr->next = temp->next;
            free(temp->name);
            temp->next = NULL;
        }
    }
}

void FREE(struct node *head)
{
    int i;
    struct node *temp;

    while(head != NULL)
    {
        temp = head;
        head = head->next;
        free(temp);
    }
}


void printList(struct node *head)
{
    struct node *curr = head;

    printf("Linked list:\n");

    while(curr != NULL)
    {
        printf("%s\n", curr->name);
        curr = curr->next;
    }
}

int main(void)
{
    FILE *input = fopen("hw7.data", "r");
    struct node *head = (struct node*)malloc(sizeof(struct node));
    head->next = NULL;
    strcpy(head->name, "");

    char *tempText = NULL;
    char *tempName = NULL;
    char *tempOP = NULL;

    size_t lineLen;
    int i = 0;
    getline(&tempText, &lineLen, input);

    while(!feof(input))
    {

        tempName = strtok(tempText, " ");
        tempOP = strtok(NULL, "\n");

        if(i == 0)
        {
            strcpy(head->name, tempName);
            i = 1;
        }

        if(tempOP[0] == 'a')
        {
            insertNode(tempName, head);
        }
        else
        {
            removeNode(tempName, head);
        }

        getline(&tempText, &lineLen, input);
    }

    printList(head);
    fclose(input);
    FREE(head);
    return 0;
}

这是数据文件:

Beverly a
Kathy a
Radell a
Gary a
Chuck a
David a
kari a
Tom a
Tanya a
Scott a
Beverly d
Brenda d
Kathy a
Gary a
WenChen a
Chuck a
Mike a
Emanuel a
Linda a
Bernie a
Hassan a
Brian a
Gary d
Kathy d
Gary a
Eunjin a
Kathy a
Brenda a
Jun a
Peanut a
Travis a

1 个答案:

答案 0 :(得分:3)

此代码中存在大量错误,包括但不限于:

  • 错误的外部参数处理(即它们不是外部参数,但应该是)。
  • 对从未直接free&d;或malloc&#39; d
  • 的数据调用realloc
  • 在某种情况下错误地使用feof
  • 指针行走的多个问题。
  • 需要比较的分配。可能是一个错字,但很关键,不可能。
  • 潜在缓冲区溢出

为每个代码重新编写代码,从基础知识开始。为避免缓冲区溢出结构name成员,只需使用动态分配。您已经使用链接列表杀死了缓存;也可以确保它在6英尺以下:

struct node
{
    char *name; /* will use strdup() for allocation */
    struct node *next;
};

接下来,插入例程可以使用指向指针的指针遍历列表(main通过地址传递head指针,这样我们就可以通过解引用。这很关键,但代码中缺少:

void insertNode(char const name[], struct node **head)
{
    printf("Adding: %s\n", name);

    while (*head && strcmp((*head)->name, name) < 0)
        head = &(*head)->next;

    struct node *p = malloc(sizeof *p);
    p->name = strdup(name);
    p->next = *head;
    *head = p;
}

当涉及到删除时,我们可以做同样的事情,它具有正确的头指针管理的额外优势,即使在链表中的单个节点的情况下,甚至根本没有:

void removeNode(char const name[], struct node **head)
{
    int cmp = 0;
    while (*head && (cmp = strcmp((*head)->name, name)) < 0)
        head = &(*head)->next;

    if (*head && (cmp == 0))
    {
        printf("Removing: %s\n", name);
        struct node *tmp = *head;
        *head = tmp->next;
        free(tmp->name); // note: freeing what we strdup()'d
        free(tmp);
    }
    else
    {
        printf("Not found: %s\n", name);
    }
}

虽然在这种情况下不是必需的,但我总是建议使用与焦土freeList意识形态相同的指针机制的地址指针。它可以确保你不会给喋喋不休的指针留下愚蠢的指针,他们可能会试图取消引用。

void freeList(struct node **head)
{
    while (*head)
    {
        struct node *tmp = *head;
        *head = tmp->next;
        free(tmp->name);
        free(tmp);
    }
}

在打印列表时,使用const指针移动它是微不足道的:

void printList(struct node const *head)
{
    printf("Linked list:\n");

    for (; head; head = head->next)
        printf("%s\n", head->name);
}

最后,您的计划的核心,main。修复while循环仅在实际读取行内容时继续,并正确释放getline的最终结果而不是让它泄漏(这不重要,因为程序将很快退出,但是良好的做法) ,我们得到:

int main()
{
    FILE *input = fopen("hw7.data", "r");
    if (!input)
    {
        perror("Failed to open file: ");
        return EXIT_FAILURE;
    }

    struct node *head = NULL;
    char *text = NULL;
    size_t lineLen = 0;

    while(getline(&text, &lineLen, input) > 0)
    {
        char *name = strtok(text, " ");
        char *op = strtok(NULL, "\n");

        if(*op == 'a')
        {
            insertNode(name, &head);
        }
        else if (*op == 'd')
        {
            removeNode(name, &head);
        }
    }
    fclose(input);
    free(text);

    printList(head);
    freeList(&head);

    return EXIT_SUCCESS;
}

<强>输出

以下是您输入的输出,我添加了insertNoderemoveNode中的工具,让您知道发生了什么:

Adding: Beverly
Adding: Kathy
Adding: Radell
Adding: Gary
Adding: Chuck
Adding: David
Adding: kari
Adding: Tom
Adding: Tanya
Adding: Scott
Removing: Beverly
Not found: Brenda
Adding: Kathy
Adding: Gary
Adding: WenChen
Adding: Chuck
Adding: Mike
Adding: Emanuel
Adding: Linda
Adding: Bernie
Adding: Hassan
Adding: Brian
Removing: Gary
Removing: Kathy
Adding: Gary
Adding: Eunjin
Adding: Kathy
Adding: Brenda
Adding: Jun
Adding: Peanut
Adding: Travis
Linked list:
Bernie
Brenda
Brian
Chuck
Chuck
David
Emanuel
Eunjin
Gary
Gary
Hassan
Jun
Kathy
Kathy
Linda
Mike
Peanut
Radell
Scott
Tanya
Tom
Travis
WenChen
kari

<强>摘要

有很多错误。我解决了上面我能找到的所有内容,并希望提供一些链接列表管理的具体示例,您现在和将来都可以使用它们。

强烈建议您在调试器中使用此代码,并在变量发生时支付非常密切关注变量。加载您的监视列表,并查看每个行在输入项目中的进度。通过调试正确运行的代码来学习可能是一种很好的学习技巧,值得你花费半小时。