在链接列表中的另一个节点之前插入节点时遇到问题?

时间:2016-09-10 06:27:08

标签: c linked-list

当我插入链接列表时(我从文件中插入)尝试按字母顺序排列 - 我目前有三张支票(我将使用它来解释我的逻辑 - 如果有人有更好的想法,请让我知道,我整天都把头发拉过来了):

如果从文件中抓取的单词与当前节点中的单词匹配,只需增加节点中的频率计数器(不要打扰创建新节点)。如果该单词位于当前节点之前,则指向新创建的节点旁边的上一个>,并指向当前节点旁边的new_node->。如果该字出现在当前节点之后,则将新节点指向current_node-> gt;然后在new_node旁边设置current_node->。

我的问题是,当我尝试运行这个程序并使用一个文件,其中文件中的第二个单词出现在第一个单词之前,并尝试打印链表时,我被锁定在无限循环中 - 我&#39我已经把这个问题缩小到某个地方,一个节点指针没有得到更新的事实,但我不知道在哪里,我知道我的结局。

我会附上我拥有的两个文件,如果有人可以帮助我,我真的很感激。 (那里有一些调试线并不是真的有必要,我用它们来试图找出问题所在。)

most_freq.h

#ifndef MOST_FREQ_H_
#define MOST_FREQ_H_

#include <stdio.h>

//used to hold each "word" in the list
typedef struct word_node
{
char *word;
unsigned int freq; //frequency of word 
struct word_node *next;
} word_node;

struct node *readStringList(FILE *infile);

int readLine(FILE *infile, char * line_buffer);

struct node *getMostFrequent(struct word_node *head, unsigned int num_to_select);

void printStringList(struct word_node *head);

void freeStringList(struct word_node *head);

#endif

most_freq.c

#include "most_freq.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/* TODO

0. Check if item is in list (make all strings lowercase)
1a. if not, insert into list
1b. if it is, increment counter for struct */

struct word_node *head = NULL; //unchanging head node
char* str_buffer = NULL;

struct node *readStringList(FILE *infile) {
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    char* tmp_buffer = (char*) malloc(sizeof(char) * 255);
    while(readLine(infile, tmp_buffer) == 1) {
        if(head == NULL) { //if the linked list is empty
            //allocate space for node
            head = (word_node*) malloc (sizeof(word_node));

            //set as head node
            str_buffer[ strlen(str_buffer) - 1 ] = '\0';
            head->word = str_buffer; //set string of node to str_buffer
            head->freq = 1; //set frequency to 0
            head->next = NULL; //since we're at the first item of the list there is no next
        }
        else { //else, there is already a node in the list
            printf("Linked list has a node.\n\n");
            struct word_node *curr = head; //set curr to head (for traversal)
            struct word_node *prev = head; //to keep track of the last node
            while(curr != NULL) {   //while current is not null, continue traversal
                /* If string buffer matches current node's word, then simply update the frequency count */
                if(strcmp(str_buffer,curr->word) == 0) { //if word matches the word in the list
                    curr->freq++; //update the current node's frequency
                    break;
                }
                /* If string buffer comes after current word (in alphabet) then point temp node->next to current->next, and point current node->next to temp */
                else if(strcmp(str_buffer,curr->word) > 1) {
                    printf("Word comes after current node.\n");
                    word_node* temp = (word_node*) malloc (sizeof(word_node)); //allocate node for current str_buffer
                    temp->word = str_buffer;
                    temp->next = curr->next; //set temp node->next to current node's next
                    curr->next = temp; //set current->next to point to newly inserted node
                }
                else { //otherwise, str_buffer must come before current node
                    printf("Word comes before current node.\n");
                    word_node* temp = (word_node*) malloc (sizeof(word_node)); //allocate node for current str_buffer
                    temp->word = str_buffer;
                    printf("Previous Node: %s\n", prev->word);
                    printf("Current Node: %s\n", curr->word);
                    prev->next = temp;
                    temp->next = curr;
                }
                prev = curr;
                curr = curr->next; //move current node up by one
            }
        }
    }
    printStringList(head);
    exit(EXIT_SUCCESS);
}

