简体   繁体   English

在链表的末尾插入节点

[英]Insert node at the end of linked list

There is a simple iterative solution for these kind of problems. 对于这类问题,有一个简单的迭代解决方案。

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;
}

It works perfectly fine. 它工作得很好。 But I want to learn recursion and see the things with that perspective. 但我想学习递归并用这种观点看待事物。 Hence I came up with the below solution, it seems elegant but I have to admit that it was just intuition and the given code worked. 因此我想出了下面的解决方案,看起来很优雅,但我不得不承认这只是直觉和给定的代码有效。 I want to develop a mental model for working with recursion or at least some way to verify that my code will work fine. 我想开发一个用于递归的心理模型,或者至少某种方式来验证我的代码是否正常工作。 How to verify theoretically that the below solution should work. 如何从理论上验证以下解决方案是否有效。

Recursive version 递归版

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;
}

A recursive solution generally must obey these rules: 递归解决方案通常必须遵守以下规则:

  1. It has to distinguish between a general case and a base case. 它必须区分一般情况和基本情况。 Then, it has to contain some type of code bifurcation (typically one if ) to two blocks of code: the base block and the general block. 然后,它必须包含某些类型的代码分叉(通常是一个if )到两个代码块:基本块和通用块。
  2. The base block has to return an immediate response (not recursive). 基块必须返回立即响应(不是递归)。
  3. The general block has to re-invoke the same function (recursively), but not with the same arguments values (which will produce infinite recursion), but with values that are getting forward to the base case. 一般块必须重新调用相同的函数(递归),但不能使用相同的参数值(这将产生无限递归),但是具有向基本情况的值。

Of corse, this is a simple recursion model, which in practice could be more complex (more than one base case, recursion between two functions, etc). 对于corse,这是一个简单的递归模型,在实践中可能更复杂(多个基本情况,两个函数之间的递归等)。

If we analyze theoretically your proposal according to these rules, we can see it meets all of them. 如果我们根据这些规则在理论上分析您的提案,我们可以看到它符合所有这些规则。

I would take the code just a little further and remove the multiple exit points. 我会稍微采用代码并删除多个退出点。 This allows you to reason about both the effect on the list and which node mut be returned. 这允许您推断列表上的效果和返回的节点mut。

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;
}

As far as reasoning goes you generally need to use induction. 至于推理,你通常需要使用归纳法。

  • If the list is empty ( list == null ) then create a new node and that becomes your list. 如果列表为空( list == null ),则创建一个新节点,该节点将成为您的列表。
  • If the list is not empty, your new list must be the list with the new data appended to it. 如果列表不为空,则新列表必须是附加了新数据的列表。

Given the above it is possible to reason that in all cases this will function correctly because either the list is empty or it is not. 鉴于上述情况,可以推断在所有情况下,这将正常运行,因为列表为空或不是。

Using recursion on lists is generally considered inefficient and clunky because iterative algorithms fit better with linear structures. 在列表上使用递归通常被认为是低效和笨重的,因为迭代算法更适合线性结构。 A better exercise would be to write your own Tree structure because trees lend themselves very well to recursive algorithms. 更好的练习是编写自己的Tree结构,因为树很适合递归算法。 You will discover that it is often much easier and more elegant to perform the required function recursively on a 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);
}

Notice how simple it is to judge where to add "," in the toString method - a notoriously clunky issue when printing lists. 请注意判断在toString方法中添加“,”的位置是多么简单 - 在打印列表时这是一个众所周知的笨重问题。

Node Insert(Node head,int data) {
    if (head == null) {
        head  = new Node();
        head.data = data;
    }
    else{
    head.next = Insert(head.next, data);
    }
    return head;
}

Assume you have 5,3,2,1 and you want to add 4 then :- insert(node(5),int 4) 假设您有5,3,2,1并且您想要添加4然后: - insert(node(5),int 4)

if( node(5) == null ) no then head.next = node(5).next which is node(3) and call Insert(node(3),4) if( node(5) == null ) no然后head.next = node(5).nextnode(3)并调用Insert(node(3),4)

if( node(3) == null ) no then head.next = node(3).next which is node(2) and call Insert(node(2),4) if( node(3) == null ) no then head.next = node(3).nextnode(2)并调用Insert(node(2),4)

if( node(2) == null ) no then head.next = node(2).next which is node(1) and call Insert(node(1),4) if( node(2) == null ) no then head.next = node(2).nextnode(1)并调用Insert(node(1),4)

if( node(1) == null ) no then head.next = node(1).next which is null " because there are no elements after node(1) " and call Insert(node(1).next,4) if( node(1) == null )否则head.next = node(1).nextnull因为 node(1) 之后没有元素 ”并调用Insert(node(1).next,4)

(node(1).next) == null yes then set head = new node (); (node(1).next) == null yes然后设置head = new node (); and set the data head = new node (); 并设置数据head = new node (); at the end return head 在最后回头

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM