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

时间:2010-08-24 12:30:19

标签: c linked-list

我正在学习链接列表,并且想知道我在列表末尾插入元素所做的以下程序(基本上是InsertAtEnd函数)是否正确。

基本思想是* 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;
}

我想特别提出的问题是:

a)上面添加元素的代码(即InsertAtEnd函数)是否正确? (注意:我在我的机器上进行了测试,它按预期工作。但我仍想向您确认)

b)代码(InsertAtEnd函数)是否有效?

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

d)是否有更有效和简单的算法来插入元素?你能指导我吗?

6 个答案:

答案 0 :(得分:4)

a)看来是正确的

b)确实如此。您可以使函数返回void,因为您不需要该int返回值

c)否。换句话说,执行时间是不变的。

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

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

答案 1 :(得分:2)

此代码与列表的大小不变(除了空的特殊情况),即它是一个恒定时间算法。因此,提出更有效的插入算法可能非常困难。

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

答案 2 :(得分:2)

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

B + C + D)效率很高; O(1)指出插入LAST元素所花费的时间是恒定的。事实上,我想不出更简单,更有效的算法。

答案 3 :(得分:2)

a)是的,它应该有效(假设你的列表永远不会被破坏并且有headref!= NULL但是lastref == NULL)。

如果返回值始终为0,那么它的重点是什么?

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

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

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

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

答案 4 :(得分:1)

a)我认为你的代码是正确的,因为它完成了你期望它做的事情。您可能想要检查malloc的返回值。

b)我认为你的代码也很有效率。我唯一能想到的是*lastref=(*lastref)->next;*lastref=newnode;,我将用insertAtEnd代替。但我认为几乎每个编译器都会自动优化它。

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

d)我认为malloc不能更快地实施。您可以尝试将元素连续存储在内存中,并检查size的返回值。

我个人在实施这些内容时唯一能做的就是:

  • 为整个链表数据结构创建一个自己的结构(持有headlastElement - 和void* - 指针)

  • 我不会限制列表保持整数而是任意元素(因此{{1}} s)

答案 5 :(得分:1)

抱歉,这不回答问题,但我认为您可能会觉得它很有用。我略微改变了方法:

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> ***

开始变得愚蠢,记住。