【问题标题】:Sorting array from typedef struct in C从C中的typedef结构排序数组
【发布时间】:2011-12-16 05:59:55
【问题描述】:

问题:尝试对来自我创建的 typedef 结构(电话簿)的数组进行排序。

目标:尝试构建一个允许用户添加、删除、排序和打印电话簿的电话簿。

我现在在哪里:除了排序之外,我已经完成了所有工作。我通过阅读各种网络论坛/示例拼凑出一个排序功能,但无法使其正常工作。

我遇到的问题:添加条目后(工作正常),如果您尝试对条目进行排序,该函数会将这些条目的值清零,当您打印电话簿时,它会显示所有条目为空白。它应该按姓氏的字母顺序对它们进行排序。

这是我使用的排序算法:

void Sort (phone phonebook[])
{
    phone temp;
    int i;  int j;

    for (i=0; i<19; i++)
    {
        for (j=i+1; j<19; j++)
        {
            if (strcmp(phonebook[i].Surname, phonebook[j].Surname) > 0)
            {
                temp=phonebook[i];
                phonebook[i]=phonebook[j];
                phonebook[j]=temp;

            }
        }
    }
}

有什么想法吗?


完整代码在这里:

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

//typedef struct to define what's in the phonebook
typedef struct PhoneBookContacts
{
    char Name[20];
    char Surname[20];
    char PhoneNumber[20];
} phone;

//Function prototypes
void AddEntry (phone[]);
void DeleteEntry (phone[]);
void PrintEntry (phone[]);
void Sort (phone[]);
int counter = 0; //Global counter variable used to keep track of number of contacts

//Begin main function
int main (void)
{
    phone phonebook[20]; //Phonebook instance
    char userChoice; //Variable to use to select menu choice

    while (userChoice != 'Q') {
        printf ("***************\n");
        printf ("Please enter a command:\n");
        printf("'A': Add an entry\n");
        printf("'D': Delete an entry\n");
        printf("'S': Sort entries\n");
        printf("'P': Print the phonebook\n");
        printf("'Q': Quit\n");
        printf ("***************\n");

        scanf("%s", &userChoice);  //Stores menu choice into variable userChoice

        // Add Contact
        if (userChoice == 'A')
            AddEntry(phonebook);

        //Remove Contact
        if (userChoice == 'D')
            DeleteEntry (phonebook);

        //Print Contacts
        if (userChoice == 'P')
            PrintEntry(phonebook);

        //Sort Contacts
        if (userChoice == 'S')
            Sort(phonebook);

        //Quit
        if (userChoice == 'Q') {
            printf("Phonebook will now quit.");
            return 0;
        }
    }
}

//Function Definition to Add Contacts to the Phonebook
void AddEntry (phone phonebook[]) {
    counter++; //global counter increase

    printf("\nFirst Name: ");
    scanf("%s", phonebook[counter-1].Name); //counter-1 b/c arrays start at 0

    printf("Last Name: ");
    scanf("%s", phonebook[counter-1].Surname);

    printf("Phone Number (XXX-XXX-XXXX): ");
    scanf("%s", phonebook[counter-1].PhoneNumber);

    printf("\n%s added to phonebook\n", phonebook[counter-1].Name); //tell user friend added
}

void DeleteEntry (phone phonebook[])
{
    int x = 0;
    char deleteName[20];  // Temp string to compare to existing phonebook
    char deleteSurname[20];  //temp string
    char nullStr[20] = {"\0"};  // empty string to remove phonenumber

    printf("\nEnter name: ");
    scanf("%s", deleteName); //place into temp string
    printf("Enter Surname: ");
    scanf("%s", deleteSurname); //place into temp string

    for (x = 0; x < counter; x++)
    {
        if (strcmp(deleteName, phonebook[x].Name) == 0) //compare deleteName to phonebook.Name
        {
            for (x = 0; x < counter; x++)
            {
                if (strcmp(deleteSurname, phonebook[x].Surname) == 0) //If deleteSurname matches phonebook.Surname
                {
                    strcpy(phonebook[x].Name, nullStr); //Put null into Name
                    strcpy(phonebook[x].Surname, nullStr); //Null into Surname
                    strcpy(phonebook[x].PhoneNumber, nullStr); //Null into PhoneNumber
                    printf("Contact removed from phonebook.\n");
                    counter--;
                    break;
                }
            }

        }
        else printf("Invalid entry--try again.\n");
    }
}

