列表结构 - 检查子列表

时间:2015-04-09 19:09:05

标签: c pointers sublist

由于我是C编程的新手,我将发布整个(不是长代码)。我已经给出的任务是在列表中实现元素的插入,同时列表保持有序,打印它,然后检查一个列表是否是另一个列表的子列表。 虽然我的插入打印方法有效,但我收到了一堆警告: 警告:传递'插入'的参数1来自不兼容的指针类型[默认启用] | 。如何修复我的代码以删除这些警告?

另外,从逻辑上讲,我认为Contains方法没问题,为什么它不起作用?比较两个单个元素列表时它确实有效。

代码如下所示:

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

typedef struct Book{
    int id;
    char name[50];
    float earnings;
} Book;

struct Node{
    struct Book data;
    struct Node* next;
};

void Insert(struct Node** list, struct Book k)
{
    struct Node* previous;
    struct Node* current;
    struct Node* newNode;

    newNode=(struct Node*)malloc(sizeof(struct Node));

    newNode->data = k;
    newNode->next = NULL;

    if(*list==NULL)
    {
        *list = newNode;
    }
    else
    {
        current = *list;
        previous = NULL;

        while((current!=NULL) && current->data.earnings<k.earnings){
            previous = current;
            current = current->next;
        }
        if(current == NULL)
            previous->next = newNode;
        else
        {
            if(previous == NULL)
            {
                newNode->next = (*list);
                *list = newNode;
            }
            else
            {
                previous->next = newNode;
                newNode->next = current;
            }
        }
    }
}

void Print(struct Node** list){
    if((*list)==NULL){
       printf("List is empty.");
       return;
    }
    printf("\nList looks like this:\n");
    while(*list!=NULL){
        printf("%d\n",(*list)->data.id);
        printf("%s\n",(*list)->data.name);
        printf("%f\n",(*list)->data.earnings);
        *list = (*list)->next;
    }
    printf("\n");
}


int Contains(struct Node** l1, struct Node** l2){
    while((*l2)!=NULL)
    {
        while((*l1)!=NULL)
        {
            if((*l1)->data.id==(*l2)->data.id && (*l1)->data.name==(*l2)->data.name)
                return 1;

            *l1 = (*l1)->next;
        }
        *l2 = (*l2)->next;
    }
    return 0;
}

int main()
{
    struct Book book = {5,"War and peace",100.50};
    struct Node** n = NULL;
    struct Book book1 = {6,"Anna Karenina",20.5};
    struct Book book2 = {7,"Master and Margarita", 150.60};
    struct Node** n1 = NULL;
    struct Book book3 = {6,"Anna Karenina",20.5};


    Insert(&n,book);
    Insert(&n,book1);
    Insert(&n,book2);
    Print(&n);
    Insert(&n1,book3);

    printf("\nDoes list2 contains list1? YES - 1, NO - 0 : %d",Contains(&n1,&n));

    return 0;
}

3 个答案:

答案 0 :(得分:1)

您将n的地址传递给Insertn是指向节点struct Node **的指针的指针,因此&n是指向指向节点的指针的指针,struct Node ***

解决方案是将nn1指针指向节点:

struct Node* n = NULL;

Insert(&n, book);

插入代码的逻辑是n是链表的头部。当nNULL时,列表为空。插入节点时,您必须能够更新头指针,以便n中的main值更改。一种方法是将指针传递给头部并更新它以取消引用指针。

您的ContainsPrint函数不会更改列表,因此将指针传递给节点就足够了。这也将使您的代码看起来更简单,因为您无需在任何地方使用(*p)语法。

contains函数有两个错误:首先,您无法将C字符串与==进行比较。 C字符串是char数组;如果你想比较它们,你必须比较它们的内容。来自strcmp的标准函数<strings.h>就是这样做的。

其次,您有两个链表遍历的嵌套循环。您可以使用原始节点变量遍历外部循环,但必须使用额外的节点指针作为内部循环并在遍历列表之前重置它。

还不清楚“包含”的含义。在当前(预期)实现中,它意味着:两个列表中是否有任何通用书籍?一个更有用的功能会问一个问题:列表中是否有某本书?

以下是Contains

的修改后的变体
int Contains(struct Node *l1, struct Node *l2)
{
    while (l2 != NULL) {
        struct Node *p = l1;

        while (p != NULL) {
            if (p->data.id == l2->data.id 
            && strcmp(p->data.name, l2->data.name) == 0)
                return 1;

            p = p->next;
        }

        l2 = l2->next;
    }

    return 0;
}

你可以像这样打电话:

int c = Contains(n1, n);

答案 1 :(得分:0)

struct Node** n = NULL;替换为struct Node* n = NULL;并对n1

执行相同操作

您目前正在做的事情是struct Node*** list,这就是您收到警告的原因

对于Contains的问题,首先比较字符串使用strcmp你刚检查过的指针, 在您l1上的第一次循环后,您最终得到l1==NULL,以便在第一次之后不会进入

