《剑指 Offer》(第 2 版) 题解(Python 语言实现)第 21-30 题


第 21 题:调整数组使得奇数位于偶数之前

传送门:AcWing:调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序。

使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分。

样例:

输入:[1,2,3,4,5]

输出: [1,3,5,2,4]

思路1:有点类似“两路快排”的思路。“两路快排”中有一些细节需要复习一下。双指针扫描:用两个指针分别从首尾开始,往中间扫描。扫描时保证第一个指针前面的数都是奇数,第二个指针后面的数都是偶数。每次迭代时需要进行的操作:

1、第一个指针一直往后走,直到遇到第一个偶数为止;
2、第二个指针一直往前走,直到遇到第一个奇数为止。
交换两个指针指向的位置上的数,再进入下一层迭代,直到两个指针相遇为止;
时间复杂度:当两个指针相遇时,走过的总路程长度是 n n n,所以时间复杂度是 O ( n ) O(n) O(n)

Python 代码:

class Solution(object):
    def reOrderArray(self, array):
        """
        :type array: List[int]
        :rtype: void
        """
        # 特判
        size = len(array)
        if size < 2:
            return

        l = 0
        r = size - 1

        # 始终保持 [0,l) 是奇数
        # (r,size-1] 是偶数
        
        while True:
            while l <= r and array[l] & 1 == 1:
                l += 1
            # 此时 l 来到了第一个偶数位置
            while r >= l and array[r] & 1 == 0:
                r -= 1
            # 此时 r 来到了第一个奇数的位置
            
            # 这里一定是大于号,否则数组下标会越界
            # 一定要特别注意,写 == 号会出错,写 >= 是可以的,因为 == 成立的时候,不用交换
            if l > r:
                break
            array[l], array[r] = array[r], array[l]
            l += 1
            r -= 1
        return array

C++ 代码:

image-20190108005607547

C++ 代码:

class Solution {
public:
    void reOrderArray(vector<int> &array) {
         int l = 0, r = array.size() - 1;
         while (l <= r) {
             while (array[l] % 2 == 1) l ++ ;
             while (array[r] % 2 == 0) r -- ;
             if (l < r) swap(array[l], array[r]);
         }
    }
};

思路2:把奇数数组复制出来,按照从后向前归并排序的做法完成。

Java 代码:

import java.util.Arrays;

public class Solution {
    // 保证奇数和奇数,偶数和偶数之间的相对位置不变。
    public void reOrderArray(int[] array) {
        int len = array.length;
        if (len == 0) {
            return;
        }
        // 奇数全部挪到缓存数组中
        int[] buff = new int[len];
        int j = -1;
        for (int i = 0; i < len; i++) {
            if (array[i] % 2 == 1) {
                j++;
                buff[j] = array[i];
            }
        }
        // 如果没有奇数,直接返回就可以了
        if (j == -1) {
            return;
        }
        // 从后向前赋值
        int k = len - 1;
        for (int i = len - 1; i >= 0; i--) {
            if (array[i] % 2 == 0) {
                array[k] = array[i];
                k--;
            }
        }
        while (j >= 0) {
            array[j] = buff[j];
            j--;
        }
    }
}

扩展:书本上有这个问题更一般的做法。

第 22 题:输入一个链表,输出该链表中倒数第 k 个结点

传送门:AcWing:链表中倒数第 k 个节点

输入一个链表,输出该链表中倒数第k个结点。

注意:

  • k >= 0;
  • 如果 k 大于链表长度,则返回 NULL;

样例:

输入:链表:1->2->3->4->5k=2

输出:4

分析:设置快慢指针,思路很简单,不过在具体编码的时候,还是有一些细节要注意的,特别是空指针的判断上。

思路1:先遍历完,数出链表有多少个结点。

Python 代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def findKthToTail(self, pListHead, k):
        """
        :type pListHead: ListNode
        :type k: int
        :rtype: ListNode
        """
        if pListHead is None:
            return None
        counter = 0
        p = pListHead
        while p:
            counter += 1
            p = p.next
        if k > counter:
            return None
        p = pListHead
        for _ in range(counter - k):
            p = p.next
        return p

思路2:推荐,设置快慢指针,快指针先走 k − 1 k-1 k1 步,然后快慢指针一起走。

C++ 代码:

image-20190108005842835

Python 代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def findKthToTail(self, pListHead, k):
        """
        :type pListHead: ListNode
        :type k: int
        :rtype: ListNode
        """
        if pListHead is None:
            return None
        fast = pListHead
        # 要注意的临界点1:
        for _ in range(k - 1):
            fast = fast.next
            # 注意判断
            if fast is None:
                return None
        slow = pListHead
        # 要注意的临界点2:
        while fast.next:
            slow = slow.next
            fast = fast.next
        return slow

第 23 题:链表中环的入口结点

传送门:AcWing:链表中环的入口结点

给定一个链表,若其中包含环,则输出环的入口节点。

若其中不包含环,则输出null

样例:

给定如上所示的链表:[1, 2, 3, 4, 5, 6],编号:2。
注意,这里的 2 表示编号是 2 的节点,节点编号从 0 开始。所以编号是 2 的节点就是 val 等于 3 的节点。

则输出环的入口节点 3 。

分析:看的答案,记住结论就好,编码上还是要注意特判的情况,还有空指针的情况。“慢”指针进入环的时候,“快指针”要来追它,因为快慢指针走的步数差是固定的。例如: A 手上有 100 块钱,A 每天赚 10 块钱,B 手上有 50 块钱,B 每天赚 20,一定有一天,你们的钱相等,而且只要环内结点个数这么多就可以了。

image-20190110151143523

我写的错误解:

image-20190109132103035

Python 代码:

# 34. 链表中环的入口结点
# 给定一个链表,若其中包含环,则输出环的入口节点。
#
# 若其中不包含环,则输出null。

# Definition for singly-linked list.
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None


