簡體   English   中英

如何在 C(使用我正在使用的 DS)中為此 MergeSort 實現正確分配 memory?

[英]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]))。 我還需要做些什么才能將項目結構復制到新結構的詞表中嗎?

再一次,我是新手,所以讓我松懈一下:)

代碼中有很多錯誤:

  1. merge()中,當將元素復制到R列表時,使用了錯誤的(且未初始化的)索引變量k而不是j R->wordlist[j]= D->wordlist[middle + 1 + k]; 應該是R->wordlist[j]= D->wordlist[middle + 1 + j]; .

  2. 在將LR列表合並回D之前的merge()中, D列表的索引變量k被初始化為錯誤值。 k = leftLen; 應該是k = start; .

  3. 在應該將“右”列表的剩余元素復制到D的循環中的merge()中,元素是從“左”列表而不是“右”列表復制的。 D->wordlist[k] = L->wordlist[j]; 應該是D->wordlist[k] = R->wordlist[j]; .

  4. 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); )。

  5. Item()沒有將字符串復制到分配的 memory。添加strcpy(W->item, str); .

  6. merge()中有 memory 處泄漏:

    1. L->wordlist[i] = malloc(sizeof(item*)); 不需要並且可以刪除,因為L->wordlist[i]在下一行被更改: L->wordlist[i] = D->wordlist[start + i]; .

    2. 同樣, R->wordlist[j] = malloc(sizeof(item*)); 不需要並且可以刪除,因為R->wordlist[j]在下一行發生了變化。

    3. LR memory 被創建但從未被銷毀。 將這些行添加到merge()的末尾以釋放它們:

       free(L->wordlist); free(L); free(R->wordlist); free(R);
  7. 沒有檢查任何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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM