0

我需要一些认真的帮助来理解 C++ 中的链接列表我想使用我几周前使用数组结构编写的程序并将它们转换为链接列表并添加几个新函数。我最担心的是我对链接列表没有信心,并且一直在这里和其他网站上花费时间来获取有关它们的知识。但是我找不到可以帮助我解决我现在面临的问题的来源。

这是我的原始代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX 100

struct YouTubeVideo { 
char video_name[1024];        // YouTube video name
int video_ranking;            // Number of viewer hits
char video_url[1024];         // YouTube URL
};

struct YouTubeVideo Collection[MAX];

int tail = 0;

//-- Forward Declaration --// 
void printall();
void insertion();
void sort();
void branching(char option);        
void menu(); 
void load_file();
void save_file();

int main()
{
char ch; 

load_file();

printf("\n\nWelcome to CSE240: YouTube Classic Hits\n");

do {
     menu();
     fflush(stdin);                 // Flush the standard input buffer 
     ch = tolower(getchar());       // read a char, convert to lower case
     branching(ch);
} while (ch != 'q');

return 0; 
}

void menu()
{
 printf("\nMenu Options\n");
 printf("------------------------------------------------------\n");
 printf("i: Insert a new favorite\n");
 printf("p: Review your list\n"); 
 printf("q: Save and quit\n");
 printf("\n\nPlease enter a choice (i, p, or q) ---> "); 
}

void branching(char option)
{
switch(option)
{
    case 'i':
        insertion();
        sort();
    break;

    case 'p':
        printall();
    break;

    case 'q':
        save_file();
    break;

    default:
        printf("\nError: Invalid Input.  Please try again..."); 
    break;
}
}

void insertion()
{
if(tail < MAX)
{
    printf("\nWhat is the name of the video? (No spaces characters allowed)\n");
    scanf("%s", Collection[tail].video_name);

    printf("\nHow many viewer hits does this video have?\n");
    scanf("%d", &Collection[tail].video_ranking);

    printf("\nPlease enter the URL: ");
    scanf("%s", Collection[tail].video_url);

    tail++;
}
else
{
    printf("\nERROR: Your collection is full. Cannot add new entries.\n");
}
}

void sort()
{
int i = 0, j = 0; 
struct YouTubeVideo temp;

for(i = 0; i < tail; i++)
{
    for(j = i+1; j < tail; j++)
    {
        if(Collection[i].video_ranking < Collection[j].video_ranking)
        {
            temp = Collection[i];
            Collection[i] = Collection[j];
            Collection[j] = temp;
        }
    }
}

//RA: I think it's easier (and faster) to assume your current list is already
//    sorted and then insert your new element into the correct position. (You
//    can show this maintains a sorted list by induction.)

printf("\nSorting Complete...\n");
}

void printall()
{
int i; 

printf("\nCollections: \n"); 

for(i = 0; i < tail; i++)
{
    printf("\nVideo Name: %s", Collection[i].video_name);
    printf("\nRanking (Hits): %d", Collection[i].video_ranking);
    printf("\nURL: %s", Collection[i].video_url);
    printf("\n");
}
}

void save_file() { 
FILE *fileName;                                     // declare a pointer to File type 
char ch; 
int index = 0; 

fileName = fopen("ranking.dbm", "wb");              // "b" for binary mode 
                                                        // ìwî for write


if(fileName != NULL) 
{   
    fwrite(&tail, sizeof(int), 1, fileName);        // Write tail to the     file for later retrieval.

    for(index = 0; index < tail; index++)
    { 
        fwrite(&Collection[index].video_name, 1024, 1, fileName); 
        fwrite(&Collection[index].video_ranking, sizeof(int), 1, fileName);     
        fwrite(&Collection[index].video_url, 1024, 1, fileName);
    } 

    fclose(fileName);
} 
else 
    printf ("ERROR: Could not open file for saving data !\n"); 
}

void load_file() { 
FILE *fileName;                         // declare a pointer     to File type 
int index = 0; 

fileName = fopen("ranking.dbm", "rb");  // "b" for binary mode 
                                    // ìrî           for read

if(fileName != NULL) {   
    fread(&tail, sizeof(int), 1, fileName);

    for(index = 0; index < tail; index++) 
    {
        fread(Collection[index].video_name, 1024, 1, fileName);
        fread(&Collection[index].video_ranking, sizeof(int), 1, fileName);
        fread(Collection[index].video_url, 1024, 1, fileName);
    }

    fclose(fileName);
}
else 
    printf ("ERROR: Could not open file for loading data !\n"); 
}

这些是我应该做的确切说明:

Convert the “YouTubeVideo” array structure (Collection) into a linked-list. The program must sort (by “video_name”) the entries as they are inserted into the linked-list. [30 points] (*Note: You will lose 10 points if the linked list is not sorted.)

现在,根据我目前的理解,我已经尽我所能尝试了,但我现在遇到了问题。

这是我尝试解决方案的代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

using namespace std;

#define MAX 100

 struct YouTubeVideo
{
char name[1024];        // YouTube video name
int ranking;                // Number of viewer hits
char url[1024];             // YouTube URL
};

struct YouTubeVideo Collection[MAX];

int tail = 0;

//-- Forward Declaration --//
void printall();
void insertion();
void branching(char option);
void menu();


int main()
{
char ch;

// TODO: Add code to load save data from file

cout << "\n\nWelcome to CSE240: YouTube Classic Hits\n";

do {
    menu();
    cin >> ch; // read a char, convert to lower case
    cin.ignore();

    ch = tolower(ch);
    branching(ch);
} while (ch != 'q');

return 0;
}

void menu()
{
cout << "\nMenu Options\n";
cout << "------------------------------------------------------\n";
cout << "i: Insert a new favorite\n";
cout << "p: Review your list\n";
cout << "s: Search\n";
cout << "d: Delete an entry\n";
cout << "q: Save and quit\n";
cout << "\n\nPlease enter a choice (i, p, s, d, or q) ---> ";
}

void branching(char option)
{
switch(option)
{
    case 'i':
        insertion();
        break;

    case 'p':
        printall();
        break;

    case 's':
        // TODO: Add code to search for a particular node by name
        break;

    case 'd':
        // TODO: Add code to remove a node
        break;

    case 'q':
        // TODO: Add code to save data into a file
        break;

    default:
        cout << "\nError: Invalid Input.  Please try again...";
        break;
}
}

void insertion() { // insert a new entry
struct YouTubeVideo *p, *temp;
p = (struct YouTubeVideo *) malloc(sizeof(struct YouTubeVideo)); if (p == 0) {
    printf("out of memory\n"); return; }
printf("Enter Video name, Views, URL: \n"); scanf("%s", p->name); // p->name is array         scanf("%d", &p->phone);
scanf("%s", p->ranking);
temp = head;
if ((head == NULL)||(strcmp(p->name, temp->name) <=0)) {
    p->next = head;
    head = p;
}

else {
    while (temp->next != NULL) {
        if (stricmp(p->name, temp->next->name) <=0) { p->next = temp->next;
            temp->next = p;
            return;
        } else
            temp = temp->next; }
    p->next = NULL;
    temp->next = p;
} }

void printall()
{
int i;

cout << "\nCollections: \n";

for(i = 0; i < tail; i++)
{
    cout << "\nVideo Name: " << Collection[i].name << "\n";
    cout << "\nRanking (Hits): " << Collection[i].ranking << "\n";
    cout << "\nURL: " << Collection[i].url << "\n";
    cout << "\n";
}
}

我遇到的问题是我insertion收到错误undeclared identifier head并且no member named next in YouTubeVideo. 我尝试在很多地方放置和声明它们,但似乎无法修复这些错误。

我真的很感激你能给我的一些帮助和任何可能的知识。我真的给了这个很大的努力,但我只是被困住了。

4

3 回答 3

1

好的,我会尽力让您快速了解 C++ 中的链表是什么,同时让您通过自己的作业来学习。您的代码几乎是 C,这将更像 C++ 风格,尽管我会避免使用模板,并且有时会拼出 C++ 兽医可能会使事情变得更简洁的内容。

这是一个包含整数的单链表的典型简单节点结构:

struct LinkedListNode
{
  int value;
  LinkedListNode* next;
};

这个节点保存一个整数,加上它保存一个指向列表中下一个节点的指针。

对于这样的链表,这将是一个简化的界面:

struct LinkedList
{
public:
  LinkedList();
  bool isEmpty() const;
  int valueAtBeginning() const;
  void insertAtBeginning(int newValue);

private:
  LinkedListNode* head;
};

这个类提供了一个构造函数(一种制作链表的方法),一种向列表中插入新项目的方法,一种获取列表中第一个值的方法,以及一种检查列表是否为空的方法。它还保留(供自己参考)指向列表中第一个节点的指针。

让我们通过一种方式来实现这些。

这是构造函数:

LinkedList::LinkedList():
  head(NULL)
{
}

此函数将“第一项”指针初始化为 NULL。这将是“没有第一项,因为列表为空”的代码。说到这里:

bool LinkedList::isEmpty() const
{
  return (head == NULL);
}

这个函数说“如果头指针为空,则列表为空。否则它不是”。请注意该方法是如何标记的const,这使得该代码不会修改列表的任何部分。

下一个也很简单:

int LinkedList::valueAtBeginning() const
{
  assert(!isEmpty());
  return head->value;
}

这个函数简单地跟随指向列表中第一项的指针,并取出它的value成员并返回它。它还断言列表不为空。这将很容易找出您是否犯了从空列表中要求某些东西的错误。同样,请注意该方法是如何标记的const,因为它不会更改列表。

最后,在开头添加新内容:

void LinkedList::insertAtBeginning(int newValue)
{
  LinkedListNode* oldHead = head;
  LinkedListNode* newHead = new LinkedListNode();
  newHead->value = newValue;
  newHead->next = oldHead;
  head = newHead;
}

