【问题标题】:a c program to add two singly linked lists, of unequal lengths containing single digited numbers in all of their nodes一个 c 程序,用于添加两个长度不等的单向链表,在它们的所有节点中都包含单个数字
【发布时间】:2011-04-16 17:15:55
【问题描述】:

我把这个作为面试问题。我得到了 2 个不等长的链表,每个节点都包含一个数字。我被要求建立一个包含两个链表之和的第三个链表,再次以节点中的 1 位数字的形式。 前任: 链表 1 是 4-7-9-6 链表 2 是 5-7 那么第三个链表将是 4-8-5-3 有人可以建议我一种有效的算法,在空间复杂度方面的妥协最小吗?(我不希望算法数据涉及多次反转列表)。

【问题讨论】:

  • 4-7-9-6和5-7之和等于4-8-5-3怎么算?
  • 我不知道。我最初考虑这可能是对齐列表的末端并取总和 mod 10 的情况,但结果将是 4-7-4-3。
  • 我猜它们只是整数,表示为数字列表,所以它是 4796 + 57 = 4853 ?
  • 是的,保罗。这正是我的意思。很抱歉不清楚。
  • 我猜测面试问题的正确答案是“列表是单链还是双链?”

标签: c data-structures


【解决方案1】:

这很简单。假设最左边的节点是最高有效位。对齐两个列表,添加和传播进位。如果需要,返回后创建一个新节点..

#include <stdio.h>


struct list {
 int val;
 struct list * next;
};


    int listadd (struct list  *l1, struct list *l2) {

        if ((l1 == NULL) || (l2 == NULL))
                return;
        int carry = 0;

        if ((l1->next == NULL) && (l2->next != NULL)) {
                carry += listadd (l1, l2->next) + l2->val;
                return carry;
        }

        else if ((l1->next != NULL) && (l2->next == NULL)) {
                carry +=listadd (l1->next, l2);
                l1->val = l1->val + carry;
        }
        else if ((l1->next != NULL) && (l2->next != NULL)) {
                carry += listadd (l1->next, l2->next);
        }
        else if ((l1->next == NULL) && (l2->next == NULL)) {
                l1->val = l1->val + l2->val;
                carry = l1->val/10;
                l1->val = l1->val%10;
                return carry;
        }

        carry = l1->val/10;
        l1->val = l1->val%10;
        return carry;
}

struct list * createnode (int val) {
  struct list * temp = (struct list *) malloc (sizeof(struct list));
  temp->val = val;
  temp->next = NULL;
  return temp;
}

int main() {
   int carry = 0;
   struct list *l1 = createnode(1);
l1->next = createnode(2);

   struct list *l2 = createnode(7);
   l2->next = createnode(8);

  carry = listadd(l1,l2);

  if (carry != 0) {
        struct list * temp = createnode(carry);
        temp->next = l1;
        l1 = temp;
  }


   while (l1!= NULL) {
        printf ("%d", l1->val);
        l1=l1->next;
   }
}

【讨论】:

    【解决方案2】:

    看看这段代码:

    node *add_two_linkedlist(node *head1,node *head2)
    {
        int i,j,temp;
        node *p,*n;
        p=head1;
        n=head2;
        i=count(head1);
        j=count(head2);
    
        if(i>j)
        {
            while(j!=0)
            {
                p->data=p->data+n->data;
                if(p->data>10)
                {
                    temp=(p->data)/10;
                    p->data=(p->data)%10;
                    p=p->next;
                    n=n->next;
                    p=p->data+temp;
                    j--;
                }               
            }   
            return head1;
        }
    
        if(j>i)
        {
            while(i!=0)
            {
                n->data=p->data+n->data;
                if(n->data>10)
                {
                    temp=(n->data)/10;
                    n->data=(n->data)%10;
                    n=n->next;
                    p=p->next;
                    n=n->data+temp;
                    i--;
                }               
            }   
            return head2;
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果列表以相反的顺序存储数字,解决方案可能会简单得多。

      尽管如此,在给定的约束下,这是一种方法。

      1. 找到两个列表的nthToLast数字,以n = 0开头,
      2. 用数字的总和创建一个node
      3. 更新(正在运行的)carry
      4. inserthead ofresult 列表中新创建的节点

      以下是(未经测试的)C 代码。

      typedef struct DigitNode_ {
          int digit;
          struct DigitNode_ * next;
      } DigitNode;
      
      /*  Returns the n-th element from the end of the SLL;
          if no such element exists, then return NULL.
          See: https://stackoverflow.com/questions/2598348/
      */
      extern DigitNode * nthNodeFromTail( DigitNode * listHead, size_t n );
      
      /*  Push pNode in the front, i.e. as the new head of the list */
      extern void pushFront( DigitNode ** pListHead, DigitNode * pNode );
      
      /*  Create new list as sum of a and b, and return the head of the new list.
              a -> 4 -> 7 -> 9 -> 6 -> NULL
              b ->           5 -> 7 -> NULL
          results in
              c -> 4 -> 8 -> 5 -> 3 -> NULL
      */
      DigitNode * sumDigitLists( DigitNode * a, DigitNode * b ) {
      
          DigitNode * c = 0;
          int carry = 0;
      
          /* i is the position of a node from the tail of the list, backwards */
          for( size_t i = 0; /* see 'break' inside */; i++ ) {
      
              const DigitNode * const ithNodeA = nthNodeFromTail( a, i );
              const DigitNode * const ithNodeB = nthNodeFromTail( b, i );
      
              /* Stop when processing of both lists are finished */
              if( !ithNodeA && !ithNodeB ) {
                  break;
              }
      
              const int ithDigitA = ithNodeA ? ithNodeA->digit : 0;
              const int ithDigitB = ithNodeB ? ithNodeB->digit : 0;
      
              assert( (0 <= ithDigitA) && (ithDigitA <= 9) );
              assert( (0 <= ithDigitB) && (ithDigitB <= 9) );
      
              const int conceptualIthDigitC = carry + ithDigitA + ithDigitB;
              const int ithDigitC = conceptualIthDigitC % 10;
              carry = conceptualIthDigitC / 10;
      
              DigitNode ithNodeC = { ithDigitC, NULL };
              pushFront( &c, &ithNodeC ); 
          }
      
          return c;
      }
      

      【讨论】:

        【解决方案4】:

        如果列表是双向链接的,这很容易:

        1. 遍历两个列表到最后。
        2. 将对应节点的数字相加,保留进位数字。
        3. 在列表 3 中创建节点。
        4. 将一个节点移向列表的开头并重复。

        【讨论】:

          【解决方案5】:
          1. 将每个数字作为其 ASCII 等价物读取到从 0 开始索引的 char 数组中,用于两个列表。
          2. 在两个 char 数组上使用 atoi() 函数(如果您担心长度,可以使用 atol()atoll()
          3. 两个数字相加
          4. 使用 itoa() 函数转换为 char 数组,然后放回新列表中。

          虽然,我承认itoa() 函数不是标准的。

          【讨论】:

          • 这只有在你的链表表示的数字符合机器字时才有效。链表中没有十亿个元素是没有道理的。
          【解决方案6】:
          1. 反转列表 1 和 2
          2. 逐个元素求和(而 保持进位),把 结果是您的列表 3 从头到尾构建

          1. 将列表 1 和 2 转换为整数(例如 int list1AsInt = 0; For each node {list1AsInt *= 10; list1AsInt += valueOfThisNode;}
          2. 对这些整数求和
          3. 将结果转换为链表(例如valueOfBrandNewNode = list3AsInt % 10; list3AsInt /= 10; Add a new node that points to the prev one;

          1. 遍历这两个列表一次以找出 他们的长度。对于这个例子, 假设列表 1 更长 N 个节点。
          2. 创建一个列表 3 来表示总和 没有进位和清单 4 到 代表carry。
          3. 对于列表 1 的前 N ​​个节点, 将这些值复制到列表 3 并制作 列表 4 的值为 0。
          4. 对于列表 1 的剩余节点 和 2,逐个元素求和, 将 sum mod 10 放入列表 3 和 进位清单 4. 通过以下方式跟踪 列表 4 是否全为 0 的布尔值。
          5. 将值为 0 的最后一个节点添加到列表中 4.
          6. 如果列表 4 完全是 0,则完成。 否则,递归到第 2 步, 将清单 3 视为 新的列表 1 和列表 4 作为新的 清单 2. 我们知道 新列表 1 是较大的长度 旧列表 1 和 2 的长度 新名单 2 比这多一个。

          【讨论】:

          • 当列表的长度太长以至于结果整数不能存储为 int 时,第二个解决方案 wudnt 起作用。
          • 除了第一个解决方案还有其他替代方案,我不必颠倒列表吗?
          • 我添加了第三个选项,它不需要反转列表,但它的速度很大程度上取决于需要多少进位。每个进位最多需要额外遍历两个列表。
          • +1 以获得不错的选择。关于#1,值得注意的是,列表反转是可能的。此外,混合是可能的:#2 作为遍历但计数节点,所以溢出可以恢复到#3。也可以简单地将节点读入 std::vector 然后从向量中求和。优化#3:固定长度窗口以加快进位速度,或在当前节点求和点之前保存一个/多个/所有节点* N 个元素,以减少从头部重新扫描。或者添加忽略进位 (9+5=14),然后通过规范化循环将 2(+) 个指针移动到连续节点,一次携带一个节点,直到循环找不到工作。
          猜你喜欢
          • 2018-12-23
          • 2020-06-17
          • 2020-05-18
          • 1970-01-01
          • 2014-08-06
          • 1970-01-01
          • 1970-01-01
          • 2021-11-17
          • 1970-01-01
          相关资源
          最近更新 更多