在 JavaScript 中反转链表的策略
Posted
技术标签:
【中文标题】在 JavaScript 中反转链表的策略【英文标题】:strategies to reverse a linked list in JavaScript 【发布时间】:2014-06-10 06:38:15 【问题描述】:我刚刚通过一个简单的面试问题:请反转一个单链表。
虽然我未能及时提供有效的答案来挽救面试,但后来我能够想出一个解决方案。
我的解决方案正确吗?你会如何用 Big-Oh 来分析这个?有没有更有效的方法来反转单链表?
// reverse a linked list
var reverseLinkedList = function(linkedlist)
var node = linkedlist;
var previous = null;
while(node)
// reverse pointer
node.next = previous;
// increment previous to current node
previous = node;
// increment node to next node
if (node.next)
node = node.next
else
node = null;
注意:在搜索类似帖子时,我确实在 javascript 中找到了one example。我想知道我的代码是否可行(没有temp
变量)。谢谢。
【问题讨论】:
【参考方案1】:您的代码存在一些问题。这应该说清楚。
// reverse a linked list
var reverseLinkedList = function(linkedlist)
var node = linkedlist;
var previous = null;
while(node)
// save next or you lose it!!!
var save = node.next;
// reverse pointer
node.next = previous;
// increment previous to current node
previous = node;
// increment node to next node or null at end of list
node = save;
return previous; // Change the list head !!!
linkedlist = reverseLinkedList(linkedlist);
【讨论】:
【参考方案2】:正如ckersch 所提到的,您可以在 O(n) 时间内递归地解决这个问题。问题是,你需要知道递归是内存密集型的,因为函数在调用堆栈中累积,直到它们达到停止条件并开始返回实际的东西。
我解决这个问题的方法是:
const reverse = (head) =>
if (!head || !head.next)
return head;
let temp = reverse(head.next);
head.next.next = head;
head.next = undefined;
return temp;
当 reverse() 到达链表的末尾时,它会抓取最后一个节点作为新的头,并向后引用每个节点。
【讨论】:
【参考方案3】:这将是 O(n) 时间,因为您在每个节点上执行恒定数量的操作。从概念上讲,没有更有效的做事方式(就大 O 表示法而言,可以进行一些代码优化。)
你不能超过 O(n) 的原因是,为了这样做,你需要跳过一些节点。由于您需要修改每个节点,因此这是不可能的。
效率然后归结为一个常数因素。您可以对列表中的每个项目执行的操作越少,您的代码执行速度就越快。
我会这样实现:
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
if(list.next !== null)
reverseLinkedList(list.next, 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, null);
当然,这是递归的,所以在空间方面效率低下,但我喜欢递归代码:)
这也不会返回反向链接列表,但如果这很重要,我们可以相当容易地修改它。
【讨论】:
感谢您的回答和对 Big-O 的分析,非常感谢。【参考方案4】:反转单链表: 输入:1->2->3->4->5->NULL 输出:5->4->3->2->1->NULL
要了解解决方案,我们必须跟踪前一个头和下一个变量 例如在上面的输入 Head = 1 ; next = 2 我们没有 previous 所以假设 previous = null 循环列表直到 head 不为空。反转头部的连接(上一个和下一个)。 下面是代码
var reverseList = function(head)
let previous = null;
while(head !== null)
let next = head.next;
head.next = previous;
previous= head
head = next;
return previous;
;
【讨论】:
【参考方案5】:ES6 解决方案: 只需跟踪反向列表并继续将其添加到 tmp。
const reverseLinkedList = (head) =>
let reversed = null;
while(head)
const tmp = head;
head = head.next;
tmp.next = reversed;
reversed = tmp;
return reversed;
;
console.log(JSON.stringify(reverseLinkedList(
data: 1,
next:
data: 2,
next:
data: 3,
next:
data: 4,
next:
data: 5,
next:
data: 5,
next:
data: 6
)));
【讨论】:
【参考方案6】://O(n) | O(1) wherre n is the number of nodes in the linked list
class Node
constructor(val)
this.val = val;
this.next = null;
function reverseLinkedList(head)
if(!head) return null;
let p1 = head;
let p2 = null;
while(p1)
let temp = p1.next;
p1.next = p2;
p2 = p1;
p1 = temp;
return p2;
const a = new Node(1);
a.next = new Node(2);
a.next.next = new Node(3)
console.log("Current Node",a);
console.log("Reversed List",reverseLinkedList(a))
【讨论】:
【参考方案7】:class LinkedList
constructor ()
this.head = this.tail = null
// add to the end of the list
append (value)
if (!this.tail)
this.head = this.tail = new Node(value)
else
let oldTail = this.head
this.head = new Node(value)
this.head.next = oldhead
reverseList()
//your code here
let currentNode = this.head
this.head = null
while(currentNode)
if (!this.head)
this.head = new Node(currenthead.data)
else
let oldhead = this.head
this.head = new Node(currentNode.data)
this.head.next = oldhead
currentNode = currentNode.next
class Node
constructor (value, next)
this.data = value
this.next = next || null
const list = new LinkedList()
list.append(1)
list.append(2)
list.reverseList()
【讨论】:
您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案8】:由于在链表的开头插入数据会将其他第一个节点推到最后,并且因为它是O(1)
过程。
然后我创建了以下函数reverse()
它基本上在开头插入节点元素,基本上会在最后得到一个反向列表。
下面是一个演示:
class Node
constructor(data, next = null)
this.data = data;
this.next = next;
class LinkedList
constructor()
this.head = null;
this.size = 0;
insertFirst(data = null)
// make new head point to the previous head
this.head = new Node(data, this.head);
this.size ++;
insertLast(data = null) // insert last in the beginning will be the first in the linked list
const node = new Node(data);
// If empty, insert first
if (!this.head) this.insertFirst(data);
else
let current = this.head;
// while next is not null, continue
while (current.next)
current = current.next;
// eventually next is null, we want to set next here to the node we want to add
current.next = node;
this.size ++;
// print linked list
print()
let current = this.head;
let output = "";
while (current) // while current is not null, eventually it will be null
output += current.data + " => ";
current = current.next; // current jumping to the next node
output += "| NULL"; // ending
console.log(output);
return output;
reverse()
if (!this.head) return; // if no head, do nothing
let current = this.head;
const linkedList = new LinkedList(); // create a new linked list
// don't worry, it will be garbage collected once this function ends since it's not a global variable
while (current)
linkedList.insertFirst(current.data); // insert first at the beginning will be the end of the linked list at the end
current = current.next;
// assign current head to the reversed linked list head
this.head = linkedList.head;
const linkedList = new LinkedList();
// fill data as 100 -> 200 -> 300 -> 400
linkedList.insertLast(100);
linkedList.insertLast(200);
linkedList.insertLast(300);
linkedList.insertLast(400);
// To view results
const bodyElement = document.getElementsByTagName("body")[0];
bodyElement.innerhtml = `<p>Original Linked List: <b>$linkedList.print()</b></p>`; // 100 200 300 400
linkedList.reverse();
bodyElement.innerHTML += `<p>Reversed Linked List: <b>$linkedList.print()</b></p>`; // 400 300 200 100
b
color: green;
<body></body>
总的来说,这个reverse()
函数的整个过程就是O(n)
。
希望这对你来说听起来很清楚,如果我错了,请纠正我。
【讨论】:
以上是关于在 JavaScript 中反转链表的策略的主要内容,如果未能解决你的问题,请参考以下文章