我相信,就像你说的,你把事情搞得过于复杂了.
该问题源于C语言无法返回数组
嗯,这个问题本身对我来说并不清楚.
- 这百分百的无能为力是不存在的
- 为什么要在创建链表的程序中返回数组?你为什么不能这么做呢?
我的整个计划是使用ATOI将用户的命令行参数转换为数字,然后反向将它们添加到链表中.如果我的 list 是3,4,5,我加上1,2,它就会变成1,2,3,4,5(理论上).
这只是意味着您希望将元素始终添加到列表的开头.这不是一种常见的 Select :通常,最好将元素插入到末尾,保持插入的顺序.这样的名单被称为stable.
另一种常见的 Select 是按顺序插入元素,但保留可能重复项的插入顺序.因此,如果添加%1%1%2%3%4,则会在第一个%1之后插入第二个%1
我的 node 函数执行所有的转换,由于C的规则,我不能返回数组,所以我返回了一个指针
这是node
函数
// Get command line arguments from printf
int* nodes(char* input, int amount)
{
int* y = malloc(amount * 4);
char* x = &input[0];
y[0] = atoi(x);
int i = 0;
int j = 1;
while (input[i] != '\0')
{
if (input[i] == ' ')
{
x = &input[i + 1];
y[j] = atoi(x);
j++;
}
i++;
}
return y;
}
你想做什么?您说过atoi
将用于转换参数,但现在node
"完成了所有的转换".怎么会这样呢?
我认为所有的数字都是背靠背的,因为有了Malloc,所以我有一个循环遍历n->;Numbers=Numbers[Amount-a].第一个值是正确的,但然后其他的值只是一堆垃圾值.
我不明白你想说什么.malloc
仅根据单个参数(以字节为单位的大小)来分配内存.
我很确定这是因为指针只指向第一个对象,其余的对象都是按添加的顺序排列的.
嗯,这是意料之中的:指针指向某个东西.只对一件事.至于其余的,我看不出是什么.可能是其他因素?还是其他 node ?
我觉得我错过了一些重要的东西.
我也相信你是.请参见下面的示例和讨论.也许它增加了一些东西
restarting things: a C
complete EXAMPLE
我将try 向您展示一种可用于每个程序的方法,但使用链接列表的方式是您正在try 构建的.
因为这对学生来说是非常非常常见的作业(job),所以我会留下完整的代码和输出.
关于行参数
在开始时考虑该程序p
#include <stdio.h>
int main(int argc, char* argv[])
{
if (argc < 2)
{
printf("\tNo arguments provided!\n");
return -1;
}
printf(
" %d arguments on the command line\n\n", argc - 1);
for (int i = 1; i < argc; i += 1)
printf(" #%d \"%s\"\n", i, argv[i]);
return 0;
}
提出了这些论点
p 1 2 3 4 5 5
以下是输出:
6 arguments on the command line
#1 "1"
#2 "2"
#3 "3"
#4 "4"
#5 "5"
#6 "5"
该数组由操作系统提供.不需要仅仅为了插入到链表中而将其转换为另一个int
的array.我们可以只使用这些值,并按此顺序.
链表
这就是你所说的链表:
typedef struct node
{
int number;
struct node* next;
} node;
But it is not.看不到这一点会让你在DAA建筑周围的生活变得更加艰难.你有关于这方面的增刊吗?
链表 is a --- possibly empty --- collection of nodes.每个 node 包含或指向一些数据.
A node is not a list. A list is not a node.个
重写:一个 node
这是一个 node :
typedef struct s_n
{
int number;
struct s_n* next;
} Node;
请注意,只有一个方向的指针比有next
个和prev
个指针要复杂得多.单一链表比双向链表更难管理.
为什么?导航.例如,当您需要删除一个 node 时-您将-并且只有next
个指针,您必须记住,除非您要删除第一个元素,否则以前的一些 node 将指向您要删除的 node ,但是you do not have its 添加ress...
Supose List是{ v, x, y }
.删除x
时,如果Node
中有prev
个指针,那么x->prev
指向v
,v->next
指向x
.x->next
点,至y
点.
要删除x
,您必须将v
指向y
,y
在删除x
之前指向v
.这很简单:
x->prev->next = y
和y->prev = v
.那么x
就可以删除了.
如果只有next
个指针,则需要在代码中保存删除
的状态.请参见下面的示例.
和一份名单
typedef struct
{
unsigned size;
Node* head;
Node* tail;
} List;
现在我们有了一个列表和一个 node .请注意,在程序中创建的名称保留第一个大写字母的惯例.
正如您所看到的,该列表有两个指针,因为我们希望保持值的插入顺序,而每次仅仅为了获得最后一个 node 的地址而遍历该列表将不是很有效.
而且,由于随时可以方便地知道列表中有多少个 node ,因此我们也有size
个 node .我相信这是很容易知道它是如何unsigned
的价值.
你当然可以没有size
个,但如果它总是在列表中,那么总是知道它的项目数要简单得多.
一些规划
在编写任何程序之前,让我们看看我们将需要的操作以及程序将如何工作.
操作:
在程序启动时,将使用命令行中提供的值创建该列表.如果没有,则列表将被创建为空,因为仍然可以对空列表执行所有操作
一些函数
在Raii in C++
的风格中,一开始我们有
List* create();
List* destroy(List* toGo);
使用这种方式使工作更容易:可以在一行中创建List
,同时销毁List
,我们可以保证列表指针无效:没有机会悬空指针.
请注意,我们还没有一行代码.只是浏览了一下数据.
更多功能
如果我们想要销毁一个列表,很明显,我们需要在删除列表本身之前删除所有 node ,否则就会泄漏内存.
在我们进行测试时,很明显我们还需要一个打印列表内容的选项.因此,我们添加这些函数作为最低要求
int 添加(int data, List* list);
Node* 删除(int one, List* list);
int display(List* list, const char* title);
int 发现(int one, List* list);
由于我们有一些定义和响应行参数的原始程序,所以我们可以编写第二个程序
第二个项目
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct s_n
{
int number;
struct s_n* next;
} Node;
typedef struct
{
unsigned size;
Node* head;
Node* tail;
} List;
List* create();
List* destroy(List* toGo);
int 添加(int data, List* list);
int 删除(int one, List* list);
int display(List* list, const char* title);
int 发现(int one, List* list);
int main(int argc, char* argv[])
{
if (argc < 2)
{
printf("\tNo arguments provided!\n");
return -1;
}
printf(
" %d arguments on the command line\n\n",
argc - 1);
for (int i = 1; i < argc; i += 1)
printf(" #%d \"%s\"\n", i, argv[i]);
return 0;
}
List* create() { return NULL; }
List* destroy(List* toGo) { return NULL; }
int 添加(int data, List* list) { return 0; }
int 删除(int one, List* list);
int display(List* list, const char* title) { return 0; }
int 发现(int one, List* list) { return 0; }
我们甚至可以运行它.
create
and destroy
list
创建列表只需为其分配内存并将其标记为空
List* create()
{
List* one = malloc(sizeof(List));
if (one == NULL) return NULL;
one->size = 0;
one->head = NULL;
one->tail = NULL;
return one;
}
删除列表也是基本的:我们知道我们有size
个 node ,我们知道如何删除每个 node ,因为我们有一个删除列表的函数,所以...
List* destroy(List* gone)
{
if (gone == NULL) return NULL;
for (unsigned i = 0; i < gone->size; i += 1)
{
Node* p = gone->head->next;
free(gone->head);
gone->head = p;
}
free(gone);
return NULL;
};
这是正常的,因为这里的 node 不分配内存.
A third program
现在,我们可以运行此版本的Main:
int main(int argc, char* argv[])
{
List* my_list = create();
my_list = destroy(my_list);
return 0;
}
请注意,destroy()
总是返回NULL
,这可能看起来很愚蠢.但目标是能够使列表被销毁的同一行中的List
指针无效,并在调用destroy()
的代码中:
my_list = destroy(my_list);
这不会使悬挂的无效指针在调用代码中保持丢失的机会...
而且它运行得很好!它没有什么用处,但对精神上的支持是有好处的
inserting data: 添加()
int 添加(int data, List* list)
{ // insert at last element
if (list == NULL) return -1;
Node* p = malloc(sizeof(Node));
p->number = data; // data copied to node
p->next = NULL; // inserted at tail
if (list->size == 0)
{
list->head = list->tail = p;
list->size = 1;
return 0;
}
list->tail->next = p;
list->tail = p;
list->size += 1; // count it in
return 0;
}
略多于10行.请注意,我们使用tail
指针在结尾处插入并保持 node 的插入顺序.无论如何,在开头插入或head
是相同的,只需使用head
指针,并使您的node->next
首先指向head
,然后再重新分配head
.
To test this we need display()
很明显,我们可以在一个空的列表上调用display()
,也可以在一个包含几十个项目的列表上调用display()
.或者甚至使用无效指针,所以让我们这样做吧.
标题的使用在这里非常方便,所以不需要在测试非常非常常见的printf
之前...
int display(List* list, const char* title)
{
if (title != NULL) printf("%s", title);
if (list == NULL)
{
printf("\nList does not exist\n");
return 0;
}
printf("\nList size: %u\n [ ", list->size);
Node* p = list->head;
for (unsigned i = 0; i < list->size; i += 1)
{
printf("%d ", p->number);
p = p->next;
}
printf("]\n\n");
return 0;
};
display()
只是跟着指针走.这份名单实际上是极简主义的,只有一个数字.
第4个节目现在甚至可以显示一些数据
请考虑以下几点:
int main(int argc, char* argv[])
{
if (argc < 2)
{
printf("\tNo arguments provided!\n");
return -1;
}
printf(
" %d arguments on the command line\n", argc - 1);
List* my_list = create();
display(my_list, "list empty");
for (int i = 1; i < argc; i += 1)
添加(atoi(argv[i]), my_list);
display(my_list, "after insertions");
my_list = destroy(my_list);
display(my_list, "afer destroy()");
return 0;
}
和的输出
p 1 2 3 4 5
5 arguments on the command line
list empty
List size: 0
[ ]
after insertions
List size: 5
[ 1 2 3 4 5 ]
afer destroy()
List does not exist
到目前为止,没有什么令人惊讶的:在第一次运行时就像预期的那样工作,因为我们一步一步地围绕数据编写
删除()
and 发现()
simple implementations
它们类似于display()
,因为它是导航整个列表的问题,但是
删除()
return 0 when element is found and 删除d
发现()
returns the Node
添加ress when found, or NULL
这是一个可能的删除()
:
int 删除(int data, List* list)
{ // 删除 the 1st node that contains 'data'
if (list == NULL) return -1;
Node* p = list->head;
Node* prev = NULL;
for (unsigned i = 0; i < list->size; i += 1)
{
if (p->number == data)
{ // this is the one
if (p == list->head)
list->head = p->next;
else
{
prev->next = p->next;
if (p == list->tail) list->tail = prev;
}
list->size -= 1;
free(p);
return 0;
}
prev = p; // save state: no back pointer
p = p->next; // go ahead
}
return -2; // not found
};
由于缺少后向指针,大约有15行
以及可能的发现()
分:
Node* 发现(int data, List* list)
{ // return 添加ress of the first node with 'data'
// or NULL if not found. Do not consider the
// 'list` as sorted
if (list == NULL)
{
fprintf(stderr, "\n发现(): List does not exist\n");
return NULL;
}
Node* p = list->head;
for (unsigned i = 0; i < list->size; i += 1)
{
if (p->number == data) return p;
p = p->next;
}
return NULL;
};
第五期节目时间到了
插入命令行参数(如果有)
现在是恢复第一个程序并插入命令行中的参数的好时机.
我们已经有添加()
个了,这不会很难的.参数是字符串,但我们也有atoi()
个so:
int main(int argc, char* argv[])
{
if (argc < 2)
{
printf("\tNo arguments provided!\n");
return -1;
}
printf(
" %d arguments on the command line\n", argc - 1);
List* my_list = create();
display(my_list, "list empty");
for (int i = 1; i < argc; i += 1)
添加(atoi(argv[i]), my_list);
display(my_list, "after insertions");
//
删除 (1, my_list);
删除 (3, my_list);
删除 (5, my_list);
display(my_list, "after 删除 1 3 & 5");
//
my_list = destroy(my_list);
display(my_list, "afer destroy()");
return 0;
}
插入了3个删除第一条、最后一条和中间记录的调用以进行测试...
这句话是唯一的 fresh 事:
for (int i = 1; i < argc; i += 1) 添加(atoi(argv[i]), my_list);
在列表中插入参数之前只需调用atoi()
即可.
我们知道添加()
个有效,我们知道atoi()
个有效,其余的我们已经测试过了,所以这只是一个复制和粘贴的问题.
输出
5 arguments on the command line
list empty
List size: 0
[ ]
after insertions
List size: 5
[ 1 2 3 4 5 ]
after 删除 1 3 & 5
List size: 2
[ 2 4 ]
afer destroy()
List does not exist
到目前为止的完整例子
C
code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct s_n
{
int number;
struct s_n* next;
} Node;
typedef struct
{
unsigned size;
Node* head;
Node* tail;
} List;
List* create();
List* destroy(List* toGo);
int 添加(int data, List* list);
int 删除(int one, List* list);
int display(List* list, const char* title);
Node* 发现(int one, List* list);
int main(int argc, char* argv[])
{
if (argc < 2)
{
printf("\tNo arguments provided!\n");
return -1;
}
printf(
" %d arguments on the command line\n", argc - 1);
List* my_list = create();
display(my_list, "list empty");
for (int i = 1; i < argc; i += 1)
添加(atoi(argv[i]), my_list);
display(my_list, "after insertions");
//
删除 (1, my_list);
删除 (3, my_list);
删除 (5, my_list);
display(my_list, "after 删除 1 3 & 5");
//
my_list = destroy(my_list);
display(my_list, "afer destroy()");
return 0;
}
List* create()
{
List* one = malloc(sizeof(List));
if (one == NULL) return NULL;
one->size = 0;
one->head = NULL;
one->tail = NULL;
return one;
}
List* destroy(List* gone)
{
if (gone == NULL) return NULL;
for (unsigned i = 0; i < gone->size; i += 1)
{
Node* p = gone->head->next;
free(gone->head);
gone->head = p;
}
free(gone);
return NULL;
};
int 添加(int data, List* list)
{ // insert at last element
if (list == NULL) return -1;
Node* p = malloc(sizeof(Node));
p->number = data; // data copied to node
p->next = NULL; // inserted at tail
if (list->size == 0)
{
list->head = list->tail = p;
list->size = 1;
return 0;
}
list->tail->next = p;
list->tail = p;
list->size += 1; // count it in
return 0;
}
int 删除(int data, List* list)
{ // 删除 the 1st node that contains 'data'
if (list == NULL) return -1;
Node* p = list->head;
Node* prev = NULL;
for (unsigned i = 0; i < list->size; i += 1)
{
if (p->number == data)
{ // this is the one
if (p == list->head)
list->head = p->next;
else
{
prev->next = p->next;
if (p == list->tail) list->tail = prev;
}
list->size -= 1;
free(p);
return 0;
}
prev = p; // save state: no back pointer
p = p->next; // go ahead
}
return -2; // not found
};
int display(List* list, const char* title)
{
if (title != NULL) printf("%s", title);
if (list == NULL)
{
printf("\nList does not exist\n");
return 0;
}
printf("\nList size: %u\n [ ", list->size);
Node* p = list->head;
for (unsigned i = 0; i < list->size; i += 1)
{
printf("%d ", p->number);
p = p->next;
}
printf("]\n\n");
return 0;
};
Node* 发现(int data, List* list)
{ // return 添加ress of the first node with 'data'
// or NULL if not found. Do not consider the
// 'list` as sorted
if (list == NULL)
{
fprintf(stderr, "\n发现(): List does not exist\n");
return NULL;
}
Node* p = list->head;
for (unsigned i = 0; i < list->size; i += 1)
{
if (p->number == data) return p;
p = p->next;
}
return NULL;
};
我只是想展示一种写这些东西的方法.
发现()
没有考试,但它和删除()
一样.请让我知道这是否有帮助,如果有什么我可以补充的.