简体   繁体   中英

adding to sorted linked list

i have a linked list using structs, right now the user mush add the grades from the lowest to highest to be sorted or from highest to lowest. i couldn't write the third condition eg the user entered ACB the program will crash so i dont know what is the problem.

i know my code is a mess :D and i am sry for that also Excuse my bad English.

here are the structs:

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;

and here is the function:

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;

        }
    }

}

In the original code, this fragment was dubious:

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

The indentation suggests that the close brace on its own line is an interloper; you don't need an empty else block, and the indentation suggests that the if (current->head == NULL) code should be part of the body of the else rather than a new set of conditions. If you remove the line identified as an interloper, you probably need an extra close brace at the end of the function.

If you indent your code cleanly, then it is easy to see how the blocks line up. When you make a mess of the indentation, it is hard to spot errors like this. Indentation is used to make it easy to read and understand the code, and misleading indentation means the code is hard to read and difficult to understand and get right.


In the revised code, stripped of original comments, reformatted and indented for legibility, and annotated with new comments for discussion points, we have:

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;
        }
    }
}

The position of the open braces for blocks is debatable. There are those, including K&R, who like the opening brace on the same line as the if , else , while , for , switch , etc. I strongly prefer the Allman Style of indenting, but work with local conventions when necessary. When I reformat something heavily, it goes into Allman style — but you shouldn't interpret that as anything other than perverseness on my part. The change is not part of any critique of your code.

You don't show us how a student record with no course records is represented. The most plausible representation is that the head , tail and current are all initialized to NULL.

Your function takes a student record, but (surprisingly) not a course to be added, despite the name add_course() . Given the use of current->current at P4, we have to assume that you've partially added a course by setting current->current to the new course before calling the function, and you need the function to fix up the position of the new course in the list starting at head and finishing at tail . This is not very functionally cohesive — it is a poor design. Ideally, the new course record should be passed to the function as a separate argument, and the next field should be NULL (and the course name should be initialized, and the grade must be initialized):

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

We can observe that the variables at P1 and P2 are not used until P6 or P7, so the code at P1, P2, and P3 should be moved to before P6 or later. I'll stick with C89 code rather than using the 'define anywhere' rules supported by C99 (and C11 and C++).

The paragraph at P4 to P5 is supposed to deal with an empty list. It could be written to use tmp (in which case, the definition at P2 should stay where it is). It might be clearer to write:

 COURSE *new_course = current->current;

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

The final assignment should be unnecessary if the course is properly initialized before the function is called.

Suppose there is already one course associated with the student and a new course is added by the calling code changing the value of current->current to a new course, and calling this function. There are several possible situations to consider:

  1. The new course is a duplicate of an (the) existing course (and should be rejected — but the function doesn't return a value, so there's no way to indicate that the course was rejected).
  2. The new course grade is lower than the existing course grade.
  3. The new course grade is higher than the existing course grade.
  4. The new course grade is the same as the existing course grade.

The problem has not specified what should be done for the first and last situations. When the second situation arises, the new course should be inserted before the existing course; when the third arises, the new course should be inserted after the existing course. For definiteness, if the new grade is the same, then the courses should be listed alphabetically. Dealing with duplicates involves a search of the entire list for a match; it should probably be encapsulated into a function:

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

The function takes a student, searches the list from head to tail , comparing the course name with the course in the list. It returns the element in the list that matches if there is one; the code here will simply require that the function returns NULL indicating that the name is not found.

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;
}

It would be possible, and perhaps more flexible, to have this function interface and implementation changed to:

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

This can be used to search any properly constructed list of courses, such as a list of valid courses (so you can reject invalid courses).

We should also review what should happen when there are multiple existing courses, so that we can avoid repeated code. The duplicate course check is the same and should still come first. Since we can safely assume by induction that the empty list is in order, and the single element list is in order, we can decide that add_course() will always ensure that the list of courses is in order.

However, I'm going to leave that for you to work on.

We're going to need a course comparison function. We can use the same conventions as strcmp() , returning a negative number if the first argument should come before the second, a positive number if the second should come before the first; and (nominally) zero if the two courses are the same:

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));
}

Continuation

[...a long pause...24 hours or more passes...]

Here is your code, decommented, wrapped into a working, compiling, running (C99) program. Beyond mild reformatting and adding the assertion, I've not changed the code in add_course() at all.

#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);
}

Note the dump_student() function; I find functions with that sort of interface very useful, and often leave them available in code for later debugging. The FILE * argument means that the output can be sent to standard error (or a log file) and the tag to identify which occurrence of the call is being run. You could add file, line, function name to the interface if you wanted to; I normally do not do that.

There are only a couple of places where the code is C99; the for loop in main() and the course pointer definition in dump_student() ; you can move the variable definitions if your C compiler does not support C99 syntax.

And this is the example output on a 64-bit compilation on Mac OS X 10.7.4.

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)

Note that the first couple of insertions are fine, but there are problems after that. I ran under valgrind and that gives the code a clean bill of health (though there is no dynamic memory allocation outside the system libraries).

I suggest you track down why the list is not being extended properly after the third insertion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM