我在这个网站上看到了很多链接列表的C实现示例,并且大多数将下一个指针放在每个节点的末尾,就像这样......
struct intNode1 {
int data;
intNode1 *next;
};
为什么他们这样实现它们而不是像这样?
struct node {
struct node *next;
};
struct intNode2 {
struct node node;
int data;
};
实现链接列表的后一种方式允许您的插入和删除代码在任何类型的节点上工作,并允许您创建通用列表类型,而前一种方式强制您从头开始实现每种类型的列表。
例如,这是使用两种节点的单链表的(不完整)实现:
struct intList {
struct intNode1 *head;
};
struct list {
struct node *head;
};
现在,显然在通用列表上需要比较它的节点的任何操作都需要一个指向比较函数的函数指针,但这通常可以隐藏在列表的不太通用的接口的实现中。例如:
/* Returns zero if successful or nonzero otherwise */
int list-insertInt(struct list *list, int n) {
struct intNode2 * newNode;
if(!(newNode = malloc(sizeof *newNode)) {
return -1;
}
newNode->data = n;
return list-insertNode(list, (struct node *)newNode);
}
/* Assumes that the list contains only int nodes. */
int list-containsInt(struct list *list, int n) {
struct intNode2 *current = (intNode2 *)list->head;
while (current) {
if(current->data == n) {
return true;
}
current = current->next;
}
return false;
}
你当然可以free
一个列表而不知道它有哪种节点:
void list-free(struct list *list) {
struct node *current = list->head;
struct node *next;
while(current) {
next = current->next;
free(current);
current = next;
}
}
PS。我写这篇文章的时候有点晚了(早上很早,但我还没有睡觉)。所以请随意编辑这个问题,以便更清楚。
答案 0 :(得分:5)
因为有关数据结构的教科书主要是为了向初学者传授概念。这种“优化”只会给初学者耳朵带来很多噪音。这是你放学后的知识,将你与其他人分开......
答案 1 :(得分:1)
我不知道其他任何人,但我只是这样做,当我想将数据部分写入文件时,我只是写了结尾处的指针(包括prev
指针。这是一个双重链表。)
我很少有一个链表,其中每个节点的类型可能不同,而且在向初学者教授列表和其他抽象数据类型的概念时几乎肯定不会。
答案 2 :(得分:1)
将指针放在末尾的优点是节点和有效负载具有相同的地址。现在看来这似乎不是什么好处,但回想一下ANSI C之前。是的,我说的是编译器甚至没有尝试检查指针数据类型的时候。
当你想将有效负载传递给一个函数时,你可以只传递指针,节省几个字节的输入(和宝贵的磁盘空间!)。
答案 3 :(得分:0)
某些链接列表将Next指针放在结构的末尾,有些则没有。我并不认为这是一个问题。
说实话,我不确定我是否遇到过C中的案例,其中链表维护了不同的节点类型。但是,如果你需要这样做,可以使用你所描述的东西。
请注意,今天大多数C程序员都使用C ++,它允许您使用继承来完成同样的事情。使用继承,将Next成员放在类中的位置无关紧要。