【问题标题】:Linked list addition (of total size n) and reversion of last k nods at order of n链表添加(总大小为 n)和最后 k 个节点以 n 的顺序反转
【发布时间】:2020-04-08 03:53:48
【问题描述】:

我正在关注这个挑战:

给出的是数字 mp,它们都可能大到 250000。接下来的 m 行有以下命令之一:

  • APPEND y,它将y 添加到我们列表(队列)的末尾
  • ROTATE,反转列表的最后一个元素 p。如果列表的元素少于 p 个,它会反转列表中的所有元素。

我们的工作是在执行完所有命令后打印列表。

蛮力的方法是手动反转数组,这将具有O(pm)的复杂度,但是您需要以O(m)的复杂度来实现它.

我考虑过使用双向链表,我很确定它会起作用,但我无法完成我的答案。

示例

输入

8 3

APPEND 1
APPEND 2
APPEND 3
APPEND 4
ROTATE
APPEND 5
APPEND 6
ROTATE

输出

1 4 3 6 5 2

【问题讨论】:

  • 我不太确定谁是“我们”,谁是“你”,以及问题所在。您能否提供一个示例,作为代码或伪代码?
  • 嗯,看起来是个不错的挑战。你有什么问题,到目前为止你尝试过什么?
  • 用 prev 和 next 两个指针创建一个结构体,指向列表中的下一个或上一个元素(第一个和最后一个元素为 null)。每当调用 append 时,只需创建一个新节点并为它和前一个节点分配指针。我们必须将 K 个数字的节点保留在列表末尾之前,或者如果列表大小已经小于 m,则为第一个节点,例如节点 L 当调用 reverse 时,对于 L 之前的元素,将 next 指针分配给 L+p , 对于 L+1,L+2,....,L+p 更改节点布尔值的状态,以便在 L 向前移动时更改方向。
  • 我的意思是每个节点都有一个名为 state 的布尔值,它表示 next 指针是 next 或 prev (取决于该节点是否是必须旋转的数组切片。使用这个,保证算法是从O(m)开始的。我认为它必须工作。但只有一个问题我不能处理:在已经移动L并改变状态时,如果调用另一个ROTATE我们该怎么办?跨度>
  • 鉴于引用的问题陈述,这篇文章的标题看起来是一个 XY 问题——除了使用双向链表之外,肯定还有其他方法。如果您采用 p 元素数组,跟踪 headtail,切换 a) 角色和附加方向或 b)"偏移量的符号”,打印即将被覆盖的每个元素(不按字母打印列表之后所有命令,授予)。

标签: algorithm doubly-linked-list


【解决方案1】:

双向链表的想法是正确的。要使其正常工作,您需要摆脱上一个/下一个概念,而只需跟踪一个节点可能拥有的潜在 2 个邻居,而无需任何方向指示(上一个/下一个)。

你的双向链表会有一个头和一个尾——它们必须保留。并且您还可以维护对当前是“最后一个元素”的起始节点的节点的引用(或者当列表中没有那么多元素时更少)。每当您添加节点时,请保持更新。为了知道该引用向哪个方向移动,还要维护对它之前节点的引用。

然后,当需要执行反转时,只需将引用(和反向引用)交换到“最后一个元素”子列表的头部和尾部。不要遍历整个子列表来更改每对连续节点之间的链接。通过删除 prev/next 的想法,您可以保留那些“内部”链接。每当您需要遍历列表时,您将始终知道您来自哪一侧(即“前一个”节点是什么),因此您可以推导出哪个邻居必须是“下一个”节点。

这是该想法在 JavaScript 中的实现。在代码的末尾,针对您给出的示例输入执行算法:

class Node {
    constructor(x, neighbor1=null, neighbor2=null) {
        this.x = x;
        this.neighbors = [neighbor1, neighbor2]; // No specific order...
    }
    opposite(neighbor) { 
        // Return the neighbor that is on the other side of the argument-neighbor
        return this.neighbors[1 - this.neighbors.indexOf(neighbor)];
    }
    replaceNeighbor(find, repl) {
        let i = this.neighbors.indexOf(find);
        this.neighbors[i] = repl;
    }
}

class List {
    constructor(k) {
        this.nodeCount = 0;
        this.k = k;
        // All node references are null:
        this.head = this.tail = this.tailBeforeLastK = this.headOfLastK = null;
    }
    add(x) {
        this.nodeCount++;
        let node = new Node(x, this.tail, null);
        if (this.head === null) {
            this.headOfLastK = this.head = this.tail = node;
            return;
        }
        this.tail.replaceNeighbor(null, node);
        this.tail = node;
        if (this.nodeCount > this.k) { // Move the head of the "last K" sublist 
            [this.tailBeforeLastK, this.headOfLastK] = 
                [this.headOfLastK, this.headOfLastK.opposite(this.tailBeforeLastK)]; 
        }
    }
    reverse() {
        if (this.nodeCount < 2 || this.k < 2) return;
        // Exchange the links to the start/end of the K-last sublist
        this.tail.replaceNeighbor(null, this.tailBeforeLastK);
        if (this.tailBeforeLastK) {
            this.tailBeforeLastK.replaceNeighbor(this.headOfLastK, this.tail);
            this.headOfLastK.replaceNeighbor(this.tailBeforeLastK, null);
        }
        else this.head = this.tail;
        
        // Swap
        [this.tail, this.headOfLastK] = [this.headOfLastK, this.tail];
    }
    toArray() {
        let result = [];
        for (let prev = null, node = this.head; node; [prev, node] = 
                                                      [node, node.opposite(prev)]) {
            result.push(node.x);
        }
        return result;
    }
}

// Example
let k = 3;
// null means: REVERSE, a number means: ADD <number>:
let actions = [1, 2, 3, 4, null, 5, 6, null]; 

let list = new List(k);

for (let action of actions) {
    if (action === null) list.reverse();
    else                 list.add(action);
}
console.log(list.toArray());

【讨论】:

  • 感谢您的回复。让我实现它并检查您的想法。
猜你喜欢
  • 2021-05-13
  • 1970-01-01
  • 2015-08-14
  • 1970-01-01
  • 2020-12-25
  • 1970-01-01
  • 2020-08-07
  • 1970-01-01
  • 2011-10-31
相关资源
最近更新 更多