【问题标题】:adding a node to a linked list using a function使用函数将节点添加到链表
【发布时间】:2015-04-02 20:57:53
【问题描述】:

我目前有一个链表,需要向其中添加用户从键盘输入的数据,所以我有两个结构:

struct CourseInfo {
    int courseID;
    char courseName[30];
};
typedef struct CourseInfo courseinfo;
struct StudentInfo {
    char StudentID[10];
    char FirstName[21];
    char LastName[26];
    int num_course;
    courseinfo array[10];
    struct StudentInfo *next;
};

所以我目前有一个包含 3 个节点的链表。然后我需要调用一个函数并添加一个节点。节点需要插入到正确的位置,即之前的学生ID需要小于它,之后的学生ID需要更大,所以我拥有的当前ID是111111111、333333333和444444444,我正在尝试添加222222222所以它会放在第二个位置,所以我的函数看起来像:

studentinfo *addStudent(studentinfo *data) //returns type studentinfo* now
{
    studentinfo *add;
    add = malloc(sizeof(studentinfo));
    add->next = NULL; //Now its set to NULL to begin
    int knt;
    printf("%s", "Adding new student:\nStudent ID: ");
    scanf("%s", add->StudentID);
    printf("%s", "First Name: ");
    scanf("%s", add->FirstName);
    printf("%s", "Last Name: ");
    scanf("%s", add->LastName);
    printf("%s", "Number of courses: ");
    scanf("%d", &add->num_course);
    for(knt = 0; knt < add->num_course; knt++) {
        printf("%s", "Course ID: ");
        scanf("%d", &add->array[knt].courseID);
        printf("%s", "Course Name: ");
        scanf("%s", add->array[knt].courseName);
    }
    if(searchStudentID(data, add->StudentID)) {
        puts("immediately inside if");
        while(data != NULL) {
            puts("Immediately inside while");
            if(strcmp(add->StudentID, data->StudentID) < 0) {
                puts("inside if");
                add->next = data;
                data = add;
            }
            else {
                puts("inside first else");
                studentinfo *PrevPtr = data;
                studentinfo *NPtr = data->next;
                while(NPtr != NULL) {
                    ("inside while(NPTR != NULL)");
                    if(strcmp(add->StudentID, NPtr->StudentID) < 0) {
                        add->next = PrevPtr;
                        PrevPtr->next = add;
                        break;
                    }
                    else {
                        puts("inside a differnet else");
                        PrevPtr = NPtr;
                        NPtr = NPtr->next;
                    }
                }
                if(PrevPtr->next == NULL) {
                    puts("inside last if");
                    add->next = NULL;
                    PrevPtr->next = add;
                }
            }
        }
    }
    else {
        puts("Found id");
    }
    return data; //returns data back to call
}

所以我添加了所有这些puts 语句,因为我想看看为什么程序不断崩溃。所以 puts 语句puts("Inside a different else") 陷入了无限循环并继续打印。如果我们还没有 ID,则函数 searchStudentID 仅返回 1,如果我们已经拥有它,则返回 0。我知道此功能有效,因此无需发布。

我认为问题可能出在休息区;语句,因为它不会从第一个 while 循环中退出,而只是从内部循环中退出,但我不是肯定的。对该函数的调用如下所示:

list = addStudent(list); //Now the new data is stored in list

list是3个节点的链表

【问题讨论】:

  • 您是否使用调试器浏览过代码
  • @pm100 我目前正在使用 code::blocks,当我构建和运行时我没有收到任何错误
  • 不相关,else 子句中的最后一条消息应为:“Found id and leaked memory” 关于实际问题,只要在您执行此操作的任何地方说明就足够了:data = (anything) 表示 nothing 回到此函数的调用方。
  • 调试器是一种工具,可让您探索程序的执行情况。即逐行浏览它,看看它在做什么。你应该学会使用一个

标签: c linked-list nodes


【解决方案1】:

