C 链表:使用最后一个指针在末尾插入

C 链表:使用最后一个指针在末尾插入,c,linked-list,C,Linked List,我正在学习链表,想知道我为在链表末尾插入元素而编写的以下程序(基本上是insertaden函数)是否正确 基本思想是*HEAD指向列表的第一个元素,*LAST指向最后一个元素。这节省了遍历列表的最后一个元素然后添加元素的时间和计算 #include<stdio.h> #include<stdlib.h> // Structure for the list element/node struct node { int data; // Stores the data

我正在学习链表,想知道我为在链表末尾插入元素而编写的以下程序(基本上是insertaden函数)是否正确

基本思想是*HEAD指向列表的第一个元素,*LAST指向最后一个元素。这节省了遍历列表的最后一个元素然后添加元素的时间和计算

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

// Structure for the list element/node
struct node 
{
 int data; // Stores the data
 struct node *next; // Points to the next element in the list.
};

int InsertAtEnd(struct node **, struct node **, int); /*Declaration of the function which 
                                                    inserts elements at the end.*/

int main()
{
 struct node *HEAD=NULL; //Points to the first element in the list.
 struct node *LAST=NULL; //Points to the last element in the list.
 int i=1;

 for(i=1;i<11;i++)
   {
     InsertAtEnd(&HEAD,&LAST,i);
   }
 }

// Function to insert element at the end.
int InsertAtEnd(struct node **headref,struct node **lastref,int i)
 {
   struct node *newnode=malloc(sizeof(struct node)); /*Allocates memory for the newnode 
                                                     and store the address in pointer 
                                                     newnode*/
   newnode->data=i; // Assign value to the data variable of the newnode.
   newnode->next=NULL; // Assign NULL to the next pointer of the newnode.

   if(*headref==NULL) //Checks if the list is empty.
    {
      *headref=newnode; // Places the address of the new node in HEAD pointer.
      *lastref=newnode; // Places the address of the new node in LAST pointer.

       return 0; //Exit function
    }

/* If the list is not empty, then make the next pointer of the present last node point to the new node*/

   (*lastref)->next=newnode;

   *lastref=(*lastref)->next; // Increment LAST to point to the new last node.

    return 0;
}
#包括
#包括
//列表元素/节点的结构
结构节点
{
int data;//存储数据
struct node*next;//指向列表中的下一个元素。
};
int insertaden(结构节点**,结构节点**,int)/*功能声明
在末尾插入元素*/
int main()
{
struct node*HEAD=NULL;//指向列表中的第一个元素。
struct node*LAST=NULL;//指向列表中的最后一个元素。
int i=1;
for(i=1;idata=i;//为newnode的数据变量赋值。
newnode->next=NULL;//将NULL分配给newnode的下一个指针。
if(*headref==NULL)//检查列表是否为空。
{
*headref=newnode;//将新节点的地址放在HEAD指针中。
*lastref=newnode;//将新节点的地址放在最后一个指针中。
返回0;//退出函数
}
/*如果列表不为空,则使当前最后一个节点的下一个指针指向新节点*/
(*lastref)->next=newnode;
*lastref=(*lastref)->next;//递增LAST以指向新的最后一个节点。
返回0;
}
我想特别问的问题是:

a) 上述用于在末尾添加元素(即InsertAttend函数)的代码是否正确?(注意:我在我的机器上测试了它,它按预期工作。但我仍然想从你们那里确认)

b) 代码(插入函数)是否有效

c) 如果我尝试制作更长的列表,代码(InsertAtEnd函数)的效率是否会受到影响


d) 是否有更有效、更简单的算法在最后插入元素?你能告诉我它们在哪里吗?

这段代码与列表的大小无关(除了空的特殊情况),也就是说,它是一个常数时间算法。所以很难想出一个更有效的插入算法

但是,唯一可以改进的部分是使用
malloc()
。看起来您正在单独分配每个节点。如果您准备使用比您真正需要的更多内存,您可能会考虑同时分配多个节点,因此对<代码> MALROCK()/<代码>的调用更少。因此,当您尝试创建节点时,它首先检查池中是否还有空闲节点。如果是,那么只需连接指针即可。如果没有,您将需要分配一个新池。显然,这需要更复杂的代码。

a)这似乎是正确的

b) 是的。您可以使函数return为void,因为您不需要该int返回值

c) 不。换句话说,执行时间是恒定的

d) malloc需要时间。您可以使用缓冲技术,提前malloc一块节点,然后从缓冲区中获取下一个节点。当缓冲区变为empy时,分配另一个块。区块维度可以使用统计算法进行配置,甚至(复杂)计算

此外,您不检查malloc的NULL返回,但它可能会失败

a)不检查malloc的结果。在内存不足的情况下,它可能返回NULL,从而导致崩溃。我相信算法的其余部分是正确的

B+C+D)效率很高;插入最后一个元素所需的时间是恒定的,这是O(1)Menning。事实上,我想不出一个更简单、更有效的算法。

a)是的,它应该可以工作(假设您的列表从未损坏,并且具有headref!=NULL但lastref==NULL)

如果返回值始终为0,那么返回值的意义是什么

b) 当然,除了为每个节点分配内存之外。这是您的设计决策,但不同的解决方案将更加复杂,超出本练习的范围

至于函数本身——链表的优点是它们是O(1)。现在删除一个元素是另一回事,除非你有一个双链接列表,否则删除速度会慢一些

c) 不,是O(1)。如您所见,没有循环或任何相关内容,无论列表中有5个或5000个元素,您都会执行完全相同的步骤

d) 通过使用sentinel,即使用虚拟节点代替headref,可以避免检查列表是否为空。您只需在内存中的某个位置放置一个节点,并将lastref设置为该节点。现在,您不需要将空列表作为特例来处理。

a)我认为您的代码在某种意义上是正确的,它完成了您期望它完成的任务。您可能需要检查
malloc
的返回值

b) 我认为你的代码也很有效。我唯一能想到的是下一行
*lastref=(*lastref)->next我将替换为
*lastref=newnode。但我认为几乎每个编译器都会自动对其进行优化

c) 您的方法具有常量runtime(O(1)),因此添加到更大的列表不会更改runtime。但是,如果元素连续存储在内存中,遍历列表的速度可能会更快

d) 我不认为
insertAtEnd
可以更快地实现。您可以尝试在内存中连续存储元素,并检查是否返回
malloc

我个人在实施这些东西时所做的唯一一件事是:

  • 为整个链表数据结构创建自己的结构(持有
    大小
    -和
    最后一个元素
    -指针)

  • 我不会将列表限制为包含整数,而是包含任意元素(因此
    void*
    s)

int InsertAtEnd(struct node ***, int); /*Declaration of the function which inserts elements at the end.*/ struct node *HEAD=NULL; //Points to the first element in the list. struct node **LAST=&HEAD; //Points to the last (NULL) *pointer* in the list. // Function to insert element at the end. int InsertAtEnd(struct node ***lastrefref,int i) // no need to pass HEAD { struct node *newnode=malloc(sizeof(struct node)); /*Allocates memory for the newnode and store the address in pointer newnode*/ // And here we should be checking for errors... newnode->data=i; // Assign value to the data variable of the newnode. newnode->next=NULL; // Assign NULL to the next pointer of the newnode. **lastrefref = newnode; // Put new element at end of list *lastrefref=&(newnode->next); // Update last pointer location }
struct <anything> ***