zoukankan      html  css  js  c++  java
  • 【剑指offer】leetcode刷题 -- Python3实现 -- 共75题(更新中)

    目录:

    1. 剑指 Offer 03. 数组中重复的数字 -- 简单
    2. 剑指 Offer 04. 二维数组中的查找 -- 简单
    3. 剑指 Offer 05. 替换空格 -- 简单
    4. 剑指 Offer 06. 从尾到头打印链表 -- 简单
    5. 剑指 Offer 07. 重建二叉树 -- 中等
    6. 剑指 Offer 09. 用两个栈实现队列 -- 简单
    7. 剑指 Offer 10- I. 斐波那契数列 -- 简单
    8. 剑指 Offer 10- II. 青蛙跳台阶问题 -- 简单
    9. 剑指 Offer 11. 旋转数组的最小数字 -- 简单
    10. 剑指 Offer 12. 矩阵中的路径 -- 中等
    11. 剑指 Offer 13. 机器人的运动范围 -- 中等
    12. 剑指 Offer 14- I. 剪绳子 -- 中等
    13. 剑指 Offer 14- II. 剪绳子 II -- 中等
    14. 剑指 Offer 15. 二进制中1的个数 -- 简单

    剑指 Offer 03. 数组中重复的数字 -- 2020-9-8

    题目链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/

    题目描述

    找出数组中重复的数字。

    在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。
    数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

    示例 1:
    输入:
    [2, 3, 1, 0, 2, 5, 3]
    输出:2 或 3

    限制:
    2 <= n <= 100000

    解题思路

    方法一:先排序,然后再遍历一遍。时间复杂度O(nlogn),空间复杂度O(1)。
    方法二:使用哈希表。时间复杂度O(n),空间复杂度O(n)。
    方法三:时间复杂度O(n),空间复杂度O(1),如下:
    遍历,判断当前数m和下标i是否相同,如果不同,则与下标m的数比较,如果相同则找到重复,如果不同则交换。
    缺点:这个方法会改变输入的数组。

    题目稍微变一下:
    长度为n+1的数组中包含有1~n的数字,找出任意一个重复元素。并且不能改变输入的数组。
    思路1:将数组复制一份,然后采用上面的方法。
    思路2:时间换空间。因为元素总数n+1大于n,所以可以采取二分查找的方法。
    将数组一分为二,如果其中一个子数组个数大于n/2,则这个子数组里有重复元素,继续递归查找。
    这个方法的时间复杂度O(nlogn),空间复杂度O(1)。

    代码

    class Solution:
        def findRepeatNumber(self, nums):
            # 数组为空检查
            if nums is None or len(nums) == 0:
                return -1
    
            # 非法值检查
            for i in range(len(nums)):
                if not (nums[i] >= 0 and nums[i] <= len(nums)-1):
                    return -1
    
            # 查找重复值
            for i in range(len(nums)):
                while nums[i] != i:
                    m = nums[i]
                    if nums[i] == nums[m]:  # 发现重复值
                        return nums[i]
                    else:
                        # swap
                        temp = nums[i]
                        nums[i] = nums[m]
                        nums[m] = temp
            return -1
    

    返回目录

    剑指 Offer 04. 二维数组中的查找 -- 2020-9-8

    题目链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/

    题目描述

    在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
    请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

    示例:
    现有矩阵 matrix 如下:
    [
    [1, 4, 7, 11, 15],
    [2, 5, 8, 12, 19],
    [3, 6, 9, 16, 22],
    [10, 13, 14, 17, 24],
    [18, 21, 23, 26, 30]
    ]
    给定 target = 5,返回 true。
    给定 target = 20,返回 false。

    限制:
    0 <= n <= 1000
    0 <= m <= 1000

    解题思路

    从右上角或者左下角开始查找,逐步删去行列。(从左上角或右下角则不能)
    比如从右上角开始查找,如果该数字大于要查找的数字,则删去该列。如果该数字小于要查找的数字,则删去该行。等于的话则返回结果。

    代码

    class Solution:
        def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
            if matrix is None or len(matrix) == 0 or len(matrix[0]) == 0:
                return False
            
            rows, cols = len(matrix), len(matrix[0])
            i, j = 0, cols-1
    
            while(i < rows and j >= 0):
                if matrix[i][j] == target:
                    return True
                elif matrix[i][j] > target:
                    j -= 1
                else:
                    i += 1
                    
            return False
    

    返回目录

    剑指 Offer 05. 替换空格 -- 2020-9-8

    题目链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/

    题目描述

    请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

    示例 1:
    输入:s = "We are happy."
    输出:"We%20are%20happy."
     
    限制:
    0 <= s 的长度 <= 10000

    解题思路

    从前往后替换,需要多次移动字符,时间复杂度是O((n^2))。
    从后往前替换,则时间复杂度只有O(n)。
    不过python的话,更加简单,直接replace或者先split再join即可。

    代码

    class Solution:
        def replaceSpace(self, s: str) -> str:
            if s is None or len(s) == 0:
                return s
    
            return s.replace(' ', '%20')
    

    返回目录

    剑指 Offer 06. 从尾到头打印链表 -- 2020-9-8

    题目链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/

    题目描述

    输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

    示例 1:
    输入:head = [1,3,2]
    输出:[2,3,1]

    限制:
    0 <= 链表长度 <= 10000

    解题思路

    后进先出,用栈实现既可。

    代码

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def reversePrint(self, head: ListNode) -> List[int]:
            if head is None:
                return []
    
            temp = head
            lst = []
            while temp != None:
                lst.append(temp.val)
                temp = temp.next
            lst.reverse()
            return lst
    

    返回目录

    剑指 Offer 07. 重建二叉树 -- 2020-9-9

    题目链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/

    题目描述

    输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

    例如,给出
    前序遍历 preorder = [3,9,20,15,7]
    中序遍历 inorder = [9,3,15,20,7]
    返回如下的二叉树:

     
    限制:
    0 <= 节点个数 <= 5000

    解题思路

    前序遍历的第一个元素就是根节点3,在中序遍历中,3左边的所有元素属于左子树,3右边的所有元素属于右子数。
    对应于前序遍历,除了根节点外,先输出左子树,后输出右子树。所以找到左右子树对应的前序中序序列,由此可以通过递归的方法解决。
    但是递归效率比较低,所以可以改成迭代。

    代码

    递归版本:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
            if preorder is None or inorder is None or len(preorder) == 0 or len(preorder) != len(inorder):
                return None
            
            root = preorder[0]
            left_pre, right_pre, left_in, right_in = [], [], [], []
    
            # 在中序序列中找根节点
            found = False
            for i in range(len(inorder)):
                if inorder[i] != root and not found:
                    left_in.append(inorder[i])
                elif inorder[i] != root and found:
                    right_in.append(inorder[i])
                elif inorder[i] == root:
                    found = True
    
            if not found:
                return None
    
            left_pre = preorder[1:len(left_in)+1]
            right_pre = preorder[len(left_in)+1:]
            
            tree_node = TreeNode(root)
            tree_node.left = self.buildTree(left_pre, left_in)
            tree_node.right = self.buildTree(right_pre, right_in)
    
            return tree_node
    

    迭代版本:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
            if preorder is None or inorder is None or len(preorder) == 0 or len(preorder) != len(inorder):
                return None
    
            stack = []
            root = TreeNode(preorder[0])
            stack.append(root)
            length = len(preorder)
            index = 0
    
            for i in range(1, length):
                next_pre = preorder[i]
                temp = TreeNode(next_pre)
                if stack[-1].val != inorder[index]:
                    stack[-1].left = temp
                    stack.append(temp)
                else:
                    while stack and stack[-1].val == inorder[index]:
                        node = stack[-1]
                        stack.pop()
                        index += 1
                    node.right = temp
                    stack.append(temp)
            return root
    

    返回目录

    剑指 Offer 09. 用两个栈实现队列 -- 2020-9-10

    题目链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/

    题目描述

    用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,
    分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

    示例 1:
    输入:
    ["CQueue","appendTail","deleteHead","deleteHead"]
    [[],[3],[],[]]
    输出:[null,null,3,-1]

    示例 2:
    输入:
    ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
    [[],[],[5],[2],[],[]]
    输出:[null,-1,null,null,5,2]

    提示:
    1 <= values <= 10000
    最多会对 appendTail、deleteHead 进行 10000 次调用

    解题思路

    栈1专门用来推入,栈2用来推出。
    推出时,如果栈2为空,则把栈1的依次取出放入栈2,再推出。
    如果栈2为空,且栈1也为空,则返回-1。

    代码

    class CQueue:
    
        def __init__(self):
            self.stack1, self.stack2 = [], []
    
        def appendTail(self, value: int) -> None:
            self.stack1.append(value)
    
        def deleteHead(self) -> int:
            if self.stack2:
                return self.stack2.pop()
            if not self.stack1:
                return -1
            while self.stack1:
                self.stack2.append(self.stack1.pop())
            return self.stack2.pop()
    

    返回目录

    剑指 Offer 10- I. 斐波那契数列 -- 2020-9-11

    题目链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/

    题目描述

    写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
    F(0) = 0,   F(1) = 1
    F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
    斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
    答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

    示例 1:
    输入:n = 2
    输出:1

    示例 2:
    输入:n = 5
    输出:5

    提示:
    0 <= n <= 100

    解题思路

    用迭代的方法计算。
    还有一种log(n)时间复杂度的公式法。

    代码

    class Solution:
        def fib(self, n: int) -> int:
            a, b = 0, 1
            
            for i in range(n):
                a, b = b, a+b
                
            return a % 1000000007
    

    返回目录

    剑指 Offer 10- II. 青蛙跳台阶问题 -- 2020-9-11

    题目链接:https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/

    题目描述

    一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
    答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

    示例 1:
    输入:n = 2
    输出:2

    示例 2:
    输入:n = 7
    输出:21

    示例 3:
    输入:n = 0
    输出:1

    提示:
    0 <= n <= 100

    解题思路

    如果总共n级台阶,若n大于2,则跳第一步有两种,跳一级和跳两级,f(n) = f(n-1) + f(n-2),还是在计算裴波那切数列。

    代码

    class Solution:
        def numWays(self, n: int) -> int:
            a, b = 1, 1
            
            for i in range(n):
                a, b = b, a+b
                
            return a % 1000000007
    

    返回目录

    剑指 Offer 11. 旋转数组的最小数字 -- 2020-9-11

    题目链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/

    题目描述

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。
    例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

    示例 1:
    输入:[3,4,5,1,2]
    输出:1

    示例 2:
    输入:[2,2,2,0,1]
    输出:0

    解题思路

    使用两个指针,指向最左边和最右边,值为left和right。
    1- 如果left小于right,说明整个数组都排好序了,直接返回left。
    2- 如果left大于right,取中间位置的值mid判断:
    如果mid和left、right都相等,无法判断属于哪边,则退回到顺序遍历处理。
    如果mid大于或者等于left,则该值属于左有序序列,将左边指针指向mid,
    如果mid小于或者等于right,则该值属于右有序序列,将右边指针指向mid。
    如果left和right位置相邻,则返回right。

    代码

    class Solution:
        def minArray(self, numbers: List[int]) -> int:
            left = 0
            right = len(numbers)-1
            mid = left
    
            while(numbers[left] >= numbers[right]):
                if left+1 == right:
                    return numbers[right]
                mid = (left + right)//2
    
                if numbers[mid] == numbers[left] and numbers[mid] == numbers[right]:
                    temp_min = numbers[left]
                    for i in range(len(numbers)):
                        if numbers[i] < temp_min:
                            temp_min = numbers[i]
                    return temp_min
                elif numbers[mid] >= numbers[left]:
                    left = mid
                elif numbers[mid] <= numbers[right]:
                    right = mid
    
            return numbers[mid]
    

    返回目录

    剑指 Offer 12. 矩阵中的路径 -- 2020-10-26

    题目链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/

    题目描述

    请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。
    如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

    [["a","b","c","e"],
    ["s","f","c","s"],
    ["a","d","e","e"]]

    但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

    示例 1:
    输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
    输出:true

    示例 2:
    输入:board = [["a","b"],["c","d"]], word = "abcd"
    输出:false

    提示:
    1 <= board.length <= 200
    1 <= board[i].length <= 200

    解题思路

    思路很简单,就是所谓的回溯法,或者叫DFS。
    不过代码要多写几次,容易出错。

    代码

    class Solution:
        def exist(self, board: List[List[str]], word: str) -> bool:
            if board is None or word is None or len(board) <= 0 or len(board[0]) <= 0 or len(word) <= 0:
                return False
    
            rows = len(board)
            cols = len(board[0])
    
            for i in range(rows):
                for j in range(cols):
                    visited = []
                    for k in range(rows):
                        visited.append([False]*cols)
    
                    if board[i][j] == word[0] 
                        and self.exist_from_here(board, visited, word, 0, rows, cols, i, j):
                        return True
            return False
    
        def exist_from_here(self, board, visited, word, index, rows, cols, x, y):
            if index == len(word)-1:
                return True
    
            visited[x][y] = True
            next_node = self.get_next_node(word[index+1], board, visited, rows, cols, x, y)
    
            for node in next_node:
                if self.exist_from_here(board, visited, word, index+1, rows, cols, node[0], node[1]):
                    return True
            
            visited[x][y] = False
            return False
             
        def get_next_node(self, char, board, visited, rows, cols, x, y):
            order = [[-1, 0], [0, 1], [1, 0], [0, -1]]
    
            rst = []
            for i in range(len(order)):
                new_x = order[i][0] + x
                new_y = order[i][1] + y
                
                if 0 <= new_x < rows and 0 <= new_y < cols 
                    and not visited[new_x][new_y] and board[new_x][new_y] == char:
                    rst.append([new_x, new_y])
            
            return rst
    

    返回目录

    剑指 Offer 13. 机器人的运动范围 -- 2020-10-28

    题目链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/

    题目描述

    地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

    示例 1:
    输入:m = 2, n = 3, k = 1
    输出:3

    示例 2:
    输入:m = 3, n = 1, k = 0
    输出:1

    提示:
    1 <= n,m <= 100
    0 <= k <= 20

    解题思路

    和上题相同,也是回溯法,用递归实现DFS即可。

    代码

    class Solution:
        def movingCount(self, m: int, n: int, k: int) -> int:
            if k < 0 or m <= 0 or n <= 0:
                return 0
            
            visited = [False]*(m*n)
            count = self.moveNext(k, m, n, 0, 0, visited)
            return count
    
        def moveNext(self, k, rows, cols, row, col, visited):
            count = 0
            if(self.check(k, rows, cols, row, col, visited)):
                visited[row*cols+col] = True
                count = 1 + self.moveNext(k, rows, cols, row+1, col, visited) 
                          + self.moveNext(k, rows, cols, row, col+1, visited) 
                          + self.moveNext(k, rows, cols, row-1, col, visited) 
                          + self.moveNext(k, rows, cols, row, col-1, visited)
            return count
        
        def check(self, k, rows, cols, row, col, visited):
            if 0 <= row < rows and 0 <= col < cols 
                and self.getSum(row)+self.getSum(col)<=k and not visited[row*cols+col]:
                    return True
            return False
    
        def getSum(self, num):
            rst = 0
            while num != 0:
                rst += num % 10
                num =  num // 10
            return rst
    

    返回目录

    剑指 Offer 14- I. 剪绳子 -- 2020-10-28

    题目链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/

    题目描述

    给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]k[1]...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

    示例 1:

    输入: 2
    输出: 1
    解释: 2 = 1 + 1, 1 × 1 = 1
    示例 2:

    输入: 10
    输出: 36
    解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
    提示:

    2 <= n <= 58

    解题思路

    可以采用动态规划法(时间复杂度O(n^2), 空间复杂度O(n)),
    贪婪算法(时间复杂度O(1), 空间复杂度O(1))。
    动态规划法求解子问题的思路比较常规,而贪婪算法只要数学基础好就行。

    代码

    class Solution:
        def cuttingRope(self, n: int) -> int:
            if n < 2:
                return 0
            if n == 2:
                return 1
            if n == 3:
                return 2
            
            times_3 = n // 3
            if n % 3 == 1:
                times_3 -= 1
            times_2 = (n - times_3*3)//2
    
            return 3**times_3 * 2**times_2
    

    返回目录

    剑指 Offer 14- II. 剪绳子 II -- 2020-10-28

    题目链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/

    题目描述

    给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]k[1]...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

    答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

    示例 1:
    输入: 2
    输出: 1
    解释: 2 = 1 + 1, 1 × 1 = 1

    示例 2:
    输入: 10
    输出: 36
    解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

    提示:
    2 <= n <= 1000

    解题思路

    题目和上题几乎一样,最后取个模就可以。取模的话,在中间过程中取也是一样的。

    代码

    class Solution:
        def cuttingRope(self, n: int) -> int:
            if n < 2:
                return 0
            if n == 2:
                return 1
            if n == 3:
                return 2
            
            times_3 = n // 3
            if n % 3 == 1:
                times_3 -= 1
            times_2 = (n - times_3*3)//2
    
            rst = 1
            for i in range(times_3):
                rst *= 3
                rst %= 1000000007
            for i in range(times_2):
                rst *= 2
                rst %= 1000000007
            return rst
    

    返回目录

    剑指 Offer 15. 二进制中1的个数 -- 2020-10-29

    题目链接:https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/

    题目描述

    请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

    示例 1:
    输入:00000000000000000000000000001011
    输出:3
    解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

    示例 2:
    输入:00000000000000000000000010000000
    输出:1
    解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

    示例 3:
    输入:11111111111111111111111111111101
    输出:31
    解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

    解题思路

    两种情况,第一种,最右边是1,如1011,则减去1为1010,与原数与位运算得1010。
    第二种情况,最右边是0,如1100,则减去1为1011,与原数与位运算得1000。
    用这种技巧可以快速求得结果,也不用担心因为对负数移位导致的死循环问题。

    代码

    class Solution:
        def hammingWeight(self, n: int) -> int:
            cnt = 0
            while(n):
                cnt += 1
                n = (n-1) & n
            return cnt
    

    返回目录

    题目链接:

    题目描述

    解题思路

    代码

    
    

    返回目录

  • 相关阅读:
    Python基本数据类型
    Python内存相关
    Python运算符和编码
    js比较日期大小 判断日期
    js判断一个数是不是正整数
    sql查询排序
    js获取select标签选中的值
    PL/sql配置相关
    搜狗的好玩用法
    Oracle数据库中的dual表
  • 原文地址:https://www.cnblogs.com/yanqiang/p/13631963.html
Copyright © 2011-2022 走看看