携手创造,共同成长!这是我参加「日新方案 8 月更文应战」的第17天,点击查看活动概况

题目

请你仅运用两个行列完成一个后入先出(LIFO)的栈,并支撑普通栈的悉数四种操作(pushtoppopempty)。

完成 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并回来栈顶元素。
  • int top() 回来栈顶元素。
  • boolean empty() 假如栈是空的,回来 true;否则,回来 false

留意:

  • 你只能运用行列的根本操作 —— 也便是 push to backpeek/pop from frontsizeis empty 这些操作。
  • 你所运用的言语也许不支撑行列。 你能够运用 list (列表)或者 deque(双端行列)来模拟一个行列 , 只要是标准的行列操作即可。
  • 输入:
    ["MyStack", "push", "push", "top", "pop", "empty"]
    [[], [1], [2], [], [], []]
  • 输出:
    [null, null, null, 2, 2, false]
  • 解说:
    MyStack myStack = new MyStack();
    myStack.push(1);
    myStack.push(2);
    myStack.top(); // 回来 2
    myStack.pop(); // 回来 2
    myStack.empty(); // 回来 False

办法一:两个行列

思路及解法

为了满意栈的特性,即最终入栈的元素最先出栈,在运用行列完成栈时,应满意行列前端的元素是最终入栈的元素。能够运用两个行列完成栈的操作,其间 queue1 用于存储栈内的元素,queue2 作为入栈操作的辅佐行列。

入栈操作时,首先将元素入队到 queue2,然后将 queue1 的悉数元素顺次出队并入队到 queue2,此刻 queue2 的前端的元素即为新入栈的元素,再将 queue1queue2 交换,则 queue1 的元素即为栈内的元素,queue1 的前端和后端别离对应栈顶和栈底。

因为每次入栈操作都保证 queue1 的前端元素为栈顶元素,因而出栈操作和取得栈顶元素操作都能够简单完成。出栈操作只需求移除 queue1 的前端元素并回来即可,取得栈顶元素操作只需求取得 queue1 的前端元素并回来即可(不移除元素)。

因为 queue1 用于存储栈内的元素,判别栈是否为空时,只需求判别 queue1 是否为空即可。

代码

class MyStack {
    var queue1: [Int] = []
    var queue2: [Int] = []
    init() {
    }
    func push(_ x: Int) {
        queue2.append(x)
        while !queue1.isEmpty {
            queue2.append(queue1.removeFirst())
        }
        swap(&queue1, &queue2)
    }
    func pop() -> Int {
        let r: Int = queue1.removeFirst()
        return r
    }
    func top() -> Int {
        var r: Int = 0
        if !queue1.isEmpty {
            r = queue1.first!
        }
        return r
    }
    func empty() -> Bool {
        return queue1.isEmpty
    }
}

复杂度剖析

  • 时刻复杂度:入栈操作 O(n)O(n),其余操作都是 O(1)O(1),其间 nn 是栈内的元素个数。
    入栈操作需求将 queue1queue1 中的 nn 个元素出队,并入队 n+1n+1 个元素到 queue2queue2,共有 2n+12n+1 次操作,每次出队和入队操作的时刻复杂度都是 O(1)O(1),因而入栈操作的时刻复杂度是 O(n)O(n)
    出栈操作对应将 queue1queue1 的前端元素出队,时刻复杂度是 O(1)O(1)
    取得栈顶元素操作对应取得 queue1queue1 的前端元素,时刻复杂度是 O(1)O(1)
    判别栈是否为空操作只需求判别 queue1queue1 是否为空,时刻复杂度是 O(1)O(1)

  • 空间复杂度:O(n)O(n),其间 nn 是栈内的元素个数。需求运用两个行列存储栈内的元素。

办法二:一个行列

思路及解法

办法一运用了两个行列完成栈的操作,也能够运用一个行列完成栈的操作。

运用一个行列时,为了满意栈的特性,即最终入栈的元素最先出栈,同样需求满意行列前端的元素是最终入栈的元素。

入栈操作时,首先取得入栈前的元素个数 nn,然后将元素入队到行列,再将行列中的前 nn 个元素(即除了新入栈的元素之外的悉数元素)顺次出队并入队到行列,此刻行列的前端的元素即为新入栈的元素,且行列的前端和后端别离对应栈顶和栈底。

因为每次入栈操作都保证行列的前端元素为栈顶元素,因而出栈操作和取得栈顶元素操作都能够简单完成。出栈操作只需求移除行列的前端元素并回来即可,取得栈顶元素操作只需求取得行列的前端元素并回来即可(不移除元素)。

因为行列用于存储栈内的元素,判别栈是否为空时,只需求判别行列是否为空即可。

代码

class MyStack {
    var queue: [Int] = []
    init() {
    }
    func push(_ x: Int) {
        let n: Int = queue.count
        var i: Int = 0
        queue.append(x)
        while i < n {
            i += 1
            queue.append(queue.removeFirst())
        }
    }
    func pop() -> Int {
        let r: Int = queue.removeFirst()
        return r
    }
    func top() -> Int {
        var r: Int = 0
        if !queue.isEmpty {
            r = queue.first!
        }
        return r
    }
    func empty() -> Bool {
        return queue.isEmpty
    }
}

复杂度剖析

  • 时刻复杂度:入栈操作 O(n)O(n),其余操作都是 O(1)O(1),其间 nn 是栈内的元素个数。
    入栈操作需求将行列中的 nn 个元素出队,并入队 n+1n+1 个元素到行列,共有 2n+12n+1 次操作,每次出队和入队操作的时刻复杂度都是 O(1)O(1),因而入栈操作的时刻复杂度是 O(n)O(n)
    出栈操作对应将行列的前端元素出队,时刻复杂度是 O(1)O(1)
    取得栈顶元素操作对应取得行列的前端元素,时刻复杂度是 O(1)O(1)
    判别栈是否为空操作只需求判别行列是否为空,时刻复杂度是 O(1)O(1)

  • 空间复杂度:O(n)O(n),其间 nn 是栈内的元素个数。需求运用一个行列存储栈内的元素。