链表管理是关于管理节点指针,而不仅仅是节点。您想要做几件事来让自己更轻松:

  • 将输入步骤与搜索+插入步骤分开。无论他们看起来如何,他们都不属于一起。这给您带来的最大好处是将您的列表插入代码减少到它应该做的事情(并且它应该做的事情):管理链接列表。我保持你的完好无损,但你真的应该检查错误并在其他地方读取数据。

  • 使用指向指针的指针遍历列表。这样做的最大好处是消除了特殊情况下头部位置插入的需要。如果这是一个新节点最终将占据的位置,那就这样吧,但消除这种特殊情况会进一步降低算法的复杂性。

  • 除非您能够保留要用于插入逻辑的搜索结果,否则不要搜索列表。对链表执行 O(N) 扫描以确定输入数据是否已经存在是没有意义的,只是再次搜索它以找到所述数据将实际插入的位置。做一次。找到它所属的位置。如果它已经在那里,什么也不做,否则,你就坐在正确插入位置的边缘已经

  • 最后,除非您知道自己需要一个,否则不要分配新节点。如果您最终什么都不做,请使用有助于自我丢弃的自动变量。

将所有这些放在一起会得到这样的结果:

struct CourseInfo {
    int courseID;
    char courseName[30];
};
typedef struct CourseInfo CourseInfo;

struct StudentInfo {
    char StudentID[10];
    char FirstName[21];
    char LastName[26];
    int num_course;
    CourseInfo array[10];
    struct StudentInfo *next;
};
typedef struct StudentInfo StudentInfo;

StudentInfo *addStudent(StudentInfo *head)
{
    StudentInfo **pp = &head, *p = NULL, rec;
    int knt;

    // TODO: error check your inputs!
    printf("%s", "Adding new student:\nStudent ID: ");
    scanf("%s", rec.StudentID);
    printf("%s", "First Name: ");
    scanf("%s", rec.FirstName);
    printf("%s", "Last Name: ");
    scanf("%s", rec.LastName);
    printf("%s", "Number of courses: ");
    scanf("%d", &rec.num_course);
    for(knt = 0; knt < rec.num_course; knt++) {
        printf("%s", "Course ID: ");
        scanf("%d", &rec.array[knt].courseID);
        printf("%s", "Course Name: ");
        scanf("%s", rec.array[knt].courseName);
    }

    // walk the list pointers, starting with head, looking for
    //  a node that is equal or greater than the input node
    while (*pp && (knt = strcmp((*pp)->StudentID, rec.StudentID)) < 0)
        pp = &(*pp)->next;

    // leave now if already present
    if (*pp && knt == 0)
        return head;

    // allocate new node
    p = malloc(sizeof *p);
    if (p == NULL)
    {
        perror("Failed to allocate new node");
        exit(EXIT_FAILURE);
    }

    // structure copy.
    *p = rec;

    // link into proper list position.
    p->next = *pp;
    *pp = p;

    // always return the head (which may have updated above)
    return head;
}

就是这样。如前所述,我会亲自在除此功能之外的其他地方执行输入操作,但我将其留给您考虑。

祝你好运。

【讨论】:

  • 非常感谢您提供非常详细的回答,当您通过p = malloc(sizeof *p) 分配内存时,我有一个问题我不明白,不应该是p = malloc(sizeof(studentinfo)),因为sizeof *p 自@987654326 以来不会为0 @
  • @JackRV sizeof 是一个编译时运算符构造;它的工作方式与您最初想象的略有不同。 See this question and selected answer
  • 哦,好的,我明白谢谢,它的工作方式与我想象的不同
  • 现在有点无关紧要,但是当您输入 while (*pp &amp;&amp; (knt = strcmp(rec.StudentID, (*pp)-&gt;StudentID)) &lt; 0) 时,应该是 &gt;0 而不是 &lt;0
【解决方案2】:

由于此功能更新您需要的列表

studentinfo *addStudent(studentifo *data)

并返回更新后的头部值。或者

void addStudent(studentifo **data)

然后做

*data = <new thing>

【讨论】:

    【解决方案3】:

    我看到的问题:

    1. 您没有将add-&gt;next 设置为NULL

    2. 您正在本地更改data

              add->next = data;
              data = add;
      

      在函数中本地更改data 的值。它不会改变调用函数中的值。

    3. 你有支票

      while(data != NULL)
      

      遵循if 声明

      if(searchStudentID(data, add->StudentID)) {
      

      但是当searchStudentID(data, add-&gt;StudentID) 返回falsedata == NULL 开始时,我没有看到任何添加新学生的代码。

    【讨论】:

    • 好吧,如果 searchStudentID 返回 0,那么我不想添加数据,因为学生已经在我们的文件中
    • @JackRV,这是有道理的。以data == NULL开头的情况呢?
    • 所以我编辑了我的问题和程序以满足这个标准,我明白为什么我需要添加这些东西,尽管我的程序仍然在同一个地方陷入无限循环
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-27
    • 2017-03-19
    • 1970-01-01
    • 2020-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多