【问题标题】:Insert node at the end of linked list在链表末尾插入节点
【发布时间】:2015-12-14 01:33:44
【问题描述】:

这类问题有一个简单的迭代解决方案。

Node Insert(Node head,int data) {
    Node newNode = new Node();
    newNode.data = data;
    if (head == null) {
        return newNode;
    }
    Node current = head; 
    while (current.next != null) {
        current = current.next;
    }
    current.next = newNode;
    return head;
}

它工作得很好。但是我想学习递归并以这种角度看待事物。因此我想出了下面的解决方案,它看起来很优雅,但我不得不承认它只是 直觉 并且给定的代码有效。我想开发一个使用递归的心智模型,或者至少以某种方式来验证我的代码是否可以正常工作。如何从理论上验证以下解决方案应该有效。

递归版本

Node Insert(Node head,int data) {
    // Base case.
    if (head == null) {
        Node newNode = new Node();
        newNode.data = data;
        return newNode;
    }
    // Smaller problem instance.
    head.next = Insert(head.next, data);
    return head;
}

【问题讨论】:

    标签: java recursion data-structures recursive-datastructures


    【解决方案1】:

    递归解决方案通常必须遵守以下规则:

    1. 它必须区分一般案例和基本案例。然后,它必须包含某种类型的代码分叉(通常是一个if)到两个代码块:基本块和通用块。
    2. 基本块必须立即返回响应(非递归)。
    3. 通用块必须重新调用相同的函数(递归),但不是使用相同的参数值(这将产生无限递归),而是使用向前到基础的值案例。

    当然,这是一个简单的递归模型,在实践中可能更复杂(多个基本情况、两个函数之间的递归等)。

    如果我们根据这些规则从理论上分析您的提案,我们可以看到它符合所有这些规则。

    【讨论】:

      【解决方案2】:

      我会将代码再进一步,并删除多个退出点。这使您可以推断对列表的影响以及返回哪个节点。

      Node appendRecursive(Node head, int data) {
          // By default return the same list we were given.
          Node list = head;
          if (list == null) {
              // At end of list or list is empty.
              list = new Node();
              list.data = data;
          } else {
              // Recurse.
              head.next = appendRecursive(head.next, data);
          }
          return list;
      }
      

      就推理而言,您通常需要使用归纳法。

      • 如果列表为空 (list == null),则创建一个新节点并成为您的列表。
      • 如果列表不为空,则新列表必须是附加了新数据的列表。

      鉴于上述情况,可以推断在所有情况下这都会正常运行,因为列表要么为空,要么不是。

      在列表上使用递归通常被认为是低效且笨重的,因为迭代算法更适合线性结构。更好的练习是编写自己的Tree 结构,因为树非常适合递归算法。您会发现在树上递归执行所需的功能通常更容易、更优雅。

      static class Tree {
      
          Node head = null;
      
          class Node {
      
              Node left;
              Node right;
              int data;
      
              private Node(int data) {
                  this.data = data;
              }
          }
      
          void insert(int data) {
              head = insert(head, data);
          }
      
          private Node insert(Node node, int data) {
              if (node == null) {
                  // Empty tree becomes just the node.
                  return new Node(data);
              } else {
                  // Pick the correct branch to add this data to.
                  if (data < node.data) {
                      node.left = insert(node.left, data);
                  } else {
                      node.right = insert(node.right, data);
                  }
              }
              return node;
          }
      
          private CharSequence toString(Node n) {
              StringBuilder s = new StringBuilder();
              if (n != null) {
                  // First print the tree on the left.
                  if (n.left != null) {
                      s.append(toString(n.left)).append(",");
                  }
                  // Then the data in this node.
                  s.append(n.data);
                  // Then the tree on the right.
                  if (n.right != null) {
                      s.append(",").append(toString(n.right));
                  }
              }
              return s;
          }
      
          @Override
          public String toString() {
              // Even toString is recursive.
              StringBuilder s = new StringBuilder("{");
              s.append(toString(head));
              return s.append("}").toString();
          }
      }
      
      public void test() {
          Tree tree = new Tree();
          for (int i : new int[]{6, 5, 4, 3, 2, 1}) {
              tree.insert(i);
          }
          System.out.println(tree);
      }
      

      请注意,在 toString 方法中判断在何处添加“,”是多么简单 - 打印列表时这是一个众所周知的笨拙问题。

      【讨论】:

        【解决方案3】:
        Node Insert(Node head,int data) {
            if (head == null) {
                head  = new Node();
                head.data = data;
            }
            else{
            head.next = Insert(head.next, data);
            }
            return head;
        }
        

        假设你有 5,3,2,1 并且你想添加 4 然后:- insert(node(5),int 4)

        if( node(5) == null ) 否,然后是 head.next = node(5).next,即 node(3) 并致电 Insert(node(3),4)

        if( node(3) == null ) 否,然后是 head.next = node(3).next,即 node(2) 并致电 Insert(node(2),4)

        if( node(2) == null ) 否,然后是 head.next = node(2).next,即 node(1) 并致电 Insert(node(1),4)

        if( node(1) == null ) no then head.next = node(1).next which is null "因为后面没有元素 node(1) " 并调用Insert(node(1).next,4)

        (node(1).next) == null是然后设置head = new node ();并在最后返回头设置数据head = new node ();

        【讨论】: