【问题标题】:How to reverse a linkedlist recursively in javascript?如何在javascript中递归地反转链表?
【发布时间】:2015-07-08 20:48:21
【问题描述】:

我试图在 Javascript 中递归地反转 linkedlist。我已经尝试过自己并搜索网络以找到它。但没有成功。下面是我试过的代码:

var Node = (function(){
    function Node(val){
        this.elem = val;
        this.next = null;
    }
    return Node;
})();

var SLinkedlist = (function(){
    function SLinkedlist(){
        this.head = new Node("head");
    }
    return SLinkedlist;
})();

SLinkedlist.prototype.find = function(val){
    var current = this.head;
    while(current !== null && current.elem !== val){
        current = current.next;
    }
    return current;
}

SLinkedlist.prototype.insert = function(newVal, val){
    var current = this.find(val);
    var newNode = new Node(newVal);
    newNode.next = current.next;
    current.next = newNode;
}
function reverseLinkedList(list, previous){

      //We need to use the the current setting of
      //list.next before we change it. We could save it in a temp variable,
      //or, we could call reverseLinkedList recursively
      console.log(list);
      if(list !== null && list.next !==null){
        reverseLinkedList(list.next, list);
      }
      console.log("after recursion!")
      console.log(list);      
      //Everything after 'list' is now reversed, so we don't need list.next anymore.
      //We passed previous in as an argument, so we can go ahead and set next to that.
      list.next = previous;
     }
reverseLinkedList(list.head, null);

谁能帮帮我?

【问题讨论】:

  • 有什么代码可以显示你的进度吗?
  • 是的!用代码更新了我的问题。递归方法取自一些例子。
  • @Arpit- 感谢格式化。我是新来的。

标签: javascript recursion data-structures linked-list


【解决方案1】:

假设您的列表与此类似:

var list = 
{
  name: "1",
  next: {
    name: "2",
    next: {
      name: "3",
      next: {
        name: "4"
      }
    }
  }
};

console.log("Original list");
var head = list;
while (head != undefined) {
  console.log(head.name);
  head = head.next;
}

渲染

Original list 
1 
2 
3 
4 

你可以像这样使用递归函数来反转它:

head = reverse(list, undefined);

console.log("Reverse list");
while (head != undefined) {
  console.log(head.name);
  head = head.next;
}


function reverse(list, prev) {
  // if this is the last node, switch it with previous and return
  if (list.next == undefined) {
    list.next = prev;
    return list;
  }

  // otherwise, switch it with the reverse of what is next
  var ret = reverse(list.next, list);
  list.next = prev;
  return ret;
}

渲染

Reverse list 
4 
3 
2 
1 

它是如何工作的?它基于以下原则:

Reverse([1 2 3 4]) ==   
[ Reverse([2 3 4]) 1 ] ==  
[ Reverse([3 4]) 2 1 ] ==  
[ 4 3 2 1 ]  

【讨论】:

  • @sklivvz- 非常感谢您的解决方案。我理解它背后的概念,但是我在将递归合并到我当前的链表结构中时遇到了问题。我的链表第一个节点是“head”,它指向第一个数据。例如:var list = { name: "head", next: { name: "1", next: { name: "2", next: { name: "3" } } } };
  • @sklivvz- 请告诉我是否应该将“head”节点作为链接中的第一个元素?谢谢!
  • @alps07 如果你想让“head”在尾部,你可以使用相同的,否则,反转list.next :-)。实验!通常我不会使用“head”元素。
  • @slivvz- 我没有看到单链表有尾指针的地方。我已经实现了用我的数据结构递归地反转链表。但不利的一面是我正在创建全局变量,它是一个新的链表实例来存储反向列表。我返回的列表将最后一个元素作为“头”节点。我会再试一次。您的解决方案更加简洁易懂。我是 javascript 的新手,试图通过解决数据结构算法来学习它。 javascript 作用域使事情变得有点棘手。
【解决方案2】:

这是我的面向对象的递归解决方案。

function Node(value) {
    this.value = value;
    this.next = null;
}

function SLinkedList(node) {
    if (node) {
        this.head = node;
    } else {
        this.head = null;
    }
}

SLinkedList.prototype.prepend = function(node) {
    node.next = this.head;
    this.head = node;
}

SLinkedList.prototype.print = function() {
    var arr = [];
    var current = this.head;
    while (current !== null) {
        arr.push(current.value);
        current = current.next;
    }
    alert(arr.join(' '));
}

SLinkedList.prototype.reverse = function() {
    if (this.head === null || this.head.next === null) {
        return;
    }
    var first = this.head;
    var rest = new SLinkedList(this.head.next);
    rest.reverse();
    first.next.next = first;
    first.next = null;
    this.head = rest.head;
}

var list = new SLinkedList();
list.prepend(new Node(4));
list.prepend(new Node(3));
list.prepend(new Node(2));
list.prepend(new Node(1));
list.print();

list.reverse();
list.print();

JSFiddle:http://jsfiddle.net/5c9gtstk/1/

为您的代码提供一些提示,这些提示并非 100% 错误,但我认为会有所帮助,主要用于更新您的链接列表:

  • 第一个节点以“head”为值并不常见,但您可以这样做 - 只需从“head”节点的 next 开始。
  • console.log(list) 行并不能说明发生了什么,因为数据结构只包含elemnext,这就是我编写打印函数的原因。这可以帮助您进行调试。
  • 您的插入函数也是非典型的,但这在此算法中并不是真正的问题 :)

http://www.thatjsdude.com/interview/linkedList.html#singlyLinkedList 有一个简单明了的说明,说明如何做到这一点。

