如何在javascript中递归反转链表?

时间:2015-07-08 20:48:22

标签: javascript recursion data-structures linked-list

我试图在linkedlist中递归转发Javascript。我试过自己并在网上搜索它。但没有成功。以下是我尝试的代码:

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

任何人都可以帮助我吗?

6 个答案:

答案 0 :(得分: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"作为价值,虽然你可以做到 - 只需从你的"头开始"节点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个浏览器页面。我鼓励你去看看。

答案 1 :(得分: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 ]  

答案 2 :(得分:0)

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

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

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

同样@bbill绝对正确地手动执行代码,一步一步就像Chrome devtools。这确实可以让您深入了解发生的事情。

希望这有帮助。

答案 3 :(得分:0)

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

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

完整的工作解决方案here

答案 4 :(得分:0)

要添加到上述答案中,以下是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 :(得分:0)

您可以尝试以下方法: 我从这里参考: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();