【问题标题】:Sorting a doubly linked list in C在C中对双向链表进行排序
【发布时间】:2016-06-19 13:18:12
【问题描述】:

我正在尝试创建一个读取文本文件的程序 来自标准输入并打印按频率递减排序的单词及其频率。为此,我将单词存储在链表中,如果链表已经包含一个单词,则更新其频率。但是,我无法按单词的频率对链表进行排序。

我正在使用的struct 如下所示:

struct list {
    char *word;
    struct list *previous;
    struct list *next;
    int count;
};

所以我知道我应该将每个节点的count 值与其邻居的值进行比较,然后根据count 值切换它们的位置,但是我不知道如何保持函数循环直到它已排序。

我的代码是什么样子的:

struct list {
    char *word;
    struct list *previous;
    struct list *next;
    int count;
};

void list_add(struct list *l, char *word) {
    struct list *current = l->previous;
    struct list *prev;
    int already_in_list = 0;
    while (current != NULL) {
        if (strcmp(current->word, word) == 0) {
            current->count++;
            already_in_list = 1;
            // Compare new frequency with elements higher 
            // up in the list and sort*/ 
            **How do I do this?**
        }
        prev = current;
        current = current->next;
    }
    if (already_in_list != 1) list_add_new(l, word);
}

当前输出为:

word: bye   count: 1
word: is    count: 1
word: my    count: 1
word: hello count: 6
word: world count: 2
word: name  count: 1

我想要的输出:

word: hello count: 6
word: world count: 2
word: name  count: 1
word: bye   count: 1
word: is    count: 1
word: my    count: 1

【问题讨论】:

  • 根据您的预期输出:是否应该有任何相同出现的单词的顺序?现在好像没有……
  • 不,为什么会有?
  • 我在问是否应该以某种方式对同等出现的词进行排序,如果是,您将如何排序;按字母顺序、未修改、随机放置等。
  • 我并不关心频率相同的单词是如何排序的。
  • 对链表进行排序的最佳方法:1) 创建指向链表元素的指针数组,2) 使用 qsort 库函数对指针进行排序,3) 重新创建链表从已排序的指针数组中列出。

标签: c sorting linked-list


【解决方案1】:

这可以通过将您的列表分配到 buckets 中来解决。如果您有一个足够大的数组来容纳最大的字数,那么您可以在(几乎)线性时间内重新排序列表:

创建一个list 指针数组并将它们全部初始化为NULL。将此称为“频率列表”。您所做的就是取消链接主列表中的每个节点并将其放入适当的频率列表(按字数索引)。如果反向遍历主列表,并在频率列表的最前面插入,则不需要额外的遍历,并保持自然的词序。

一旦您的主列表为空,遍历频率列表数组(反向)并将它们全部连接起来。现在您已经仅按字数对列表进行了排序。

唯一的问题是它不是相当线性时间,因为频率计数的分布可能非常广泛。您可以使用其他一些结构(例如散列)来更有效地查找频率表。对于大多数实际用途,它仍然大部分是线性的,并且肯定比对数更好。

警告: 我不知道为什么我对线性时间如此大惊小怪,因为您的代码本质上是 O(N^2) 因为将每个单词添加到您的主列表涉及搜索整个列表以查看它是否已经在其中。