至于你的反向算法,你几乎有正确的想法。我可以在这里给出的主要提示是画出递归调用中发生的事情!慢慢地手动逐步执行您的代码。这需要几秒钟的时间,但会让一切都 100% 清晰。

具体来说,是 list.next = previous; 在反转列表的其余部分后需要做的所有事情吗?您还需要更改其他一些指针。

其他人,包括这里的答案,给出了比我能给出的更好的解释。同样,图表是关键。 http://www.geeksforgeeks.org/write-a-function-to-reverse-the-nodes-of-a-linked-list/ 有一个很好的简短解释 - 跳到关于递归方法的部分。

我上面的 2 个链接各有 1 个浏览器页面。我鼓励你看看。

【讨论】:

  • @bbill- 非常感谢递归解决方案。我需要更改我的单链表构造函数,就像你在这里一样。
  • 当然。您的代码非常接近,您只是缺少一些细节。我会更新我的答案。
  • 另外,实际上,您不必更改构造函数。由于您的反向方法作用于节点。
  • @bbill-感谢 bbill 的详细解释。感谢您花时间解释它。我会听从你的建议。
【解决方案3】:

要反转一个链表,我们可以使用简单的递归方法。将链表的头部传递给下面的方法。

reverse( head ) {
    if( head ) {
        reverse( head.next );
        console.log( head.data );
    }
}

完整的工作解决方案here

【讨论】:

  • 这个解决方案不是链表的反转。它只是以相反的顺序打印链表。
【解决方案4】:

为了补充上述答案,这里是 ES6/JavaScript 中的 LIFO(后进先出)链表实现,具有递归和 就地 反转列表的功能(没有创建一个新列表):

class Element {
  constructor(value) {
    this.value = value;
    this.next = null;
  }
}

class List {
  constructor(arr) {
    this.head = null;
    if (arr) {
      arr.forEach(el => this.add(new Element(el)));
    }
  }

  get length() {
    return this.head ? this.countElements(1, this.head) : 0;
  }

  add(el) {
    const element = el;
    element.next = this.head;
    this.head = element;
  }

  countElements(count, element) {
    return element.next ? this.countElements(count + 1, element.next) : count;
  }

  toArray(arr = [], element = this.head) {
    arr.push(element.value);
    return element && element.next ? this.toArray(arr, element.next) : arr;
  }
  
  reverse(prev = null) {
    if (this.head.next) {
      const current = this.head;
      this.head = this.head.next;
      current.next = prev;
      return this.reverse(current);
    }
    this.head.next = prev;
    return this;
  }
}

const list = new List([1, 2, 3, 4, 5, 6, 7 , 8 ,9 , 10]);
console.log(list.toArray().toString());  // returns LIFO list as array (last list el = first array el)
console.log(list.reverse().toArray().toString()); // returns LIFO list reversed as array(first first)

【讨论】:

  • 恕我直言,这是最好且易于理解的解决方案。
【解决方案5】:

这两个现有答案为该问题提供了很好的解决方案,但我认为了解您的代码为什么不起作用可能会有所帮助。在 javascript 中,当执行递归调用时,会创建一个具有自己作用域的新闭包。所以在最后的调用中,“tail”所在的那个,引用它的list变量实际上是一个局部变量,它覆盖了环境中现有的list。因此,当函数退出时,如果不返回对这个本地 list 的引用,它就会丢失。

其他两个答案的作用是返回该值并将其存储在临时变量中(代码中的 cmets 实际上没有帮助..您需要将尾部存储在某处,否则您将丢失它!)

您可能知道,如果所有这些都在您的反向函数中处理,您就不会污染全局范围。

@bbill 也完全正确地手动执行代码,在 Chrome 开发工具中一次一步执行。这确实可以很好地了解正在发生的事情。

希望这会有所帮助。

【讨论】:

    【解决方案6】:

    你可以试试这个方法: 我从这里参考:https://www.youtube.com/watch?v=KYH83T4q6Vs 基本上,一旦我们递归地到达最后一个节点,我们就会将指针从最后一个节点更改为指向上一个节点。

    class LinkedList {
        constructor() {
            this.head = null;
            this.size = 0;
        }
    
        reverseLLRec(node) {
            if (!node.next) {
                this.head = node;
                return;
            }
            this.reverseLLRec(node.next);
            const curr = node.next;
            curr.next = node;
            node.next = null;
        }
    }
    
    
    

    class Node {
        constructor(val) {
            this.val = val;
            this.next = null;
        }
    }
    class LinkedList {
        constructor() {
            this.head = null;
            this.size = 0;
        }
    
        reverseLLRec(node) {
            if (!node.next) {
                this.head = node;
                return;
            }
            this.reverseLLRec(node.next);
            const curr = node.next;
            curr.next = node;
            node.next = null;
        }
    
        insertAtLast(data) {
            const node = new Node(data);
            if (this.head === null) {
                this.head = node;
            } else {
                let current = this.head;
                while (current.next) {
                    current = current.next;
                }
                current.next = node;
            }
        }
    
        printList() {
            let curr = this.head;
            let list = [];
            while (curr) {
                list.push(curr.val);
                curr = curr.next;
            }
            console.log(list);
        }
    }
    
    const ll = new LinkedList();
    ll.insertAtLast(1);
    ll.insertAtLast(2);
    ll.insertAtLast(3);
    ll.insertAtLast(4);
    ll.insertAtLast(5);
    console.log('Before reverse:');
    ll.printList();
    ll.reverseLLRec(ll.head);
    console.log('After reverse:');
    ll.printList();

    【讨论】:

      猜你喜欢
      • 2020-10-02
      • 1970-01-01
      • 2012-12-14
      • 1970-01-01
      • 2012-11-05
      • 2010-09-26
      • 1970-01-01
      • 2017-06-22
      相关资源
      最近更新 更多