int Contains(struct Node** l1, struct Node** l2)
{
    struct Node* tmp;

    while ((*l2) != NULL)
    {
        tmp = *l1;
        while (tmp != NULL)
        {
            if(tmp->data.id==(*l2)->data.id &&
                strcmp(tmp->data.name, (*l2)->data.name) == 0)
            {
                return 1;
            }

            tmp = tmp->next;
        }
        *l2 = (*l2)->next;
    }
    return 0;
}

答案 2 :(得分:0)

请研究以下内容。主要思想是我们更好地利用函数,不要将指向指针的引用传递给list;我们的Insert函数返回新列表。我们也利用递归。

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

typedef struct Book {
    int id;
    char name[50];
    float earnings;
} Book;

typedef struct Node{
    Book data;
    struct Node* next;
} Node, *List;

#define Empty ((List) NULL)

List NewNode(struct Book data, List next)
{
    List newNode = (Node *) malloc(sizeof *newNode);
    newNode->data = data;
    newNode->next = next;
    return newNode;
}

List Insert(List list, struct Book book)
{
    if (list == Empty) {
        return NewNode(book, Empty);
    } else if (list->data.earnings < book.earnings) {
        return NewNode(book, list);
    } else {
        /* Rewrite the rest of the list by inserting into it,
           then patch the front node to point to the rewritten list. */
        List subList = Insert(list->next, book);
        list->next = subList;
        return list;
    }
}

void PrintOne(List list)
{
    printf("%d\n", list->data.id);
    printf("%s\n", list->data.name);
    printf("%f\n" ,list->data.earnings);
}

void Print(List list)
{
    if (list == Empty) {
       puts("List is empty.");
    } else {
        puts("List looks like this:");
        for (; list != Empty; list = list->next)
            PrintOne(list);
    }
}

int IsTailOf(List leftList, List rightList)
{
    if (leftList == rightList)
        return 1; /* Every list is its own tail */
    else if (rightList == Empty)
        return 1; /* The empty list is the tail of all lists, incl. itself */
    else if (leftList == Empty)
        return 0; /* Nonempty right cannot be tail of empty left */
    else
        return IsTailOf(leftList->next, rightList); /* Tail of my rest is my tail */
}


int main()
{
    Book book0 = {5,"War and peace",100.50};
    Book book1 = {6,"Anna Karenina",20.5};
    Book book2 = {7,"Master and Margarita", 150.60};
    Book book3 = {6,"Anna Karenina",20.5};

    List n0, n1, n2;

    List list = Empty;

    n0 = list = Insert(list, book0);
    list = Insert(list, book1);
    n1 = list = Insert(list, book2);
    Print(n0);
    n1 = list = Insert(list, book3);

    printf("List pointers: list == %p, n0 == %p, n1 == %p, n2 == %p\n",
           (void *) list, (void *) n0, (void *) n1, (void *) n2);

    printf("Does list contain n0? YES - 1, NO - 0 : %d\n",
           IsTailOf(list, n0));

    printf("Does n1 contain list? YES - 1, NO - 0 : %d\n",
           IsTailOf(n1, list));

    printf("Does n0 contain list? YES - 1, NO - 0 : %d\n",
           IsTailOf(n0, list));

    return 0;
}

子列表函数IsTailOf严格地与节点指针一起工作,基于对什么&#34;子列表&#34;的可能解释。手段。它检测右侧列表是否在物理上是左侧列表(或整个事物)的一部分。

如果要计算给定列表的尾部是否与另一个列表相同,则不同。导致易于编写(和读取)代码的简单方法是首先实现比较两个列表的等价测试函数。然后可以重复尝试此测试:

#include <string.h> /* for strcmp */

int EqualBooks(Book left, Book right)
{
    return left.id == right.id && strcmp(left.name, right.name) == 0;
}

int EqualLists(List left, List right)
{
    /* Different style here from IsTailOf, just for variety:
       no "if else", just "if and return" */

    if (left == right)
        return 1; /* They are the same object! */

    if (left == Empty || right == Empty)
        return 0; /* Empty is not equal to nonempty */

    if (!EqualBooks(left->data, right->data))
        return 0; /* Equal lists must be equal in the first item */

    /* Equal lists must have equal remainders, not only first items */
    return EqualLists(left->next, right->next);
}

一个简单实施的子列表测试(让我们称之为&#34;后缀&#34;测试)然后看起来像:

int IsSuffixOf(List left, List right)
{
    if (EqualLists(left, right))
        return 1; /* a full match implies suffix relationship */

    if (left == Empty)
        return 0; /* Nonempty cannot be a suffix of empty */

    return IsSuffixOf(left->next, right);
}

琐碎的尾递归很容易变成迭代。我们来看IsSuffixOf。首先,通过对leftright变量的分配重新替换尾调用,并向后添加goto

int IsSuffixOf(List left, List right)
{
tail:
    if (EqualLists(left, right))
        return 1; /* a full match implies suffix relationship */

    if (left == Empty)
        return 0; /* Nonempty cannot be a suffix of empty */

    left = left->next;
    goto tail;
}

现在它是迭代的;我们只是通过重写goto循环消除for

int IsSuffixOf(List left, List right)
{
    for (;; left = left->next) {
        if (EqualLists(left, right))
            return 1; /* a full match implies suffix relationship */

        if (left == Empty)
            return 0; /* Nonempty cannot be a suffix of empty */
     }
}