简体   繁体   English

如何按字母顺序排列此链接列表?

[英]How can I alphabetize this linked list?

I am writing a program that has names and ages entered into it. 我正在编写一个程序,其中输入了名称和年龄。 The names can then be called and the age of the person will be printed out. 然后可以呼叫姓名,并打印出该人的年龄。 If the person does not exist in the list it prints their age as -1. 如果此人不存在于列表中,则将其年龄打印为-1。 If a name is entered with a new age that is already in the list, the new entry is not added. 如果输入的名称具有列表中已经存在的新年龄,则不会添加新条目。 Currently it appears the names are sorted by the order that I input them. 目前看来,名称是按照我输入它们的顺序排序的。 How can I sort them alphabetically by only changing the code for the function add? 如何仅通过更改add函数的代码按字母顺序对它们进行排序? This code is compileable and works as intended except for the non-alphabetized list. 该代码是可编译的,并且可以按预期工作,但未按字母顺序排列的列表除外。

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

typedef struct person {
    char *name;
    int age;
    struct person *next;
} Person;

void print(Person *); // prints the entire list
Person *add(Person *, char *, int); // adds a new node to the list
int getAge(Person *, char *); // returns the age of the person or -1 if not found

int main(void) {
    char input1[100];
    int input2;
    Person *myList = NULL;
    printf("Enter a person's name (one word) and age : ");
    scanf("%s %d", input1, &input2);
    while (input2 != 0) {
        myList = add (myList, input1, input2);
        printf("\n\nThe list is now : ");   print(myList);
        printf("Enter a name (one word) and age, enter 'xxx' and 0 to exit : ");
        scanf("%s %d", input1, &input2);
    }
    printf("\n\nThe final list is ");   print(myList);
    printf("\n\nEnter the name of a person to look up their age : ");
    scanf("%s", input1);
    while ( strcmp(input1, "xxx") != 0 ) {
        printf("\t%s is %d years old\n", input1, getAge(myList, input1) );
        printf("Enter a name to look up their age or 'xxx' to exit : ");
        scanf("%s", input1);
    }

    return 0;
}

void print(Person *ptr) {
    while (ptr) { printf("[%s-%d] ", ptr->name, ptr->age); ptr = ptr->next; }
    printf("\n");

    return;
}

//adds person to list if the person does not exist already
Person *add(Person *ptr, char *n, int a) {
    Person *newNode = malloc( sizeof(Person) );
    int duplicate = 1;
    Person *dummy = ptr;
    while (dummy) {
        if(strcmp(dummy->name, n) == 0) {
            printf("Name Already Exists in List! Please retry with other name..\n");
            duplicate=-1;
            break;
        }
        else
            dummy = dummy->next;
    }
    if (duplicate!=-1) {
        newNode->name = malloc( strlen(n) + 1 );
        strcpy(newNode->name, n);
        newNode->age = a;
        newNode->next = ptr;
        return newNode;
    }
    duplicate = 1;
    return ptr;
}

//function to find age of the passed person
int getAge(Person *ptr, char *name) {
    while (ptr) {//while loop to traverse entire linked list elements (All persons one by one)
        if(strcmp(ptr->name, name) == 0) //comparing person name in the list with the search key name
            return ptr->age; //if found, returning the age of that person
        else
            ptr = ptr->next; //if not found, check in next node of linked list
    }

    return -1; // if not found, even after visting all nodes, return -1
}

You can do an insertion sort. 您可以进行插入排序。 Each time you add a new record, you scan through the list to see where it belongs and insert it there. 每次添加新记录时,您都在列表中进行扫描以查看其所属位置并将其插入到该位置。 This could be combined with your scan for duplicates. 这可以与扫描重复项结合在一起。

Person *add(Person *head, char *n, int a) {
  char empty[1] = "";
  Person sentinel = {0};
  sentinel.name = empty;
  sentinel.next = head;
  Person *p = &sentinel;
  while (p) {
    int cmp = p->next ? strcmp(n, p->next->name) : -1;
    if (cmp == 0) {
      printf("Name Already Exists in List! Please retry with another name..\n");
      break;
    }
    if (cmp < 0) {
      Person *newNode = malloc( sizeof(Person) );
      newNode->name = malloc( strlen(n) + 1 );
      strcpy(newNode->name, n);
      newNode->age = a;
      newNode->next = p->next;
      p->next = newNode;
      break;
    }
    p = p->next;
  }
  return sentinel.next;  // a possibly-updated copy of head
}

Insertion sort always compares the new element to the next element (rather than to the current element). 插入排序始终将新元素与下一个元素(而不是当前元素)进行比较。 This makes dealing with the first element awkward, especially in a list. 这使得处理第一个元素很麻烦,尤其是在列表中。 We get around that with a temporary "sentinel" that we pretend is just before the head of the list. 我们用一个临时的“前哨”来解决这个问题,我们假装在列表的开头。

There are other approaches. 还有其他方法。 You can create the new node at the head of the list and then slide it down until it's in position. 您可以在列表的顶部创建新节点,然后将其向下滑动直到就位。 If you encounter a duplicate, you remove the new one and patch up the list. 如果遇到重复,请删除新副本并修补列表。 Insertion sorts in other data structures typically work from the tail back toward the head, but that won't work with a singly-linked list. 其他数据结构中的插入排序通常从尾部向头部进行,但不适用于单链列表。

I wrote something similar where I sorted student ID's. 我在排序学生证的地方写了类似的东西。 You should try doing a swap. 您应该尝试进行交换。 Declare a temp variable and used that to swap. 声明一个临时变量并将其用于交换。 The code is something like. 该代码是类似的。

int temp,
    first_name,
    last_name;

temp = first_name;
first_name = last_name;
last_name = temp;

Hope that gives you an idea! 希望能给您一个想法!

Edit: What the other person suggested is a good idea as well, an insertion sort. 编辑:其他人建议的也是一个好主意,一种插入排序。

Your question has already been answered, but I'd like to point out another approach to adding a node. 您的问题已经得到解答,但是我想指出另一种添加节点的方法。 The list is defined by its head node. 该列表由其头节点定义。 When iserting elements, the head node may change. 在浪费元素时,头节点可能会更改。 (It will change in your current code, which adds elements at the front.) In order to reflect the change, you return the new head: (它将在您当前的代码中更改,这会在前面添加元素。)为了反映更改,您返回了新的标题:

myList = add(myList, input1, input2);

This is redundant, because you have to specify myList twice. 这是多余的,因为您必须两次指定myList It is also legal to discard the result returned from a function, so there is the possibility of an error. 丢弃从函数返回的结果也是合法的,因此有可能发生错误。 If you pass in a pointer to the head pointer, you will eliminate the redundancy. 如果将指针传递到头指针,则将消除冗余。

add(&myList, input1, input2); add(&myList,input1,input2);

The ´& will indicate that myList` may change. “&” will indicate that myList`可能会更改。 And you can now use the return value for something else, for example a pointer to the newly inserted node or null if the name was already there. 现在,您可以将返回值用于其他内容,例如,指向新插入的节点的指针;如果名称已经存在,则返回null。

Inserting a person at the front (unconditionally) looks like this: (无条件地)在前面插入一个人,如下所示:

Person *add_front(Person **ptr, const char *name, int age)
{
    Person *p = person_new(name, age);

    p->next = *ptr;
    *ptr = p;

    return p;
}

Inserting at the end requires to walk the list first: 在末尾插入需要先遍历列表:

Person *add_back(Person **ptr, const char *name, int age)
{
    Person *p = person_new(name, age);

    while (*ptr) {
        ptr = &(*ptr)->next;
    }

    p->next = *ptr;
    *ptr = p;

    return p;
}

Note how you do not need to treat the empty list as a special case. 请注意,您无需将空列表视为特殊情况。 Adrian's solution eliminates the special case with a sentinel element. Adrian的解决方案通过哨兵元素消除了特殊情况。 Here, it is eliminated by the pointer itself: ptr points to the list head pointer in the calling function at first. 在这里,它被指针本身消除了: ptr首先指向调用函数中的列表头指针。 As you walk through the list, it points to the next pointer of the previous node. 当您浏览列表时,它指向上一个节点的next指针。 Updating *ptr updated the pointer that took us to the current node. 更新*ptr更新了将我们带到当前节点的指针。

The function person_new creates a new node. 函数person_new创建一个新节点。 I find it tidier to make this a separate function, which you could call from other functions: 我觉得这使它成为一个独立的函数更为简洁,您可以从其他函数中调用它:

Person *person_new( const char *name, int age)
{
    Person *p = malloc(sizeof(*p));

    p->name = malloc(strlen(name) + 1);
    strcpy(p->name, name);
    p->age = age;
    p->next = NULL;

    return p;
}

Now the function you want, which inserts the node in alphabetical order, looks like this: 现在,您想要的函数(按字母顺序插入节点)如下所示:

Person *add(Person **ptr, const char *name, int age)
{
    while (*ptr && strcmp((*ptr)->name, name) < 0) {
        ptr = &(*ptr)->next;
    }

    if (*ptr == NULL || strcmp((*ptr)->name, name) != 0) {
        Person *p = person_new(name, age);

        p->next = *ptr;
        *ptr = p;

        return p;
    }

    return NULL;
}

When you look up a node by name, you can stop the search short when the current node is alphabetically larger than the name you're looking for: 当您按名称查找节点时,如果当前节点按字母顺序大于要查找的名称,则可以短时间停止搜索:

const Person *find(const Person *ptr, const char *name)
{
    while (ptr) {
        int cmp = strcmp(ptr->name, name);

        if (cmp > 0) break;
        if (cmp == 0) return ptr;

        ptr = ptr->next; 
    }

    return NULL;
}

You can see the code in action here . 您可以在此处查看运行中的代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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