由于我是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;
}
答案 0 :(得分:1)
您将n
的地址传递给Insert
。 n
是指向节点struct Node **
的指针的指针,因此&n
是指向指向节点的指针的指针,struct Node ***
。
解决方案是将n
和n1
指针指向节点:
struct Node* n = NULL;
Insert(&n, book);
插入代码的逻辑是n
是链表的头部。当n
为NULL
时,列表为空。插入节点时,您必须能够更新头指针,以便n
中的main
值更改。一种方法是将指针传递给头部并更新它以取消引用指针。
您的Contains
和Print
函数不会更改列表,因此将指针传递给节点就足够了。这也将使您的代码看起来更简单,因为您无需在任何地方使用(*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
。首先,通过对left
和right
变量的分配重新替换尾调用,并向后添加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 */
}
}