这在概念上很简单。我们创建一个新节点,并将其粘贴在列表的前面。旧的第一项成为第二项。另外,请注意我在这里如何使用 C++new而不是 C。malloc测验:如果列表为空,这会起作用吗?

好的,现在我将由您来存储不仅仅是整数。另外,尝试弄清楚如何编写一个从列表中删除第一项的方法(使用 C++ delete,而不是 C free)。然后尝试编写一个“遍历”列表的方法,打印出每个整数。一旦你把它写下来,试着在最后或中间编写添加/删除的方法。

于 2012-10-11T21:46:18.580 回答
1

您需要实际实现一个链表。这看起来像是一个家庭作业,所以我想知道你对 C++ 的了解程度。如果您还没有真正进行类和面向对象的编程,那么解决此问题的最简单方法是将下一个和上一个对象添加到您的 youtube 视频结构中。像这样:

struct YouTubeVideo {   
    char video_name[1024];        // YouTube video name  
    int video_ranking;            // Number of viewer hits 
    char video_url[1024];         // YouTube URL  
    YouTubeVideo* next;
    YouTubeVideo* previous;
};  

接下来,您将需要添加一个 head 声明。这将是 YouTubeVideo* 类型。添加第一个视频时,将头部设置为指向该视频。然后,无论何时添加新视频,将头视频的下一个指针设置为指向新视频,新视频上的前一个指针应指向头视频。那是您的链表的开始,但是您的解决方案仍然很混乱。

如果我是你,我会看看一些 Linked List 类是如何实现的。这是我编写的第一个链表类的头文件:

#ifndef LINKEDLIST_H 
#define LINKEDLIST_H

#include "List.h" 

class LinkedList : public List { 
 public: 
  LinkedList();
  virtual double get(int index) const; 
  virtual void add(double item); 
  virtual void insert(int index, double item); 
  virtual double delete_item(int index); 
  virtual int size() const;  
 private:
  class Node;
  Node* first;
  Node* last;
  int listsize;
}; 
class LinkedList::Node{
 public:
  double value;
  Node* next;
};

#endif

可以看到这个列表中有一个类 Node ,这个类包含一个 value double value。如果您想使用此代码,则必须使您的节点类具有一个字段,即 YouTubeVideo* 值。

于 2012-10-11T21:46:26.783 回答
0

简单来说,您需要将数组(结构类型)转换为具有相同字段的链表。

#include <stdio.h>
#include <stdlib.h>

struct LL
{
    char name[1024];        // YouTube video name
    int ranking;                // Number of viewer hits
    char url[1024];             // YouTube URL

    struct LL *next; // pointer to the next element
};

// this is from your code
#define MAX 100
struct YouTubeVideo
{
    char name[1024];        // YouTube video name
    int ranking;                // Number of viewer hits
    char url[1024];             // YouTube URL
};

// now you have an array of those structs
struct YouTubeVideo Collection[MAX];

// and you can fill it up as you wish

int main(int argc, char**argv)
{
    struct LL *ll = NULL;

    for (int i = 0; i < MaxElements; i++)
    {
        // we have a blob of memo to store your stuff
        x=(struct LL*)calloc(1, sizeof(struct LL));
        if (x != NULL)
    {
        // just run out of memory
        // so handle the error
    }
        else
    {
        // nothing to do just copy fields of Collection[i]
        // to the newly allocated space
        x->name    = strdup(Collection[i].name);
        x->ranking = Collectionp[i].ranking;
        x->url     = strdup(Collection[i].name);
        x->next    = NULL;

        // since you want your result sorted we need to find its
        // location in the linked list

        if (ll == NULL) // if the list is empty
        {
            ll=x;
            // and nothing else to do since a list with a single element
            // is always sorted
        }
        else
        {
            struct LL *p = ll, *q = ll;
            // need to find where in the list should x be inserted
            // p is not null (see the assignment above) so we
            // always can call strcmp on it. also for x

            while (p!=NULL && strcmp(p->name, x->name) < 0)
        // p can become null if your struct is to become the last
        // in the linked list: the order of comparisons are
        // important
        {
            q=p; // we need q, the parent node because it is
                 // the parent node's next pointer needs to be modified
            p=p->next;
        }
            // once we get here p points to an LL structure or NULL if
            // the element to be inserted will be the last in the list
            // q points to the element before p

                // one more trick: if element being inserted is comes earlier than
                // the first element we need to modify ll
                if (q == ll)
                {
                 x->next = ll;
                 ll = x;
                }
                else
                {
                    x->next=q->next;
                    q->next=x;

                // these lines don't fiddle with p
            }
     }
  }

}

您可能希望将代码放入函数中。请注意,插入单链表比插入双链表要复杂一些,但它节省了一个指针。还有一句警告:我没有测试过这个,只是输入了它的逻辑。

于 2012-10-11T22:38:06.177 回答