【问题标题】:Binary searching a dictionary of words二进制搜索词的字典
【发布时间】:2016-08-12 07:03:28
【问题描述】:

在这段代码中:

  • 我读取了文件~/usr/share/dict/word 的内容并将它们存储在数组中。
  • 然后开始对该数组进行二分搜索算法,但问题是在将数组传递给第 62 行的二分搜索函数并尝试将其与 binary_search(string* dictionary, string key) 方法中的键进行比较之后。
  • 我发现它出于某种我不知道的原因将key 与这个未知字符串"��tudes" 进行比较。
  • 我确信该数组包含正确的数据。

代码:

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

#define MAX 99171

// Prototype //
int binary_search(string*, string);

int main(int argc, string argv[])
{
    // Attributes // 
    string dictionary[MAX];
    FILE* dictionaryFile = fopen("words", "r");
    char output[256];
    string key = argv[1];

    // Check if their is a problem while reading the file //
    if (dictionaryFile == NULL)
    {
        // If everything got fouled up then close the file // 
        fclose(dictionaryFile);
        printf("couldn't read the file!!!\n");
        return 1;
    }

    // storing the information into an array to make it easy to read //
    for(int i = 0; i < MAX; i++)
    { 
        fgets(output, sizeof(output), dictionaryFile); 
        dictionary[i] = output;
    }

    // Binary Search a word //
    if(binary_search(dictionary, key) == 1)
    {
        printf("word was found !!!\n");
    }
    else if(binary_search == 0)
    {
        printf("word was not found !!!\n");
    }

    // If Everything goes just fine close the file //
    fclose(dictionaryFile);
    return 0;
}


// implementing prototype //

/**
    @arag dictionary 
        a string of english words 

    @arg key 
        a key we looking for

    @return 
        0 if didn't find the key and 1 otherwise
*/
int binary_search(string* dictionary, string key)
{
    // pointer to the start and the end of the array //
    int start = 0;
    int end = MAX - 1;
    int mid;

    // while end is greater than the start //
    while (end > start)
    {
        // Get The Middle Element //
        mid = (start + end) / 2;
        printf("%s\n", dictionary[mid]);

        // Check if the middle elemenet //
        if (strcmp(key, dictionary[mid]) == 0)
        {
            return 1;
        }

        // Check the left half //
        else if(strcmp(key, dictionary[mid]) < 0)
        {
            end = mid - 1;
        }

        // Check the right half //
        else if (strcmp(key, dictionary[mid]) > 0)
        {
            start = mid + 1;
        }
    }
    // didn't find the key //
    return 0;

}

注意:cs50.h 库是由哈佛制作的,作为像我这样的初学者的训练轮,我在我的代码中使用它,这是指向其 reference 的链接。

【问题讨论】:

  • 什么是“字符串”?这甚至可以编译吗?
  • 用于字典实现.. 最好使用 trie 数据结构。
  • @Sigstop:正如 OP 已经明确表示的那样,这是一个示例程序。数组的二进制搜索在这里很好。尝试将是矫枉过正。
  • dictionary 只是一个指针数组,并且该数组中的每个指针都指向完全相同的位置:output。您需要复制每个字符串。此外,您需要 remove the newlinefgets 留在缓冲区中。
  • @LeeDanielCrocker 正如我在注释中所说,我使用了一个 cs50 库,它具有这个字符串数据类型 AKA char *

标签: c arrays string binary-search cs50


【解决方案1】:

cs50.h 库由哈佛制作,作为初学者的训练轮。

如果是这样,这些辅助轮是倒置安装的,不会接触地面。我无法从您的链接中看出,但我认为

typedef char *string;

cs50 套件的一部分。但是 C 中没有字符串;该表达式用于表示以空字符 '\0' 结尾的字符数组。

string 的上述定义让你相信 string 是一个正确的类型,它的内存是自动处理的。它不是。在你的程序中有一个字符串的位置,即数组

char output[256];

字典中的“字符串”只是指针;它们应该指向现有的字符数组或NULL。通过分配

dictionary[i] = output;

