剑指offer解题思路简述:51-60

  • 面试题51:数组中的逆序对

剑指offer解题思路简述:51-60_第1张图片

方法一:得到一个排序后的数组,然后遍历原数组,查找当前数字在排序后数组中的位置,即为当前数字的逆序数,累加后在原数组中删掉该数,接着往后处理,会超时

class Solution:
    def reversePairs(self, nums: List[int]) -> int:
        count = 0
        sorted_nums = sorted(nums)
        for i in nums:
            count = count+sorted_nums.index(i)
            sorted_nums.remove(i)
        return count

方法二:剑指方法,参考归并排序,先把所有的数据分成每两个一个小组,统计逆序数后排序,然后把相邻的每两个含有两个数字的小组组成一个有四个数字的组,在组合的时侯统计逆序对并排序

class Solution:
    def mergeSort(self, nums, tmp, l, r):
        if l >= r:
            return 0

        mid = (l + r) // 2
        inv_count = self.mergeSort(nums, tmp, l, mid) + self.mergeSort(nums, tmp, mid + 1, r)
        i, j, pos = l, mid + 1, l
        while i <= mid and j <= r:
            if nums[i] <= nums[j]:
                tmp[pos] = nums[i]
                i += 1
                inv_count += (j - (mid + 1))
            else:
                tmp[pos] = nums[j]
                j += 1
            pos += 1
        for k in range(i, mid + 1):
            tmp[pos] = nums[k]
            inv_count += (j - (mid + 1))
            pos += 1
        for k in range(j, r + 1):
            tmp[pos] = nums[k]
            pos += 1
        nums[l:r+1] = tmp[l:r+1]
        return inv_count

    def reversePairs(self, nums: List[int]) -> int:
        n = len(nums)
        tmp = [0] * n
        return self.mergeSort(nums, tmp, 0, n - 1)
  • 面试题52:两个链表的第一个公共节点

方法一:先遍历第一个链表,每个节点都在链表二中查找有没有,时间复杂度m*n

方法二:因为这种链表呈现的是一个y字型,所以可以从后往前比较,第一个出现不同的节点就是分叉的地方,所以可以用两个栈分别存下两条路径的节点,然后从后往前比较,时间复杂度m+n,空间复杂度m+n

方法三:先挨个遍历链表记录两个的长度,然后让长的先走差值步,短的再开始走,碰到的第一个相同的节点即为第一个公共节点

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

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        len_A = 0
        len_B = 0
        temp_A = headA
        temp_B = headB
        while headA:
            len_A += 1
            headA = headA.next
        while headB:
            len_B += 1
            headB = headB.next
        if len_A > len_B:
            headA = temp_A
            headB = temp_B
            for i in range(len_A - len_B):
                headA = headA.next
            while headA != headB and headA:
                headA = headA.next
                headB = headB.next
        else:
            headA = temp_A
            headB = temp_B
            for i in range(len_B - len_A):
                headB = headB.next
            while headA != headB and headA:
                headA = headA.next
                headB = headB.next
        if not headA:
            return None
        else:
            return headA
  • 面试题53:在排序数组中查找数字

剑指offer解题思路简述:51-60_第2张图片

方法:二分查找分别找到第一个k记下索引,再找到第二个k记下索引,求差,时间复杂度nlogn

class Solution:
    def search(self, nums: [int], target: int) -> int:
        # 搜索右边界 right
        i, j = 0, len(nums) - 1
        while i <= j:
            m = (i + j) // 2
            if nums[m] <= target: i = m + 1
            else: j = m - 1
        right = i
        # 若数组中无 target ,则提前返回
        if j >= 0 and nums[j] != target: return 0
        # 搜索左边界 left
        i = 0
        while i <= j:
            m = (i + j) // 2
            if nums[m] < target: i = m + 1
            else: j = m - 1
        left = j
        return right - left - 1

剑指offer解题思路简述:51-60_第3张图片

方法:二分查找,如果中间数的索引和数值相等则检查右边,反之检查左边

剑指offer解题思路简述:51-60_第4张图片

方法:二分查找,如果中间数的索引小于数值,则检查左边,反之检查右边

  • 面试题54:二叉搜索树的第K大节点

方法:二叉搜索树中序遍历得到的就是一个排序的结果

