与链接列表撤销相关的想法

时间:2012-04-22 18:10:14

标签: performance algorithm data-structures linked-list

因为我听说在采访中被问了很多,所以有些事情一直困扰着我。 反转一个单链表。问题是我检查了实现,我想知道我在想的是否可以应用

|data1|->|data2|->|data3|->|data4|->|data5|

此结构是链表的初始条件。我在想什么时候我们想逆转就不会这样;

|data5|->|data4|->|data3|->|data2|->|data1|

因此,在一个将花费 O(n)运行时间的循环中,只需用节点#5 节点#1 的数据>,节点#2 节点#4 可以完成这项工作吗?

7 个答案:

答案 0 :(得分:4)

   typedef struct Node
   {
     char data;
     struct Node* next;
   } Node; 


    Node* reverse(Node* root)
    {
       Node* new_root = 0;
       while (root)
       {
           Node* next = root->next;
           root->next = new_root;
           new_root = root;
           root = next;
       }
       return new_root; 
    } 


int main()
{
   Node d = { 'd', 0 };
   Node c = { 'c', &d };
   Node b = { 'b', &c };
   Node a = { 'a', &b };
   Node* root = &a; 
   root = reverse(root);
   return 0;
 }

答案 1 :(得分:3)

您的算法要求我们从节点5开始,然后从那里开始向后工作。但它是一个单独的链表 - 这意味着我们没有“后退”指针。即使我们有O(1)访问tail节点,从节点5到节点4也将是一个O(N)操作,因为我们需要从节点1再次启动才能到达节点4。

因此,对于单链表来说,这不是一个好算法。对于双向链表,我们可以访问后向指针,它会给我们O(N)的复杂性。 (您的算法也不完全与类型无关,因为它要求存储在每个节点中的数据是可复制的或可移动的,以便我们可以交换它。​​)

答案 2 :(得分:3)

您想要编写方法insert_head并重复使用它。因此,您从原始列表的头部开始,并附加到新链接列表的头部。因此,原始列表的尾部成为新列表的头部。

这是另一种方法:走原始列表,将节点推入堆栈。然后,当您进入新列表时弹出并insert_at_tail

您的算法失败,因为您无法在O(1)时间内在单个链接列表中向后走。要按照你的建议去做,每个节点都是O(n)(你每次都要从头部走到你每次获取前一个节点的位置),从而将整个列表反转为{{1} }。

答案 3 :(得分:2)

当被问到时,通常的限制是将列表反转到位,这样除了三个或更少的变量之外你不会使用任何额外的空间。这就是为什么这个问题不是微不足道的原因。因此,您既不能使用递归,也不能使用自己的显式堆栈,也不能使用其他LL(当您走原路时,您将使用LL.addToHead(el)。)

所以这里有一个很好的答案O(n)时间和O(1)空间:

Node reverse(Node n){//n is head; you may also get the LL
    if(null == n || null == n.next)
       return n;
    Node a=n, b=a.next, c=b.next;
    a.next=null; b.next=a;
    while(null != c){
       a=c;
       c=c.next;
       a.next=b;
       b=a;
    }
    return b;
}

顺便说一下,你提出的解决方案是O(n ^ 2)

答案 4 :(得分:1)

有一个使用O(n)空间的O(n)解决方案。考虑避免在线性时间内访问元素的问题。

答案 5 :(得分:1)

如果列表是异构的,或者节点的大小不相等,那么这个想法将会失败。举个例子:

struct base {
   int type;
   struct base *next;
   };

struct small {
   struct base common;
   int value;
   };

struct big {
    struct base common;
    char payload[12345];
    };

答案 6 :(得分:1)

根据最终输出中的位置,从节点反转数据不是一个好主意,原因有两个:

  1. 如果列表大小
  2. ,它不是一个可行的解决方案
  3. 以任意方式访问节点的时间更长。该方法比O(n)花费更多时间。
  4. 考虑这个伪代码:

    reverse_list(node *head) 
    {
      node *temp;
      *temp = head -> next;
      head-> next = NULL;
    
      while (temp != NULL) {
         swap_nodes(head, temp->next);  // this aint copying data from nodes, actual nodes are 
         swap_nodes(head, temp);        // swapped based on its and parents' next pointers  
      }
    }