您使字典中的所有字符串都等于临时缓冲区output。该缓冲区在您读取的每一行中都会被覆盖,并且将仅包含您已读取的最后一行,可能是"zulu"

您可以在阅读完字典后打印出字典来确认这一点。您应该在单独的循环中打印它,而不是在您阅读它以查看效果的同一循环中。

您可以通过将指针数组声明为 char 数组来解决此问题:

char dictionary[MAX][LEN];

其中LEN 是适合单词的最大长度,例如 24。(这里的问题可能是分配的内存,MAX * LEN 字节可能不适合堆栈。在这种情况下,您必须在带有malloc 的堆。我不会在这里打开那罐蠕虫。如果您立即遇到分段违规,请尝试减少MAX,代价是只读取字典的一部分。)

阅读文字时,一定要复制内容:

fgets(output, sizeof(output), dictionaryFile); 
strncpy(dictionary[i], output, sizeof(dictionary[i]);

或者,更好的是,直接将下一个单词读入字典:

fgets(dictionary[i], sizeof(dictionary[i]), dictionaryFile); 

不幸的是,fgets 在末尾保留了换行符,因此它读取为"word\n" 而不是"word"。您必须删除换行符,否则字符串将与输入不匹配,该输入来自通过argv 的命令行,它没有尾随的换行符。

有几种方法可以去掉不需要的换行符。一个简单的方法是用换行符作为分隔符来标记字符串:

strtok(dictionary[i], "\n");

另一个问题是dictionary 的新定义,binary_search 的签名是错误的。你不再有一个指向 char 的指针数组,你有一个 24 个(或者说是固定数量)字符的数组。将其更改为:

int binary_search(char dictionary[][LEN], const char *key)

在 C 中,如果你有数组的数组(数组中的偶数),除了最上面的维度之外的所有维度都必须是已知的,以便编译器可以布局内存。

还有其他(相当轻微的)问题:

  • 如果无法打开该文件,请尝试fclose。当文件为NULL 时,您没有打开的文件要关闭;退出。
  • 您应该强制要求至少有一个参数,否则您可能会循环一个空键,这会在您尝试比较它时导致未定义的行为(即很可能发生崩溃)。
  • 当您阅读单词时,不要依赖硬编码的字数。你不知道文件中有多少字。检查fgets的返回值;当文件用完时它返回NULLMAX 是估计字数的好方法,但您应该将实际读取的字数保留在变量中。确保您访问的字数不超过您已阅读的字数,并确保您的写入字数不会超出您分配的内存,即阅读的字数不要超过MAX 个字数。
  • 当您没有硬编码字数时,您应该将该计数作为binary_search 函数的参数。
  • 在“未找到”的 beanch 中,您的测试是 else if(binary_search == 0)。首先,else aleady 表示二分查找没有返回 1(这是 else 所指的条件),二分查找只能返回 0 和 1,因此不需要其他条件。二、binary_search只是函数的地址,不是结果;上面写的条件永远是正确的。
  • 二分查找函数中的strcmp 调用也是如此:您进行三个比较。您检查的结果是互斥的,因此最后一个条件可以是 else。 (因为strcmp 每次都会进行逐个字符的比较,因此每个单词只调用一次strcmp 并存储结果可能是值得的。)

cs50 标头中的 string 数据类型旨在提供一种简单的方法来读取字符串,而无需关心内存。一旦你开始创建更复杂的(也就是现实生活中的)数据结构,最好使用char 数组和指针。无论如何都没有办法解决这个问题,您可以查看每条数据是什么。

很抱歉,我的回答看起来像是一个错误清单。 C 的字符串处理对于初学者来说不是很容易,尤其是如果您已经有高级语言的经验的话。好消息是,当您了解 C 字符串时,您已经对 C 中的一般操作了解很多。

【讨论】:

  • 仅针对您的第一段,+1!是的:it's a convenience notation - 仅此而已。一定是因为“指针令人困惑”,但它的作者忘记了理解 C 中的指针是至关重要的。 (此外,OP 在他自己对string * 的进一步使用中似乎没有问题。)
猜你喜欢
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多