class Solution:
    # 返回对应节点TreeNode
    def KthNode(self, pRoot, k):
        # write code here
        global list
        list = []
        self.inorder(pRoot)
        if k <= 0 or k > len(list):  # 越界
            return None
        return list[k-1]
         
    def inorder(self,pRoot):
        if pRoot == None:
            return None
        self.inorder(pRoot.left)
        list.append(pRoot)
        self.inorder(pRoot.right)
  • 面试题55:二叉树的深度

剑指offer解题思路简述:51-60_第5张图片

方法一:递归,二叉树的深度等于max(左子树深度,右子树深度)+1

def depth(root):
    if root is None:
        return 0
    ld = depth(root.left)
    rd = depth(root.right)
    return max(ld,rd)+1

方法二:迭代,层序遍历,每遍历完一层深度累加1

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root: return 0
        queue, res = [root], 0
        while queue:
            tmp = []
            for node in queue:
                if node.left: tmp.append(node.left)
                if node.right: tmp.append(node.right)
            queue = tmp
            res += 1
        return res

剑指offer解题思路简述:51-60_第6张图片

方法:后序遍历,在遍历的时候记录每个节点的深度

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def recur(root):
            if not root: return 0
            left = recur(root.left)
            if left == -1: return -1
            right = recur(root.right)
            if right == -1: return -1
            return max(left, right) + 1 if abs(left - right) <= 1 else -1

        return recur(root) != -1
  • 面试题56:数组中数字出现的次数

剑指offer解题思路简述:51-60_第7张图片

方法:利用的思想是,一个数字抑或自己结果为0,所以先将所有的数字抑或一次,得到的结果不为0,找到为1的位,用该位将原始数据分成两类,所以两个不相同的数字也被分到了两个组里,然后对每组进行刚才的操作,最后的结果就是两个要求的只出现一次的数

