2017-01-30 80 views
0

我必須在鏈接列表上創建一個快速排序(在C中)。 我有我的第一個和最後一個指針的樞軸(在這個代碼中它是列表的第一個元素)。 我該結構的使用方法:C快速排序(鏈接列表)分段錯誤

typedef struct list_element list_element; 

struct list_element { 
    char *password; 
    int count; 
    list_element* next; 
}; 

typedef struct list list; 

struct list { 
    list_element* first; 
    list_element* last; 
}; 

我有100個密碼和計數的文件。 像這樣: 密碼1 123(下一行)密碼2 435(下一行)password3 133 ... 口令必須被(根據其計數)在此PROGRAMM的端排序。 因爲我只需要使用下一個指針,所以不需要爲左右列表添加任何額外的內存分配。 (這就是在鍛鍊的提示說。)

給定的主要功能:

int main(int argc, char** args) 
{ 
    if (argc != 2) 
    { 
     printf("Nutzung: %s <Dateiname>\n",args[0]); 
     return 1; 
    } 
    list mylist; 
    init_list(&mylist); 
    read_data(args[1],&mylist); 
    qsort_list(&mylist); 
    printf("Sortierte Liste:\n"); 
    print_list(&mylist); 
    free_list(&mylist); 
    return 0; 
} 

我已經初始化我的名單:

void init_list(list* mylist) 
{ 
    mylist->first = NULL; 
    mylist->last = NULL; 
} 

而在末尾插入一個新元素(passwort =文件的密碼,hauefigkeit =文件數):從文件

void insert_list(list_element* le, list* mylist) 
{ 
    if (mylist->first != NULL) { 
     le->next = mylist->last; 
     mylist->last = le; 
     le->next= NULL; 
    } 
    else { 
     mylist->last->next = le; 
     mylist->last = le; 
     mylist->last->next = NULL; 
    } 
} 

讀取數據:

void read_data(char* filename, list* mylist) 
{ 
    FILE *file_in = fopen(filename, "r"); 

    if (file_in == NULL) { 
     perror("Could not open input file!"); 
     exit(1); 
    } 

    char buffer[999] = "0"; 

    char *passwort = (char*) calloc(1,sizeof(passwort)); 
    int haeufigkeit = 0; 

    while (fgets(buffer, sizeof(buffer), file_in) != NULL) { 
     sscanf(buffer, "%s %d", passwort, &haeufigkeit); 

     list_element* le = (list_element*)calloc(1,sizeof(list_element)); 

     for(int i = 0; i <=100; i++) { 
      le->password[i] = passwort[i]; 
     } 
     le->count = haeufigkeit; 
     le->next = NULL; 

     insert_list(le, mylist); 
    } 
    fclose(file_in); 
} 

分區列表:

list_element* partition(list* input, list* left, list* right) 
{ 
    list_element* pivot = NULL; 
    if (input->first != NULL) { 
     list_element* temp; 
     pivot = input->first; 
     input->first = input->first->next; 
     pivot->next = NULL; 
     left->first = NULL; 
     right->first = NULL; 

     while (input->first != NULL) { 
      if((pivot->count)>(input->first->count)){ 
       temp=input->first->next; 
       insert_list(input->first, left); 
       input->first=temp; 
      } 
      else { 
       temp = input->first->next; 
       insert_list(input->first, right); 
       input->first = temp; 
      } 
     } 
    } 
    return pivot; 
} 

實際的快速排序:

void qsort_list(list* mylist) 
{ 
    if(mylist->first == mylist->last){ 
    } 

    else{ 
     list* left = calloc(1,sizeof(list)); 
     list* right= calloc(1,sizeof(list)); 
     list_element* pivot = partition(mylist, left, right); 

     qsort_list(left); 
     qsort_list(right); 

     if(left->first == NULL){ 
      mylist->first = pivot; 
     } 
     else{ 
      mylist->first = left->first; 
      left->last->next = pivot; 
     } 

     if(right->first == NULL){ 
      pivot->next = NULL; 
      mylist->last = pivot; 
     } 
     else{ 
      pivot->next = right->first; 
      mylist->last = right->last; 
     } 

     free(right); 
     free(left); 

    } 
} 

最終打印列表:

void print_list(list* mylist) 
{ 
    list_element *elem = mylist->first; 
    while (elem != NULL) { 
     printf("%s %d\n", elem->password, elem->count); 
     elem = elem->next; 
    }  
} 

而且空閒列表:

void free_list(list* mylist) 
{ 
    list_element *current; 
    list_element *second; 
    current = mylist->first; 
    while (current != NULL) { 
     second = current->next; 
     free(current); 
     current = second; 
    } 
} 

語法應該沒問題。 GCC(c99,Wall)編譯時沒有任何問題。

但存在分段錯誤。我一直在尋找幾個小時,我不知道問題出在哪裏。也許你可以幫我解決這個問題。


在前兩個答案後沒有任何分段錯誤。但是read_data函數仍然有問題。 程序無法正確讀取密碼。也許我誤解了你有關閱讀功能的答案。 這就是當前的函數:

void read_data(char* filename, list* mylist) 
{ 
    FILE *file_in = fopen(filename, "r"); 

    if (file_in == NULL) { 
     perror("Could not open input file!"); 
     exit(1); 
    } 

    char buffer[999] = "0"; 

    int haeufigkeit = 0; 

    while (fgets(buffer, sizeof(buffer), file_in) != NULL) { 

     char passwort[100]; 

     sscanf(buffer, "%s %d", passwort, &haeufigkeit); 

     list_element* le = (list_element*)  

    calloc(1,sizeof(list_element)); 


     le->password = passwort; 
     le->count = haeufigkeit; 
     le->next = NULL; 

     insert_list(le, mylist); 
    } 
    fclose(file_in); 
} 
+1

請問您可以調試它,並告訴我們在哪一行seg。故障在發生? –

回答

1

作爲萊昂納多Alves的馬查多指出的那樣,當具有與C/C++程序麻煩的是,第一反射到與像gdb一個調試器中運行。這是最基礎的:

gcc -g main.c -o main 
gdb main 
(gdb) run 

注意-g編譯標誌:這將增加調試信息的可執行文件。


read_data,線條

for(int i = 0; i <=100; i++) { 
    le->password[i] = passwort[i]; 
} 

真的來煩我。你爲passwort(你永遠不會自由)分配空間並嘗試將它複製到le->password,這是一個簡單的指針(沒有分配空間)。你真正需要的是使le->passwordpasswort,即

le->password = passwort; 

free_list,不要忘了取消分配list_element空間之前解除分配passwort空間:

while (current != NULL) { 
    second = current->next; 
    free(current->password); 
    free(current); 
    current = second; 
} 
+0

好的。在此尋求解決方案之前,謝謝並抱歉不使用調試器。沒有剩下的分段錯誤。但是我仍然對read_data funktion有問題。該程序無法正確讀取密碼。我將編輯我的問題,並將新的data_read函數放在那裏。也許你可以幫我解決它。 – karthey

1

之一你的程序遇到的第一個問題是read_data()沒有爲passwort分配足夠的空間。目前還不清楚,爲什麼你動態地分配這個,但是鑑於你這樣做了,sizeof(passwort)是指向char的一個指針的大小(因爲這就是passwort) - 可能是4或8個字節。後來,你似乎認爲當你嘗試將其內容複製到列表元素時,分配的空間長度爲100字節。爲什麼不簡單地將其聲明爲100個字符數組?

char passwort[100]; 

事實上,如果你還聲明list_element.passwort以同樣的方式,然後在循環中您的密碼複製代碼將是正確的,雖然有點不地道。

就像它那樣,該代碼有問題,正如@Derlin所觀察到的。然而,他提出的解決方案是錯誤的。你不能讓列表元素指向本地passwort,只要整個例程只分配一次即可。然後所有列表元素將具有相同的密碼字符串,這不是你想要的。如果你希望你的列表元素包含指向密碼,因爲他們現在這樣做,那麼你將要移動的passwort申報和分配內部循環,使您獲得分配給每個列表元素單獨的口令空間。然後,建議分配le->password = passwort將是正確的。


另一個早期的問題是,你的insert_list()功能受到嚴重破壞。

首先考慮當你試圖將一個元素插入到一個空表由init_list()作爲初始化會發生什麼。該列表的nextlast成員都將是無效的,因此insert_list()將嘗試執行此代碼:

mylist->last->next = le; 
    mylist->last = le; 
    mylist->last->next = NULL; 

觀察到mylist->last爲空,因此第一線通過嘗試取消引用空指針調用未定義的行爲。分段錯誤是一個非常合理的觀察結果。您可以通過將這些行中的第一行更改爲

mylist->next = le; 

現在考慮您嘗試插入非空列表時會發生什麼。在這種情況下,你執行這些行:

le->next = mylist->last; 
    mylist->last = le; 
    le->next= NULL; 

因爲你的意圖是在結尾處的新元素(即追加吧),奇怪的是你的新元素的next指針設置爲列表的最後一個元素。特別奇怪的是,您稍後用NULL覆蓋該值。你似乎有它落後:要設置初始last元素指向新的元素作爲其下一個元素,而不是其他的方式:

mylist->last->next = le; 

事實上,這恰恰錯了的代碼空列表大小寫,但列表非空時很好。

總的來說,你的函數也會遭遇奇怪的缺乏並行性和一些隱藏代碼的重複。我可能會寫更多這樣的總體功能:

void append_to_list(list_element* le, list* mylist) 
{ 
    le->next= NULL; 
    if (mylist->first != NULL) { 
     mylist->last->next = le; 
     mylist->last = le; 
    } 
    else { 
     mylist->first = le; 
     mylist->last = le; 
    } 
} 
+0

好的。謝謝。對於在此尋求解決方案之前未使用調​​試器感到抱歉。沒有分段錯誤,但我仍然在閱讀密碼時遇到問題。 – karthey