gpt4 book ai didi

c - 添加到排序的链表

转载 作者:行者123 更新时间:2023-11-30 18:43:08 25 4
gpt4 key购买 nike

我有一个使用结构的链表,现在用户必须将等级从最低到最高进行排序或从最高到最低进行排序。
我不能写第三个条件
例如用户输入A C B程序将崩溃,所以我不知道是什么问题。

我知道我的代码很乱:D并且我很抱歉
还请原谅我的英语不好。

这是结构:

typedef struct student
{
char sname[32];
char sid[9];
struct student *snext;
struct course *current;
struct course *head;
struct course *tail;
} STUDENT;

typedef struct course
{
char cname[15];
char grade;
struct course *next;
} COURSE;


这是函数:

void add_course(STUDENT *current){

int hx,cy,tmpz;
COURSE *tmp = current->current;
if(current->head!=NULL){
hx = current->head->grade;
cy = current->current->grade;}

//here the first element will be added
if(current->head==NULL){
current->head = current->current;
current->tail = current->current;
current->tail->next = NULL;
}
else{
//here it compares the head grade and the element i want to add
//so if the ascii number of head's grade is greater than the current's grade
//then the current will be the head and the previous head will be after the
//current head
if(hx>cy){
current->current->next = current->head;
current->head = current->current;
current->current = current->head;
while(current->current->next!=NULL)
current->current = current->current->next;
current->tail->next = current->current;
current->tail = current->current;
current->tail->next = NULL;
}

else{
//what i am trying to do here is e.g. if i have three elements already and
//their grades are ( first element: A second element: C third element: D)
//and i want to add a new element and its grade is B
//so the problem here it doesnt sort them it only crash in this condition
//i dont know what i am really doing here lol
//third condition
current->current = current->head;
hx = current->current->grade;
tmpz = tmp->grade;
while(current->current->next!= NULL && tmpz>hx){
current->current = current->current->next;
hx = current->current->next->grade;
}
current->current->next = tmp->next;
current->current->next = tmp;
current->current = tmp;

}
}

}

最佳答案

在原始代码中,此片段令人怀疑:

if(current->head!=NULL){
hx = current->head->grade;
cy = current->current->grade;}
else{
}
if(current->head==NULL){


缩进表明在其自己的行上的紧密括号是一个闯入者。您不需要空的 else块,并且缩进表明 if (current->head == NULL)代码应该是 else主体的一部分,而不是一组新的条件。如果删除标识为闯入者的行,则功能末尾可能需要额外的大括号。

如果您清楚地缩进代码,则很容易看到代码块如何排列。当您弄乱缩进时,很难发现这样的错误。缩进用于使代码易于阅读和理解,而误导性缩进意味着代码难以阅读且难以理解和正确使用。



在修订后的代码中,去除了原始注释,重新格式化并缩进了可读性,并为讨论点添加了新注释,我们有:

void add_course(STUDENT *current)
{
int hx,cy,tmpz; // P1
COURSE *tmp = current->current; // P2

if (current->head != NULL) // P3
{
hx = current->head->grade;
cy = current->current->grade;
}

if (current->head == NULL)
{
current->head = current->current; // P4
current->tail = current->current;
current->tail->next = NULL; // P5
}
else
{
if (hx > cy) // P6
{
current->current->next = current->head;
current->head = current->current;
current->current = current->head;
while (current->current->next != NULL)
current->current = current->current->next;
current->tail->next = current->current;
current->tail = current->current;
current->tail->next = NULL;
}
else
{ // P7
current->current = current->head;
hx = current->current->grade;
tmpz = tmp->grade;
while (current->current->next != NULL && tmpz > hx)
{
current->current = current->current->next;
hx = current->current->next->grade;
}
current->current->next = tmp->next;
current->current->next = tmp;
current->current = tmp;
}
}
}


块的开口撑杆的位置值得商bat。包括K&R在内的有些人喜欢与 ifelsewhileforswitch等在同一行上的左括号。我非常喜欢缩进的 Allman Style,但在必要时使用本地约定。当我重新格式化某些内容时,它会变成Allman风格-但是您不应该将其解释为我的不正当行为。所做的更改不属于对代码的任何评论。

您没有向我们展示没有课程记录的学生记录是如何表示的。最合理的表示是 headtailcurrent都初始化为NULL。

您的函数记录了一个学生记录,但是(令人惊讶地)尽管名称为 add_course(),却没有要添加的课程。假设在P4使用了 current->current,我们必须假设您已经通过在调用函数之前将 current->current设置为新课程来部分添加了课程,并且您需要该函数来确定新课程的位置在列表中以 head开始,以 tail结尾。这在功能上并不太紧密-这是一个糟糕的设计。理想情况下,应将新的课程记录作为一个单独的参数传递给函数,并且 next字段应为NULL(课程名应被初始化,并且成绩必须被初始化):

void add_course(STUDENT *student, COURSE *new_course);


我们可以观察到P1和P2处的变量直到P6或P7才被使用,因此P1,P2和P3处的代码应移至P6或以后的位置。我将坚持使用C89代码,而不是使用C99(以及C11和C ++)支持的“在任何地方定义”规则。

P4到P5的段落应该处理一个空列表。可以编写为使用 tmp(在这种情况下,P2的定义应保留在该位置)。编写起来可能更清晰:

 COURSE *new_course = current->current;

if (current->head == NULL)
{
current->head = new_course;
current->tail = new_course;
new_course->next = NULL;
}


如果在调用函数之前正确初始化了课程,则最终的分配应该是不必要的。

假设已经有一个与该学生相关的课程,并且通过调用代码将 current->current的值更改为新课程并调用此函数来添加新课程。有几种可能的情况需要考虑:


新课程是现有课程的重复(应该被拒绝-但是该函数不会返回值,因此无法指示该课程已被拒绝)。
新课程成绩低于现有课程成绩。
新课程成绩高于现有课程成绩。
新课程等级与现有课程等级相同。


该问题未指定在第一种和最后一种情况下应采取的措施。当出现第二种情况时,应在现有课程之前插入新课程;当出现第三条路线时,应在现有路线之后插入新路线。为了确定性,如果新的分数相同,则应按字母顺序列出课程。处理重复项包括在整个列表中搜索匹配项;它可能应该封装到一个函数中:

COURSE *find_course(STUDENT *student, COURSE *course);


该功能将一个学生带入,从 headtail搜索列表,将课程名称与列表中的课程进行比较。它返回列表中匹配的元素(如果有);此处的代码仅要求函数返回NULL,表明未找到名称。

COURSE *find_course(STUDENT *student, COURSE *course)
{
COURSE *next_student = student->head;
while (next_student != NULL && strcmp(next_student->cname, course->cname) != 0)
next_student = next_student->next;
return next_student;
}


可以将此功能接口和实现更改为:

COURSE *find_course(COURSE *course, const char *cname)
{
while (course != NULL)
{
if (strcmp(course->cname, cname) == 0)
return(course);
course = course->next;
}
return(course);
}


这可用于搜索任何适当构造的课程列表,例如有效课程列表(以便您可以拒绝无效课程)。

我们还应该检查当存在多个现有课程时应该发生的情况,以便避免重复的代码。重复的课程检查是相同的,应该仍然优先。由于我们可以通过归纳法安全地假定空列表是按顺序排列的,而单个元素列表是按顺序排列的,因此我们可以确定 add_course()将始终确保课程列表是按顺序排列的。

但是,我将把它留给您继续进行。

我们将需要一个课程比较功能。我们可以使用与 strcmp()相同的约定,如果第一个参数应位于第二个参数之前,则返回负数;如果第二个参数应位于第一个参数之前,则返回正数;如果两个课程相同,则(名义上)为零:

int cmp_course(const COURSE *c1, const COURSE *c2)
{
if (c1->grade < c2->grade)
return -1;
else if (c1->grade > c2->grade)
return +1;
else
return(strcmp(c1->cname, c2->cname));
}


延续性

[...长时间停顿... 24小时或更长时间...]

这是您的代码,经过分解,被包装成可工作,可编译,正在运行(C99)的程序。除了重新格式化和添加断言之外,我根本没有更改 add_course()中的代码。

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>

typedef struct student
{
char sname[32];
char sid[9];
struct student *snext;
struct course *current;
struct course *head;
struct course *tail;
} STUDENT;

typedef struct course
{
char cname[15];
char grade;
struct course *next;
} COURSE;

extern void add_course(STUDENT *current);

void add_course(STUDENT *current)
{
int hx,cy,tmpz;
COURSE *tmp = current->current;
assert(tmp != 0);
if (current->head != NULL)
{
hx = current->head->grade;
cy = current->current->grade;
}

if (current->head == NULL)
{
current->head = current->current;
current->tail = current->current;
current->tail->next = NULL;
}
else
{
if (hx > cy)
{
current->current->next = current->head;
current->head = current->current;
current->current = current->head;
while (current->current->next != NULL)
current->current = current->current->next;
current->tail->next = current->current;
current->tail = current->current;
current->tail->next = NULL;
}
else
{
current->current = current->head;
hx = current->current->grade;
tmpz = tmp->grade;
while (current->current->next != NULL && tmpz>hx)
{
current->current = current->current->next;
hx = current->current->next->grade;
}
current->current->next = tmp->next;
current->current->next = tmp;
current->current = tmp;
}
}
}

static void dump_student(FILE *fp, const char *tag, const STUDENT *student)
{
fprintf(fp, "Student: %s\n", tag);
fprintf(fp, "Name: %s; ID: %s\n", student->sname, student->sid);
fprintf(fp, "Next: 0x%" PRIXPTR "\n", (uintptr_t)student->snext);
fprintf(fp, "Current: 0x%" PRIXPTR "; ", (uintptr_t)student->current);
fprintf(fp, "Head: 0x%" PRIXPTR "; ", (uintptr_t)student->head);
fprintf(fp, "Tail: 0x%" PRIXPTR "\n", (uintptr_t)student->tail);
COURSE *cp = student->head;
while (cp != 0)
{
fprintf(fp, "Course: %-14s (%c) (0x%.16" PRIXPTR ")\n",
cp->cname, cp->grade, (uintptr_t)cp->next);
cp = cp->next;
}
}

int main(void)
{
STUDENT s1 = { "Yours Truly", "me", 0, 0, 0, 0 };
COURSE c[] =
{
{ "Math", 'B', 0 },
{ "English", 'A', 0 },
{ "Science", 'D', 0 },
{ "History", 'C', 0 },
{ "French", 'C', 0 },
};

dump_student(stdout, "Before", &s1);

for (int i = 0; i < 5; i++)
{
char buffer[8];
sprintf(buffer, "After%d", i+1);
s1.current = &c[i];
add_course(&s1);
dump_student(stdout, buffer, &s1);
}
return(0);
}


注意 dump_student()函数;我发现具有此类接口的功能非常有用,并且经常将其保留在代码中以供以后调试。 FILE *参数意味着可以将输出发送到标准错误(或日志文件),并使用标记来标识正在运行哪个调用。如果需要,可以在接口中添加文件,行,函数名称;我通常不这样做。

只有几个地方的代码是C99。 for中的 main()循环和 dump_student()中的课程指针定义;如果您的C编译器不支持C99语法,则可以移动变量定义。

这是Mac OS X 10.7.4上64位编译时的示例输出。

Student: Before
Name: Yours Truly; ID: me
Next: 0x0
Current: 0x0; Head: 0x0; Tail: 0x0
Student: After1
Name: Yours Truly; ID: me
Next: 0x0
Current: 0x7FFF643D84E0; Head: 0x7FFF643D84E0; Tail: 0x7FFF643D84E0
Course: Math (B) (0x0000000000000000)
Student: After2
Name: Yours Truly; ID: me
Next: 0x0
Current: 0x7FFF643D84E0; Head: 0x7FFF643D84F8; Tail: 0x7FFF643D84E0
Course: English (A) (0x00007FFF643D84E0)
Course: Math (B) (0x0000000000000000)
Student: After3
Name: Yours Truly; ID: me
Next: 0x0
Current: 0x7FFF643D8510; Head: 0x7FFF643D84F8; Tail: 0x7FFF643D84E0
Course: English (A) (0x00007FFF643D84E0)
Course: Math (B) (0x00007FFF643D8510)
Course: Science (D) (0x0000000000000000)
Student: After4
Name: Yours Truly; ID: me
Next: 0x0
Current: 0x7FFF643D8528; Head: 0x7FFF643D84F8; Tail: 0x7FFF643D84E0
Course: English (A) (0x00007FFF643D84E0)
Course: Math (B) (0x00007FFF643D8528)
Course: History (C) (0x0000000000000000)
Student: After5
Name: Yours Truly; ID: me
Next: 0x0
Current: 0x7FFF643D8540; Head: 0x7FFF643D84F8; Tail: 0x7FFF643D84E0
Course: English (A) (0x00007FFF643D84E0)
Course: Math (B) (0x00007FFF643D8540)
Course: French (C) (0x0000000000000000)


请注意,插入的前几行很好,但是之后有问题。我在 valgrind下运行,这为代码提供了明确的健康要求(尽管系统库之外没有动态内存分配)。

我建议您跟踪为什么在第三次插入后无法正确扩展列表。

关于c - 添加到排序的链表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11802307/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com