[英]How can I correctly allocate memory for this MergeSort implementation in C (with the DS I am using)?
我的目標是對動態數組類數據結構執行 MergeSort,我稱之為字典,用於存儲字符串及其相對權重。 抱歉,如果實施很愚蠢,我是一名學生,仍在學習中。
無論如何,根據我得到的段錯誤,我錯誤地分配了 memory 用於我的結構類型 item 被復制到我正在制作的臨時列表中。 不知道如何解決這個問題。 合並排序和數據結構設置的代碼如下,感謝您的幫助。
/////// DICTIONARY METHODS ////////
typedef struct {
char *item;
int weight;
} item;
typedef struct {
item **wordlist;
//track size of dictionary
int size;
} dict;
//dict constructor
dict* Dict(int count){
//allocate space for dictionary
dict* D = malloc(sizeof(dict));
//allocate space for words
D->wordlist = malloc(sizeof(item*) * count);
//initial size
D->size = 0;
return D;
}
//word constructor
item* Item(char str[]){
//allocate memory for struct
item* W = malloc(sizeof(item));
//allocate memory for string
W->item = malloc(sizeof(char) * strlen(str));
W->weight = 0;
return W;
}
void merge(dict* D, int start, int middle, int stop){
//create ints to track lengths of left and right of array
int leftlen = middle - start + 1;
int rightlen = stop - middle;
//create new temporary dicts to store the two sides of the array
dict* L = Dict(leftlen);
dict* R = Dict(rightlen);
int i, j, k;
//copy elements start through middle into left dict- this gives a segfault
for (int i = 0; i < leftlen; i++){
L->wordlist[i] = malloc(sizeof(item*));
L->wordlist[i] = D->wordlist[start + i];
}
//copy elements middle through end into right dict- this gives a segfault
for (int j = 0; j < rightlen; j++){
R->wordlist[j] = malloc(sizeof(item*));
R->wordlist[j]= D->wordlist[middle + 1 + k];
}
i = 0;
j = 0;
k = leftlen;
while ((i < leftlen) && (j < rightlen)){
if (strcmp(L->wordlist[i]->item, R->wordlist[j]->item) <= 0) {
D->wordlist[k] = L->wordlist[i];
i++;
k++;
}
else{
D->wordlist[k] = R->wordlist[j];
j++;
k++;
}
}
while (i < leftlen){
D->wordlist[k] = L->wordlist[i];
i++;
k++;
}
while (j < rightlen){
D->wordlist[k] = L->wordlist[j];
j++;
k++;
}
}
void mergeSort(dict* D, int start, int stop){
if (start < stop) {
int middle = start + (stop - start) / 2;
mergeSort(D, start, middle);
mergeSort(D, middle + 1, stop);
merge(D, start, middle, stop);
}
我將打印語句放在各處,並將其縮小到我復制字典的部分中的 mallocs,以便將其分類為 2 個單獨的字典。 還嘗試將 malloc 寫為 malloc(sizeof(D->wordlist[start + i]))。 我還需要做些什么才能將項目結構復制到新結構的詞表中嗎?
再一次,我是新手,所以讓我松懈一下:)
代碼中有很多錯誤:
在merge()
中,當將元素復制到R
列表時,使用了錯誤的(且未初始化的)索引變量k
而不是j
。 R->wordlist[j]= D->wordlist[middle + 1 + k];
應該是R->wordlist[j]= D->wordlist[middle + 1 + j];
.
在將L
和R
列表合並回D
之前的merge()
中, D
列表的索引變量k
被初始化為錯誤值。 k = leftLen;
應該是k = start;
.
在應該將“右”列表的剩余元素復制到D
的循環中的merge()
中,元素是從“左”列表而不是“右”列表復制的。 D->wordlist[k] = L->wordlist[j];
應該是D->wordlist[k] = R->wordlist[j];
.
在Item()
中, malloc()
調用沒有為字符串末尾的 null 終止符保留空間。 W->item = malloc(sizeof(char) * strlen(str));
應該是W->item = malloc(sizeof(char) * (strlen(str) + 1));
(並且由於sizeof(char)
根據定義為 1,因此可以簡化為W->item = malloc(strlen(str) + 1);
)。
Item()
沒有將字符串復制到分配的 memory。添加strcpy(W->item, str);
.
merge()
中有 memory 處泄漏:
L->wordlist[i] = malloc(sizeof(item*));
不需要並且可以刪除,因為L->wordlist[i]
在下一行被更改: L->wordlist[i] = D->wordlist[start + i];
.
同樣, R->wordlist[j] = malloc(sizeof(item*));
不需要並且可以刪除,因為R->wordlist[j]
在下一行發生了變化。
L
和R
memory 被創建但從未被銷毀。 將這些行添加到merge()
的末尾以釋放它們:
free(L->wordlist); free(L); free(R->wordlist); free(R);
沒有檢查任何malloc()
調用是否成功。
在合並排序甚至開始之前一次性全部分配。
#include <stdlib.h>
#include <string.h>
// Weighted Word --------------------------------------------------------------
//
typedef struct {
char *word;
int weight;
} weighted_word;
// Create a weighted word
//
weighted_word* CreateWeightedWord(const char *str, int weight){
weighted_word* W = malloc(sizeof(weighted_word));
if (W){
W->word = malloc(strlen(str) + 1); // string length + nul terminator
if (W->word)
strcpy( W->word, str);
W->weight = weight;
}
return W;
}
// Free a weighted word
//
weighted_word *FreeWeightedWord(weighted_word *W){
if (W){
if (W->word)
free(W->word);
free(W);
}
return NULL;
}
// Dictionary (of Weighted Words) ---------------------------------------------
//
typedef struct {
weighted_word **wordlist; // this is a pointer to an array of (weighted_word *)s
int size; // current number of elements in use
int capacity; // maximum number of elements available to use
} dict;
// Create a dictionary with a fixed capacity
//
dict* CreateDict(int capacity){
dict* D = malloc(sizeof(dict));
if (D){
D->wordlist = malloc(sizeof(weighted_word*) * capacity);
D->size = 0;
D->capacity = capacity;
}
return D;
}
// Free a dictionary (and all weighted words)
//
dict *FreeDict(dict *D){
if (D){
for (int n = 0; n < D->size; n++)
FreeWeightedWord(D->wordlist[n]);
free(D->wordlist);
free(D);
}
return NULL;
}
// Add a new weighted word to the end of our dictionary
//
void DictAddWord(dict *D, const char *str, int weight){
if (!D) return;
if (D->size == D->capacity) return;
D->wordlist[D->size] = CreateWeightedWord(str, weight);
if (D->wordlist[D->size])
D->size += 1;
}
// Merge Sort the Dictionary --------------------------------------------------
// Merge two partitions of sorted words
// words • the partitioned weighted word list
// start • beginning of left partition
// middle • end of left partition, beginning of right partition
// stop • end of right partition
// buffer • temporary work buffer, at least as big as (middle-start)
//
void MergeWeightedWords(weighted_word **words, int start, int middle, int stop, weighted_word **buffer){
int Lstart = start; int Rstart = middle; // Left partition
int Lstop = middle; int Rstop = stop; // Right partition
int Bindex = 0; // temporary work buffer output index
// while (left partition has elements) AND (right partition has elements)
while ((Lstart < Lstop) && (Rstart < Rstop)){
if (strcmp( words[Rstart]->word, words[Lstart]->word ) < 0)
buffer[Bindex++] = words[Rstart++];
else
buffer[Bindex++] = words[Lstart++];
}
// if (left partition has any remaining elements)
while (Lstart < Lstop)
buffer[Bindex++] = words[Lstart++];
// We don't actually need this. Think about it. Why not?
// // if (right partition has any remaining elements)
// while (Rstart < Rstop)
// buffer[Bindex++] = words[Rstart++];
// Copy merged data from temporary buffer back into source word list
for (int n = 0; n < Bindex; n++)
words[start++] = buffer[n];
}
// Merge Sort an array of weighted words
// words • the array of (weighted_word*)s to sort
// start • index of first element to sort
// stop • index ONE PAST the last element to sort
// buffer • the temporary merge buffer, at least as big as (stop-start+1)/2
//
void MergeSortWeightedWords(weighted_word **words, int start, int stop, weighted_word **buffer){
if (start < stop-1){ // -1 because a singleton array is by definition sorted
int middle = start + (stop - start) / 2;
MergeSortWeightedWords(words, start, middle, buffer);
MergeSortWeightedWords(words, middle, stop, buffer);
MergeWeightedWords(words, start, middle, stop, buffer);
}
}
// Merge Sort a Dictionary
//
void MergeSortDict(dict *D){
if (D){
// We only need to allocate a single temporary work buffer, just once, right here.
dict * Temp = CreateDict(D->size);
if (Temp){
MergeSortWeightedWords(D->wordlist, 0, D->size, Temp->wordlist);
}
FreeDict(Temp);
}
}
// Main program ---------------------------------------------------------------
#include <stdio.h>
int main(int argc, char **argv){
// Command-line arguments --> dictionary
dict *a_dict = CreateDict(argc-1);
for (int n = 1; n < argc; n++)
DictAddWord(a_dict, argv[n], 0);
// Sort the dictionary
MergeSortDict(a_dict);
// Print the weighted words
for (int n = 0; n < a_dict->size; n++)
printf( "%d %s\n", a_dict->wordlist[n]->weight, a_dict->wordlist[n]->word );
// Clean up
FreeDict(a_dict);
}
給你的注意事項:
始終如一。 您與大寫和*
位置以及奇怪的垂直間距不一致。 (不過,你比大多數初學者都好。)我個人討厭埃及牙套風格,但對每個人來說都是自己的。
我個人認為這段代碼中的malloc()
級別也太多了,但我會把它留在這一條評論中。 它按原樣工作。
字符串必須以 nul 結尾——也就是說,每個字符串都需要strlen()
個字符加上一個'\0'
字符。 有一個方便的庫 function 也可以為您復制一個字符串,稱為strdup()
,AFAIK 存在於每個系統上。
始終檢查malloc()
和朋友是否成功。
不要忘記釋放你分配的所有東西。 功能有幫助。
“Item”是一個非常沒有描述性的名稱,它與代碼中兩個不同事物的含義重疊。 我將它們重命名為分開的東西。
您的字典 object 應該會跟蹤它可以支持的元素數量。 上面的代碼只是拒絕在容量填滿后添加單詞,但是如果需要的話,你可以很容易地讓它realloc()
一個更大的容量。 重點是通過向固定大小的數組添加太多元素來防止無效的數組訪問。
在 function 中打印數組可能是 go。
請注意我是如何將start
設置為 inclusive 並將stop
設置為exclusive 的。 這是一種非常 C(和 C++)的看待事物的方式,而且是一種很好的方式。 它將幫助您處理各種算法。
另請注意我如何將合並排序拆分為兩個函數:一個將字典作為參數,一個較低級別的函數將加權單詞數組作為參數來完成所有工作。
weighted_word*
) 數組期望臨時緩沖區存在並且不關心(或不知道任何)字典 object。現在,合並條件只是比較加權詞的字符串值。 但它不必如此簡單。 例如,您可以按重量對相等的元素進行排序。 創建一個 function:
int CompareWeightedWords(const weighted_word *a, const weighted_word *b){
int rel = strcmp( a->word, b->word );
if (rel < 0) return -1;
if (rel > 0) return 1;
return a->weight < b->weight ? -1 : a->weight > b->weight;
}
並將其用於合並 function:
if (CompareWeightedWords( words[Rstart], words[Lstart] ) < 0)
buffer[Bindex++] = words[Rstart++];
else
buffer[Bindex++] = words[Lstart++];
我不認為我忘記了什么。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.