// Function def to print contacts
void PrintEntry (phone phonebook[]) {
    int x = 0;
    printf("\nPhonebook entries:\n");

    for ( x = 0; x < counter; x++) {
        printf("\n(%d)\n", x+1); //Show contact number
        printf("Name: %s %s\n", phonebook[x].Name, phonebook[x].Surname); //Name
        printf("Number: %s\n", phonebook[x].PhoneNumber); //Number
    }
}

void Sort (phone phonebook[]) {
    phone temp;
    int i;  int j;

    for (i=0; i<19; i++) {
        for (j=i+1; j<19; j++) {
            if (strcmp(phonebook[i].Surname, phonebook[j].Surname) > 0) {
                temp=phonebook[i];
                phonebook[i]=phonebook[j];
                phonebook[j]=temp;
            }
        }
    }
}

【问题讨论】:

  • +1 提出了一个组织良好的问题。 :)
  • 这并不能解决您的问题,但您应该真正考虑使用radix sort 而不是冒泡排序(除非您有内存分配问题)。它快了很多 - 平均案例性能 - Radix O(n) vs. BubbleSort O(n^2)。
  • @Michael 他正在按字符串字段排序;基数排序仅适用于对整数进行排序。
  • “位置符号是必需的,但因为整数可以表示字符串(例如,姓名或日期)和特殊格式的浮点数,所以基数排序不限于整数。” - Wikipedia
  • .. 或使用快速排序,它是 O(n log n),已被证明可以进行任何排序,并且它与 C 库一起提供,因此您不必编写它

标签: c arrays sorting struct typedef


【解决方案1】:

您可以使用stdlib.h提供的已经实现的排序功能qsort

int SortFunc(void* a, void* b) {
    phone *p1 = (phone*)a;
    phone *p2 = (phone*)b;

    return strcmp(p1->Surname, p2->Surname);
}

void Sort (phone phonebook[]) {
    qsort(phonebook, counter, sizeof(phone), &SortFunc);
} 

函数通常是Quicksort,但这取决于 C 库实现来决定。

更新:

空白列表是因为排序是相反的,总是对电话簿的所有 19 项进行排序,将空的与真实的进行比较。如果电话簿上的条目少于 19 个,则实际数据将出现在电话簿数组的 end 中。

您原来的排序功能一直在工作几乎正常。只需更改两个 for 的结束条件即可。

void Sort (phone phonebook[]) {
    phone temp;
    int i;  int j;

    for (i=0; i<counter; i++) {
        for (j=i+1; j<counter; j++) {
            if (strcmp(phonebook[i].Surname, phonebook[j].Surname) > 0) {
                temp=phonebook[i];
                phonebook[i]=phonebook[j];
                phonebook[j]=temp;
            }
        }
    }
}

我还更新了上面的Sort

【讨论】:

  • 激活迂腐模式:qsort 不需要快速排序。
  • vz0,我实现了你的排序,现在值不再是空白,而是根本不会排序——即当我打印它们时,它们的顺序与以前相同。我让我的冒泡排序算法在某一时刻做同样的事情(在我认为它应该工作的地方有它,但元素仍然没有按预期打印)。我可能在其他地方有错误?唔。无论如何,谢谢您的回答和其他贡献者。
  • @JoshT 我的第一个答案有错字。检查 SortFunc 上 srtrcmp() 调用的参数。
  • 已更正 pone 到电话,我正在编译,但现在条目是空白的。回到方块 1。 :-|
  • @JoshT 不错,你原来的排序没问题!我又更新了。希望这是真的:)