class Solution(object):
    def singleNumbers(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        temp = 0
        for i in nums:
            temp = temp^i
        flag = 1
        #从右往左找到第一个出现1的位
        while(1):
            bitone = temp & flag
            if bitone!=0:
                break
            flag = flag<<1
        num1 =[]
        num2 = []
        for i in nums:
            if bitone&i==bitone:
                num1.append(i)
            else:
                num2.append(i)
        
        temp1 = 0
        for i in num1:
             temp1 = temp1^i
        
        temp2 = 0
        for i in num2:
             temp2 = temp2^i
        
        return([temp1,temp2])

剑指offer解题思路简述:51-60_第8张图片

方法1:把所有数字的二进制表示的每一位加起来,最后的结果如果那一位可以被3整除,说明只出现一次的那个数的那一位是0,反之是1,需要一个长度为32位的辅助空间,时间复杂度n

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        res = 0
        for i in range(32):
            cnt = 0
            idx = 1<

方法2:(set(num)求和乘以3减去num和)//2

方法3:将每个数的次数存到哈希表中,结束后查哈希表中只出现一次的数字

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dic = {}
        for i in nums:
            if i in dic:
                dic[i] += 1
            else:
                dic[i] = 1       
        for i,j in dic.items():
            if j == 1:
                return i
  • 面试题57:和为s的数字

剑指offer解题思路简述:51-60_第9张图片

方法:设置两个指针分别位于数组的头和尾,然后根据和的大小来往前或者往后移动

#如果有多对,输出积最小的
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        i = 0
        j = len(array)-1
        result = []
        while itsum):
                j = j-1
            elif (array[i]+array[j]

剑指offer解题思路简述:51-60_第10张图片

方法:初始化一个[1,2]的list,如果该序列的数小于s,则后面增加一个3,如果大,则前面减少一个1,while的终止条件是,small

class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        result = []
        i=1
        j=2
        sumnum =i+j
        while itsum:
                sumnum = sumnum-i
                i = i+1
            else:
                j = j+1
                sumnum = sumnum+j
        return result
  • 面试题58:翻转字符串

剑指offer解题思路简述:51-60_第11张图片

方法一:最直观的方法,从后往前挨个单词找,放到新的list中,直到把每次单词都找到

class Solution:
    def reverseWords(self, s: str) -> str:
        s = s.strip() # 删除首尾空格
        i = j = len(s) - 1
        res = []
        while i >= 0:
            while i >= 0 and s[i] != ' ': 
                i -= 1 # 搜索首个空格
            res.append(s[i + 1: j + 1]) # 添加单词
            while s[i] == ' ': 
                i -= 1 # 跳过单词间空格
            j = i # j 指向下个单词的尾字符
        return ' '.join(res) # 拼接并返回

方法二:翻转两次,先整体翻转,再单词内部翻转,需要单独写一个函数用于翻转

方法三:用split函数分割到list中,然后reverse()

剑指offer解题思路简述:51-60_第12张图片

方法一:分成两部分ab和cdefg,先分别翻转这两部分ba和gfedc,再翻转整个字符串

方法二:list切片s[n:]+s[:n]

  • 面试题59:队列的最大值

剑指offer解题思路简述:51-60_第13张图片

方法一:简单直接每次max(k)个数的最大值,依次保存,时间复杂度kn

class Solution:
    def maxInWindows(self, num, size):
        # write code here
        if size==0 or size>len(num):
            return []
        temp = num[0:size]
        tempmax = max(temp)
        result = [tempmax]
        for i in range(size,len(num)):
            if tempmax==num[i-size] or num[i]>tempmax:
                tempmax = max(num[i-size+1:i+1])
            result.append(tempmax)
        return result

方法二:在一个新list里,每次保存当前的最大值的索引,当往后移动一位时,先核对下标,若最大值下表在比新数小k的范围内,比较新数和当前的最大值,若小于最大值,则将新数的索引append到list后,若大于最大值,则直接替换掉原list。太难了。。。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        deque = [];result = [] # deque也可以用collection里的双端队列实现
        for i in range(0, len(nums)):
            while deque and nums[i]>nums[deque[-1]]: # 只存有可能成为最大值的数字的index进deque
                deque.pop()
            deque.append(i)
            while i-deque[0]>k-1: # 如果相距超过窗口k长度则弃掉
                deque.pop(0)
            if i >= k-1: #前两个不做处理
                result.append(nums[deque[0]]) # 这过程中始终保持deque[0]为最大值的index
        return result

剑指offer解题思路简述:51-60_第14张图片

方法:设置一个辅助队列queuemax用来存放当前状态下的最大值,具体操作为,每当push时,从后开始比较queuemax的值和当前要插入的值,将比它小的都删除,然后压入当前值,pop时,直接返回queuemax中的值,当pop的值和queuemax中最前面的值相等的时候,将queuemax最前面的值也pop出。

class MaxQueue(object):

    def __init__(self):
        self.que = []
        self.sort_que = []   

    def max_value(self):
        return self.sort_que[0] if self.sort_que else -1   

    def push_back(self, value):

        self.que.append(value)
        while self.sort_que and self.sort_que[-1] < value:
            self.sort_que.pop()
        self.sort_que.append(value)
        
    def pop_front(self):

        if not self.que: return -1
        res = self.que.pop(0)
        if res == self.sort_que[0]:
            self.sort_que.pop(0)
        return res
  • 面试题60:n个骰子的点数

方法1:迭代,找规律,掷n次可能出现的点数和的所有可能性为6*n,用cnts数组记录对应点数出现的次数,初始值为[0,1,1,1,1,1,1,0,0,0,0.....],每掷一次从后往前更新。

class Solution:
    def twoSum(self, n: int) -> List[float]:
        if n == 0:
            return []
        # 初始化 1 - 6 是 1次,7 - n 是 0 次。
        # 编号从1开始,这样方便写代码。  为了从1开始,我们只需要在数组前面随便push一个元素即可,比如本例的0
        cnts = [0] + [1] * 6 + [0] * (6 * n - 6)
        # 模拟投掷 n - 1 次
        for _ in range(n - 1):
            # 从后向前更新
            for i in range(6 * n, 0, -1):
                cnts[i] = sum(cnts[max(i - 6, 0): i])

        return [i/6**n for i in cunts[1:]]

方法二:递归

class Solution:
    def twoSum(self, n: int) -> List[float]:
        def diceCnt(n):
            if n == 1:
                return [0] + [1] * 6
            cnts = diceCnt(n - 1) + [0] * 6
            for i in range(6 * n, 0, -1):
                cnts[i] = sum(cnts[max(i - 6, 0): i])
            return cnts
        return [i/6**n for i in diceCnt(n)[1:]] 

 

你可能感兴趣的:(剑指offer解题思路简述:51-60)