int readLine(FILE *infile, char * line_buffer) {
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    while ((read = getline(&line, &len, infile)) != -1) {
        line_buffer = line;
        str_buffer = (char*) malloc (sizeof(line));
        strncpy(str_buffer, line_buffer, strlen(line));
        if(str_buffer[0] != '\0') {
            return 1;
        }
        else
            return -1;
    }
}

void printStringList(struct word_node *top) {
    struct word_node *curr = top; //set curr to head (for traversal)
    printf("List of Strings (and Frequencies)\n\n");
    int count = 0;
    while(curr != NULL) {
        printf("%s (Frequency: %d)\n", curr->word, curr->freq);
        curr = curr->next;
        count++;
    }
    return;
}


int main(int argc, char *argv[])
{
    FILE *file = fopen( argv[1], "r" );

    /* fopen returns 0, the NULL pointer, on failure */
    if ( file == 0 )
    {
        printf( "Could not open file.\n" );
    }
    else
    {
        readStringList(file);
    }   
}

测试文本文件(从终端运行时作为参数传入)

foofoo
dog

3 个答案:

答案 0 :(得分:1)

     else { //otherwise, str_buffer must come before current node
                ....
                prev->next = temp;
                temp->next = curr;

如果列表中只有一个节点,那么currprev指向同一个节点,并且您在此处引入了一个循环。最初您要设置prev和{{1} } curr。您最初应将head设置为prev,然后在新节点将成为第一个节点时处理该情况(NULLprev NULL 1}})

     str_buffer[ strlen(str_buffer) - 1 ] = '\0';
     head->word = str_buffer; //set string of node to str_buffer

此外,您正在为temp_buffer分配内存并使用仅仅是指针的str_buffer。您可能希望在此使用temp_buffer

答案 1 :(得分:0)

在这部分:

            else { //otherwise, str_buffer must come before current node
                printf("Word comes before current node.\n");
                word_node* temp = (word_node*) malloc (sizeof(word_node)); //allocate node for current str_buffer
                temp->word = str_buffer;
                printf("Previous Node: %s\n", prev->word);
                printf("Current Node: %s\n", curr->word);
                prev->next = temp;
                temp->next = curr;
            }

您需要为curr == head(或curr == prev)添加支票。这种情况需要特殊处理,因为您需要更新head

否则你会得到一个无限循环。在这种情况下,您当前的代码实际上是:

 head->next = newnode;
 newnode->next = head;

使列表循环(打印时无限循环)。

您需要以下内容:

if (curr == head)
{
    temp->next = head;
    head = temp;
    ....
}
else
{
    ....
}

答案 2 :(得分:0)

除了需要处理添加到前面作为更新head指针的特殊情况之外,我发现readLine例程中的内存分配令人困惑:

char* str_buffer = NULL;                                    // 1
struct node *readStringList(FILE *infile) {
    [...]
    char* tmp_buffer = (char*) malloc(sizeof(char) * 255);  // 2
    while(readLine(infile, tmp_buffer) == 1) {              // 3

int readLine(FILE *infile, char * line_buffer) {
    char * line = NULL;
    while ((read = getline(&line, &len, infile)) != -1) {   // 4
        line_buffer = line;                                 // 5
        str_buffer = (char*) malloc (sizeof(line));         // 6
        strncpy(str_buffer, line_buffer, strlen(line));     // 7

你正在为这里读取的每一行做三(3)次内存分配,其中一个是由getline在[4]处完成的,因为你传入NULL,另一个在{{1}在[6],在[{1}}在[2]的第三个。 readLine([2])在readStringList中的任何地方都没有使用,tmp_buffer也没有使用相应的参数(指针只是在[5]处被覆盖)。

此外,这里不需要使用全局变量[1],你的函数应该使用它们的参数来传递数据(并且已经有了这些参数)。

由于您使用readStringList,您只需将其分配的缓冲区返回到外部函数,并直接在那里使用。

这样的事情:

readLine