【讨论】:

    【解决方案2】:

    如果您始终保持列表排序,这个问题实际上比排序更简单:

    您需要遍历链表才能找到您要查找的单词是否已经存在。如果您找到它,请增加其计数,从列表中取消链接该节点并将其链接回正确计数所在的位置。 (注意在这种情况下你不需要遍历整个列表来找到正确的插入位置 - 你已经知道节点需要在列表中向后移动)

    如果单词未知,创建一个新节点(确保为节点中的字符串指针分配内存!)并将其链接到列表的开头。

    请注意,代码不是完整的实现。只需一些 sn-ps 即可根据字数和字数搜索节点(前者用于确定我们是否已经拥有该字,后者用于向后移动节点时使用,因为您增加了字数)

    struct list * findNode (char * word){
        struct list * tmp = root;
    
        do {
            if (strcmp (word, tmp->word) == 0) {
              return tmp;
            }
            tmp = tmp->next;
        } while (tmp != NULL)
        return NULL;
    }
    
    /* Finds the first node with a greater word count after start */
    struct list * findCount (struct list * start, int count) {
        struct list * tmp = start;
    
        do {
            if (tmp->count > count) {
              /* return the last node that was smaller than count */
              return tmp->previous;
            }
    
            if (tmp->next = NULL)
                return tmp;
    
            tmp = tmp->next;
        } while (tmp != NULL)
        return NULL;
    }
    
    void moveAfter (struct list * from, struct list * to) {
        if (from == to)
            return;
    
        /* Unlink from where we are */
        if (from->previous != NULL) 
           from->previous->next = from->next;
        else
           root = from->next;
        /* from->next can't be NULL, otherwise we wouldn't move */
        from->next->previous = from->previous;
    
        /* link into new place */
        if (to->next != NULL)
           to->next->previous = from;
        from->next = to->next;
        to->next = from;
        from->previous = to;
    
    }
    

    【讨论】:

      【解决方案3】:

      您可以修改插入代码以保持列表按频率递减排序。

      我修改了您的代码,使列表存根next 指针指向列表的开头,而其previous 成员指向列表的最后一个元素。第一个列表元素的previous 指针指向列表存根,最后一个元素的next 指针是NULL

      这里是修改后的代码:

      #include <stdio.h>
      #include <assert.h>
      #include <stdlib.h>
      #include <string.h>
      
      //#include "analyser_ll.h"
      
      struct list {
          char *word;
          struct list *previous;
          struct list *next;
          int count;
      };
      
      typedef struct list list;
      
      struct list *list_init(void);
      void list_print(struct list *l);
      void list_add(struct list *list, const char *word);
      int list_cleanup(struct list *l);
      
      list *list_init(void) {
          list *l = malloc(sizeof(struct list));
          l->word = NULL;
          l->previous = NULL;
          l->next = NULL;
          l->count = 1;
          return l;
      }
      
      /* list ordering function:
       - NULL word is less to keep list stub at the beginning
       - higher count is smaller
       - lexicographic order of word for same count
       */
      int list_compare(list *a, list *b) {
          if (a->word == NULL) return -1;
          if (b->word == NULL) return 1;
          if (a->count > b->count) return -1;
          if (a->count < b->count) return 1;
          return strcmp(a->word, b->word);
      }
      
      void list_add(list *l, const char *word) {
          list *current = l->next;
          while (current != NULL) {
              if (strcmp(current->word, word) == 0) {
                  current->count++;
                  break;
              }
              current = current->next;
          }
          if (current == NULL) {
              /* insert new node at end */
              current = list_init();
              current->word = malloc(strlen(word) + 1);
              strcpy(current->word, word);
      
              if (l->next == NULL) {
                  l->next = current;
                  current->previous = l;
              } else {
                  current->previous = l->previous;
                  l->previous->next = current;
              }
              l->previous = current;
          }
          /* move current node to the proper position */
          while (list_compare(current->previous, current) > 0) {
              /* swap current and previous nodes */
              list *prev = current->previous;
              list *next = current->next;
              current->previous = prev->previous;
              current->next = prev;
              current->previous->next = current;
              prev->previous = current;
              prev->next = next;
              if (next) {
                  next->previous = prev;
              } else {
                  l->previous = prev;
              }
          }
      }
      
      void list_print(list *l) {
          list *current = l->next;
      
          // List is empty.
          if (current == NULL) printf("List is empty!\n");
      
          // While the linked list isn't empty, print
          // the word in every node.
          while (current != NULL && current->word != NULL && current->count > 0) {
              printf("word: %s\tcount: %d\n", current->word, current->count);
              current = current->next;
          } 
      }
      
      int list_cleanup(struct list *l) {
          list *current = l->next;
      
          // Free all list nodes and their contents
          while (current != NULL) {
              struct list *next = current->next;
              free(current->word);
              free(current);
              current = next;
          }
      
          // Free head of linked list and return.
          free(l);
          return 1;
      }
      
      int main(void) {
          list *test = list_init();
          list_add(test, "name");
          list_add(test, "world");
          list_add(test, "hello");
          list_add(test, "world");
          list_add(test, "my");
          list_add(test, "hello");
          list_add(test, "is");
          list_add(test, "bye");
          list_add(test, "hello");
          list_add(test, "hello");
          list_add(test, "hello");
          list_add(test, "hello");    
          list_print(test);
          list_cleanup(test);
      }
      

      【讨论】:

      • 运行此代码时,输​​出变为: word: bye count: 1. 更改我的打印功能使其正常工作,它似乎也无法正确更新频率。跨度>
      • @Mosbas:列表处理函数存在不一致:列表存根的next 成员应指向第一个列表节点,previous 成员应指向最后一个元素。我用修改后的代码更新了答案。
      猜你喜欢
      • 2012-03-07
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      相关资源
      最近更新 更多