[英]Sorting a doubly linked list in C
我正在嘗試創建一個程序,該程序從標准輸入中讀取文本文件,並打印單詞及其頻率(按頻率遞減排序)。 為此,我將單詞存儲在鏈接列表中,如果鏈接列表中已經包含一個單詞,則會更新其頻率。 但是,我無法按單詞的頻率對鏈接列表進行排序。
我正在使用的struct
看起來像這樣:
struct list {
char *word;
struct list *previous;
struct list *next;
int count;
};
因此,我知道我應該將每個節點的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
這可以通過將列表分發到存儲桶中來解決。 如果您有足夠大的數組來容納最大字數,則可以在(幾乎)線性時間內對列表進行重新排序:
創建一個list
指針數組,並將它們全部初始化為NULL。 將此稱為“頻率列表”。 您要做的就是取消主列表中每個節點的鏈接,然后將其放入相應的頻率列表(按字數索引)。 如果您反向遍歷主列表,然后插入頻率列表的前面,則不需要額外的遍歷,並且可以保持自然的單詞順序。
一旦主列表為空,遍歷頻率列表數組(相反)並將它們全部串聯起來。 現在,您僅按字數對列表進行了排序。
唯一的問題是時間不是很線性,因為頻率計數的分布可能很寬。 您可以使用其他結構(例如哈希)來更有效地查找頻率表。 對於大多數實踐目的,它仍然大部分是線性的,並且絕對優於對數。
警告:我不知道為什么對線性時間這么大驚小怪,因為您的代碼本質上是O(N ^ 2),因為將每個單詞添加到您的主列表中涉及搜索整個列表以查看其是否已經存在。
如果您始終使列表保持排序,那么實際上這個問題比排序要簡單一些:
您需要遍歷鏈接列表以查找所需的單詞是否已經存在。 如果找到它,請增加其計數,從列表中取消鏈接該節點,然后將其鏈接回正確的計數位置。 (請注意,在這種情況下,您無需遍歷整個列表即可找到正確的插入位置-您已經知道節點需要在列表中向后移動)
如果單詞未知,請創建一個新節點(確保為該節點中的字符串指針分配內存!)並將其鏈接到列表的開頭。
請注意,該代碼不是完整的實現。 只需一些片段即可根據單詞和單詞數來搜索節點(需要前者來找出我們是否已有該單詞,而當您向后移動節點時會使用后者,因為您增加了單詞數)
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;
}
您可以修改插入代碼,以使列表按遞減頻率排序。
我修改了代碼,使列表樁的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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.