【解决方案2】:

首先,这里有一个缓冲区溢出问题:

char userChoice;
:
scanf("%s", &userChoice);

scanf 将在您输入一个字符(该字符加上一个空终止符)时写入 两个 个字节。这破坏了我环境中第一个电话簿条目的名字,但是,由于它是未定义的行为,它可以做任何事情!

您可以通过以下方式解决此问题:

char userChoice[] = "something that's not Q";
: 
scanf("%s", userChoice);
:
if (*userChoice == 'A')  // for all of these.

如果您输入足够多的文本,这不会阻止缓冲区溢出,但如果您将自己限制为单字符命令,它就会停止。如果您想要一个真正强大的用户输入功能,请参阅here


现在谈谈您的具体问题。看起来您在那里进行了一些冒泡排序,但您的逻辑略有偏差。假设您不想使用 qsort(这将是实际代码的更好方法),您只需要修复几件事即可。

您的外循环和内循环都可以,但内循环主体应该比较元素jj+1,而不是ji。这是因为它通过交换 相邻 元素(如果它们乱序)来工作。

此外,向前聚焦的冒泡排序会将 highest 元素放置在第一次遍历列表的 end 处,因此您无法开始 j在第二遍时i+1,仅仅是因为 first 元素可能还不正确。

以下伪代码是您的经典冒泡排序:

didSwap = true
while didSwap:
    didSwap = false
    for i = 0 to lastidx - 1:
        if array[i] > array[i+1]:
            temp = array[i]
            array[i] = array[i+1]
            array[i+1] = temp
            didSwap = true

阅读并了解其工作原理,然后自行实施。如果您对此有疑问,我在下面提供了一个工作版本:

void Sort (phone phonebook[]) {
    phone temp;
    int i;  int didSwap;

    didSwap = 1;
    while (didSwap) {
        didSwap = 0;
        for (i = 0; i < counter - 1; i++) {
            if (strcmp(phonebook[i].Surname, phonebook[i+1].Surname) > 0) {
                temp=phonebook[i];
                phonebook[i]=phonebook[i+1];
                phonebook[i+1]=temp;
                didSwap = 1;
            }
        }
    }
}

【讨论】:

    【解决方案3】:
    for (i=0; i<19; i++)
    { 
        for (j=i+1; j<19; j++)
    
        {
            if (strcmp(phonebook[i].Surname, phonebook[j].Surname) > 0)
            {
                temp=phonebook[i];
                phonebook[i]=phonebook[j];
                phonebook[j]=temp;
    
            }
    
        }
    }
    

    您的代码存在三个问题。首先是算法的逻辑。冒泡排序通过固定两个相邻元素的顺序来工作。在您的代码中,在您的内部 for 循环的第一次迭代之后,它不会比较两个相邻的元素。

    第二个问题,同样在排序算法中,您的计数器 ij 都将变为 19,即使条目数少于该数。这可能会弄乱排序,因为它们将读取无效(未初始化)条目。你应该检查计数器的上限。

    下一个正在删除中

        if (strcmp(deleteName, phonebook[x].Name) == 0) //compare deleteName to phonebook.Name 
        {
            for (x = 0; x < counter; x++)
            {
                if (strcmp(deleteSurname, phonebook[x].Surname) == 0) //If deleteSurname matches phonebook.Surname
                {
                    strcpy(phonebook[x].Name, nullStr); //Put null into Name
                    strcpy(phonebook[x].Surname, nullStr); //Null into Surname
                    strcpy(phonebook[x].PhoneNumber, nullStr); //Null into PhoneNumber
                    printf("Contact removed from phonebook.\n");
                    counter--;
                    break;
                }
    
            }
    
        }
    

    上面的代码将无法正确检查第一个 姓氏是否是您单独检查的。您只需要一个带有if( strcmp(deleteSurname, phonebook[x].Surname) == 0 &amp;&amp; strcmp(deleteName, phonebook[x].Name) == 0 )for 循环

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多