class Solution(object):
    def entryNodeOfLoop(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # 先考虑边界情况
        if head is None or head.next is None:
            return None

        slow = head
        fast = head

        while fast and fast.next:
            # 慢指针走一步,快指针走两步
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                # 说明链表中存在环
                break
		
        # 注意:跳出循环的原因有两个,有可能是根本没有环,即上面  while fast and fast.next 不成立
        # 也有可能是 slow == fast 里 break 的,分别讨论就可以了
        if fast is None or fast.next is None:
            return None

        slow = head
        while slow != fast:
            slow = slow.next
            fast = fast.next
        # 走到这里,说明 slow == fast
        return slow

第 24 题:输入一个链表,反转链表后,输出链表的所有元素

传送门:AcWing:反转链表

定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。

样例:

输入:1->2->3->4->5->NULL

输出:5->4->3->2->1->NULL

思路1:递归。

Python 代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

# 递归写法:用递归就不用思考穿针引线这种事情了。


class Solution:
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # 递归的终止条件一定要写对:考虑结点为空和单结点的情况
        if head is None or head.next is None:
            return head
        next = head.next
        new_head = self.reverseList(next)
        next.next = head
        head.next = None
        return new_head

思路2:非递归写法,穿针引线。

image-20190108155255426

Python 代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

# 穿针引线,可以看到 while 循环体部分的代码是首尾相连的,有些单链表的题目也有这种规律,感觉很神奇
# 在 Python 中,其实还有更简便的写法,就跟斐波拉契数列一样


class Solution:
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """

        pre = None
        cur = head
        while cur:
            next = cur.next
            cur.next = pre
            pre = cur
            cur = next
        return pre

第 25 题:合并排序的链表

传送门:AcWing:合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。

样例:

输入:1->3->5 , 2->4->5

输出:1->2->3->4->5->5

同 LeetCode 第 21 题,传送门:21. 合并两个有序链表

思路1: 使用递归,编码简单。

Python 代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None


class Solution(object):
    def merge(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """

        if l1 is None:
            return l2
        if l2 is None:
            return l1

        # 代码能走到这里说明 l1 和 l2 均非空
        # 比较哪个小就行了
        if l1.val < l2.val:
            l1.next = self.merge(l1.next, l2)
            return l1
        # 走到这里 l1.val >= l2.val
        l2.next = self.merge(l1, l2.next)
        return l2

思路2:穿针引线。

Python 代码:

# Definition for singly-linked list.
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None


# 穿针引线的写法,一定要画图才能写出来
# 比较麻烦,还是递归处理简单

class Solution(object):
    def merge(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        # 引入头结点可以简化对问题的讨论
        dummy_node = ListNode(-1)
        cur_node = dummy_node
        p1 = l1
        p2 = l2

        while p1 and p2:
            # 两者都不为空,才须要比较
            # 其中有一个为空的时候,把其中一个接到另一个尾巴就好了
            if p1.val < p2.val:
                # next 指针修改
                cur_node.next = p1
                # p1 后移
                p1 = p1.next
            else:
                cur_node.next = p2
                p2 = p2.next
            cur_node = cur_node.next
        # 跳出循环的时候,一定有:p1 为空或者 p2 为空
        # 其中有一个为空的时候,把其中一个接到另一个尾巴就好了
        if p1 is None:  # 这一句写成 if not p1 也是可以的,不过不好理解
            cur_node.next = p2
        if not p2:
            cur_node.next = p1
        return dummy_node.next

第 26 题:输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构

传送门:树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。

我们规定空树不是任何树的子结构。

样例:

树 A :

       8
      / \
     8   7
   / \
 9   2
      / \
     4   7

树 B :

     8
   / \
 9   2

返回 true ,因为 B 是 A 的子结构。

思路:典型使用递归解决的问题。

Python 代码:在理解的基础上,记住这个题的解法

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        # pRoot1 是大树
        # pRoot2 是小树
        result = False
        if pRoot1 and pRoot2:
            # 第 1 步:在树 A 中查找与根结点的值一样的结点
            # 这里排除了 pRoot2 是空树的情况
            if pRoot1.val == pRoot2.val:
                # 判断树 1 是不是包含树 2
                result = self.__doesTree1HaveTree2(pRoot1, pRoot2)
            if not result:
                # 如果树 1 不包含树 2
                return self.HasSubtree(pRoot1.left, pRoot2) 
            			or self.HasSubtree(pRoot1.right, pRoot2)
        return result

    def __doesTree1HaveTree2(self, pRoot1, pRoot2):
        if pRoot2 is None:
            return True
        if pRoot1 is None:
            return False
        if pRoot1.val != pRoot2.val:
            return False
        # 走到这里说明 assert pRoot1.val == pRoot2.val 为 True
        return self.__doesTree1HaveTree2(pRoot1.left, pRoot2.left) 
    			and self.__doesTree1HaveTree2(pRoot1.right, pRoot2.right)

第 27 题:操作给定的二叉树,将其变换为源二叉树的镜像

传送门:二叉树的镜像

输入一个二叉树,将它变换为它的镜像。

样例:

输入树:

    8
   / \
  6  10
 / \ / \
5  7 9 11

[8,6,10,5,7,9,11,null,null,null,null,null,null,null,null]
输出树:

    8
   / \
  10  6
 / \ / \
11 9 7  5

[8,10,6,11,9,7,5,null,null,null,null,null,null,null,null]

思路1:递归方式:前序遍历或者后序遍历都行。

Python 代码:前序遍历

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None


class Solution(object):
    def mirror(self, root):
        """
        :type root: TreeNode
        :rtype: void
        """
        # 先写递归终止条件
        if root is None:
            return root

        # 按照前序遍历的方式
        root.left, root.right = root.right, root.left
        self.mirror(root.left)
        self.mirror(root.right)

Python 代码:后序遍历

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None


class Solution(object):
    def mirror(self, root):
        """
        :type root: TreeNode
        :rtype: void
        """
        # 先写递归终止条件
        if root is None:
            return root

        # 按照后序遍历的方式
        self.mirror(root.left)
        self.mirror(root.right)
        root.left, root.right = root.right, root.left

Python 代码:层序遍历

class Solution(object):
    def mirror(self, root):
        """
        :type root: TreeNode
        :rtype: void
        """
        # 先写递归终止条件
        if root is None:
            return root
        queue = [root]
        while queue:
            top = queue.pop(0)
            top.left, top.right = top.right, top.left
            if top.left:
                queue.append(top.left)
            if top.right:
                queue.append(top.right)
        return root

思路2:非递归方式(没有看出来是那种递归方式)。

Java 代码:

class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}

public class Solution {

    // 前序遍历和后序遍历都是可以的
    public void Mirror(TreeNode root) {
        if (root == null) {
            return;
        }
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        Mirror(root.left);
        Mirror(root.right);
    }

    public void Mirror1(TreeNode root) {
        if (root == null) {
            return;
        }
        Mirror(root.left);
        Mirror(root.right);
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
    }
}

非递归方式:下面这个代码有点意思。

Python 代码:

class Solution:

    def Mirror(self, root):
        if root is None:
            return
        stack = []
        while root or stack:
            while root:
                root.left, root.right = root.right, root.left
                stack.append(root)
                root = root.left
            if stack:
                root = stack.pop()
                root = root.right

第 28 题:对称的二叉树

传送门:对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。

如果一棵二叉树和它的镜像一样,那么它是对称的。

样例:

如下图所示二叉树 [1,2,2,3,4,4,3,null,null,null,null,null,null,null,null] 为对称二叉树:

       1
      / \
    2   2
  / \ / \
3  4 4  3

如下图所示二叉树 [1,2,2,null,4,4,3,null,null,null,null,null,null] 不是对称二叉树:

       1
      / \
    2   2
      \ / \
      4 4  3

分析:LeetCode 上有类似的问题,使用双端队列可以完成。

image-20190107113200915

同 LeetCode 第 101 题。

解法1:递归写法。

Python 代码:

# Definition for a binary tree node.
class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


# 递归写法:得引入辅助函数

class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        # 先写递归终止条件
        if root is None:
            return True
        return self.__helper(root.left, root.right)

    def __helper(self, p1, p2):
        if p1 is None and p2 is None:
            return True
        if p1 is None or p2 is None:
            return False
        return p1.val == p2.val and self.__helper(p1.left, p2.right) and self.__helper(p1.right, p2.left)

解法2:非递归写法,借助双端队列辅助判断。自己画一个图,就好理解了。

Python 代码:

# Definition for a binary tree node.
class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


# 非递归写法:借助双端队列辅助判断

class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        # 先写递归终止条件
        if root is None:
            return True

        # 其实应该用 from collections import deque
        deque = []

        deque.insert(0, root.left)
        deque.append(root.right)

        while deque:
            l_node = deque.pop(0)
            r_node = deque.pop()
            
			# 这一步一定不要忘记了
            if l_node is None and r_node is None:
                continue
            if l_node is None or r_node is None:
                return False
            # 代码走到这里一定有 l_node 和 r_node 非空
            # 因此可以取出 val 进行判断了
            if l_node.val != r_node.val:
                return False
            deque.insert(0, l_node.right)
            deque.insert(0, l_node.left)
            deque.append(r_node.left)
            deque.append(r_node.right)
        return True

“大雪菜”的写法:用栈模拟递归,对根结点的左子树,我们用中序遍历;对根结点的右子树,我们用反中序遍历。
则两个子树互为镜像,当且仅当同时遍历两棵子树时,对应结点的值相等。

时间复杂度:树中每个结点仅被遍历一遍,所以时间复杂度是 O ( n ) O(n) O(n)

C++ 代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if (!root) return true;
        stack<TreeNode*> left, right;
        TreeNode *lc = root->left;
        TreeNode *rc = root->right;
        while(lc || rc || left.size())
        {
            while (lc && rc)
            {
                left.push(lc), right.push(rc);
                lc = lc->left, rc = rc->right;
            }
            if (lc || rc) return false;
            lc = left.top(), rc = right.top();
            left.pop(), right.pop();
            if (lc->val != rc->val) return false;
            // 这里反过来操作
            lc = lc->right, rc = rc->left;
        }
        return true;
    }

};

作者:yxc
链接:https://www.acwing.com/solution/AcWing/content/747/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

第 29 题:顺时针打印矩阵

传送门:顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

样例:

输入:

[
      [1, 2, 3, 4],
      [5, 6, 7, 8],
      [9,10,11,12]
]

输出:[1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]

Python 代码:

class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        rows = len(matrix)
        cols = len(matrix[0])
        result = []
        if rows == 0 and cols == 0:
            return result
        left, right, top, buttom = 0, cols - 1, 0, rows - 1
        while left <= right and top <= buttom:
            for i in range(left, right + 1):
                result.append(matrix[top][i])
            for i in range(top + 1, buttom + 1):
                result.append(matrix[i][right])
            if top != buttom:
                for i in range(left, right)[::-1]:
                    result.append(matrix[buttom][i])
            if left != right:
                for i in range(top + 1, buttom)[::-1]:
                    result.append(matrix[i][left])
            left += 1
            top += 1
            right -= 1
            buttom -= 1
        return result

“大雪菜”的写法:我们顺时针定义四个方向:上右下左。从左上角开始遍历,先往右走,走到不能走为止,然后更改到下个方向,再走到不能走为止,依次类推,遍历 n 2 n^2 n2 个格子后停止。

时间复杂度:矩阵中每个格子遍历一次,所以总时间复杂度是 O ( n 2 ) O(n^2) O(n2)

C++ 代码:

class Solution {
public:
    vector<int> printMatrix(vector<vector<int>>& matrix) {
        vector<int> res;
        if (matrix.empty()) return res;
        int n = matrix.size(), m = matrix[0].size();
        vector<vector<bool>> st(n, vector<bool>(m, false));
        int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
        int x = 0, y = 0, d = 1;
        for (int k = 0; k < n * m; k ++ )
        {
            res.push_back(matrix[x][y]);
            st[x][y] = true;

            int a = x + dx[d], b = y + dy[d];
            if (a < 0 || a >= n || b < 0 || b >= m || st[a][b])
            {
                d = (d + 1) % 4;
                a = x + dx[d], b = y + dy[d];
            }
            x = a, y = b;
        }
        return res;
    }
};

作者:yxc
链接:https://www.acwing.com/solution/AcWing/content/748/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

第 30 题:定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数

传送门:AcWing:包含min函数的栈

设计一个支持 push,pop,top 等操作并且可以在 O(1) 时间内检索出最小元素的堆栈。

  • push(x)–将元素x插入栈中
  • pop()–移除栈顶元素
  • top()–得到栈顶元素
  • getMin()–得到栈中最小元素

样例:

MinStack minStack = new MinStack();
minStack.push(-1);
minStack.push(3);
minStack.push(-4);
minStack.getMin();   --> Returns -4.
minStack.pop();
minStack.top();      --> Returns 3.
minStack.getMin();   --> Returns -1.

思路:定义两个栈,一个存放入的值,另一个存最小值,两个栈应该是同步 push 和 pop,否则还要分类讨论,代码编写容易出错。

Python 代码:

class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.helper = []

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """

        if len(self.stack) == 0:
            self.helper.append(x)
            self.stack.append(x)
        else:
            # 如果将要 push 的元素比辅助栈的栈顶元素还大,不能放这个元素,
            # 此时应该把辅助栈的栈顶元素再复制一份
            peek = self.helper[-1]
            if x > peek:
                self.stack.append(x)
                self.helper.append(peek)
            else:
                self.stack.append(x)
                self.helper.append(x)
                

    def pop(self):
        """
        :rtype: void
        """

        if len(self.stack) == 0:
            return
        # 同步 pop 元素
        self.helper.pop()
        return self.stack.pop()

    def top(self):
        """
        :rtype: int
        """
        return self.stack[-1]

    def getMin(self):
        """
        :rtype: int
        """
        return self.helper[-1]

Java 代码:

import java.util.Stack;

public class Solution {

    private Stack<Integer> minStack = new Stack<>();
    private Stack<Integer> dataStack = new Stack<>();

    public void push(int node) {
        if (minStack.isEmpty()) {
            minStack.push(node);
            dataStack.push(node);
            return;
        }
        int curMin = minStack.peek();
        if (node < curMin) {
            minStack.push(node);
        } else {
            minStack.push(curMin);
        }
        dataStack.push(node);
    }

    public void pop() {
        minStack.pop();
        dataStack.pop();
    }

    public int top() {
        return dataStack.pop();
    }

    public int min() {
        return minStack.peek();
    }
}

思路2:我们除了维护基本的栈结构之外,还需要维护一个“辅助栈”。下面介绍如何维护单调栈:做到以下两点,辅助栈的栈顶元素,就是当前栈中的最小数。

1、当我们向栈中压入一个数时,如果该数小于(只要小于)“辅助栈”的栈顶元素,则将该数同时压入“辅助栈”中;否则,不压入。由于栈具有先进后出性质,所以在该数被弹出之前,“辅助栈”中一直存在一个数比该数小,所以该数一定不会被当做最小数输出。

2、当我们从栈中弹出一个数时,如果该数等于单调栈的栈顶元素,同时将单调栈的栈顶元素弹出。

时间复杂度:四种操作都只有常数次入栈出栈操作,所以时间复杂度都是 O ( 1 ) O(1) O(1)

Python 代码:

# 定义两个栈,一个存放入的值,另一个存最小值,两个栈应该是同步 push 和 pop,否则还要分类讨论,代码编写容易出错。


class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.helper = []

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """
        # 无论如何都 push
        self.stack.append(x)
        # 如果放入的元素小于辅助栈顶元素,辅助栈顶才 push,否则什么都不做
        if not self.helper or self.helper[-1] > x:
            self.helper.append(x)

    def pop(self):
        """
        :rtype: void
        """

        if len(self.stack) == 0:
            return
        # 如果弹出的元素等于辅助栈栈顶元素,才将辅助栈顶元素弹出
        if self.helper[-1] == self.stack[-1]:
            self.helper.pop()
        return self.stack.pop()

    def top(self):
        """
        :rtype: int
        """
        return self.stack[-1]

    def getMin(self):
        """
        :rtype: int
        """
        return self.helper[-1]

# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

(本节完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值