python数据结构之二分查找与分治算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haiyu94/article/details/79468289

目录

  1. 二分查找的基础知识
  2. 插入位置(LeetCode 35)
  3. 区间查找(LeetCode 34)
  4. 旋转数组查找(LeetCode 33)
  5. 分治算法与归并排序
  6. K个排序链表的合并(LeetCode 23)
  7. 逆序数计算 (LeetCode 315)
  8. 不同的括号方法 (LeetCode 241)

1. 二分查找的基础知识

对有序列表进行查找,通过将查找值与列表候选区间中间位置的值比较,可以使候选区减少一半。
(1)在一段区间内,找到中间位置的值
(2)比较要查找的值与中间位置值的大小,若相等,则返回,若不相等:如果中间值大一点,则下一步候选区域变为左区域,如果中间值大一点,则下一步候选区域变为右区域。
(3)如此重复,直到找到要查找的数字。

2. 插入位置(LeetCode 35 Search Insert Position)

2.1题目

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You may assume no duplicates in the array.
Example 1:
Input: [1,3,5,6], 5
Output: 2
Example 2:
Input: [1,3,5,6], 2
Output: 1
Example 3:
Input: [1,3,5,6], 7
Output: 4
Example 1:
Input: [1,3,5,6], 0
Output: 0

2.2思路

二分查找

2.3代码

class Solution(object):
    def searchInsert(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        size = len(nums)
        first = 0
        last = size - 1
        while first <= last:
            mid = (first + last) / 2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                last = mid - 1
            elif nums[mid] < target:
                first = mid + 1
        return first

3. 区间查找(LeetCode 34 Search for a Range)

3.1题目

Given an array of integers sorted in ascending order, find the starting and ending position of a given target value.
Your algorithm’s runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

3.2思路

使用二分查找分别对区间的左右端点进行查找。

3.3代码

class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        left = self.findLeft(nums, target)
        right = self.findRight(nums, target)
        return [left, right]


    def findLeft(self, nums, target):
        size = len(nums)
        first = 0
        last = size - 1
        while first <= last:
            mid = (first + last) / 2
            if nums[mid] == target:
                if mid == 0 or nums[mid - 1] != nums[mid]:
                    return mid
                else:
                    last = mid - 1
            elif nums[mid] > target:
                last = mid - 1
            else:
                first = mid + 1
        return -1
    def findRight(self, nums, target):
        size = len(nums)
        first = 0
        last = size - 1
        while first <= last:
            mid = (first + last) / 2
            if nums[mid] == target:
                if mid == size - 1 or nums[mid] != nums[mid + 1]:
                    return mid
                else:
                    first = mid + 1
            elif nums[mid] > target:
                last = mid - 1
            else:
                first = mid + 1
        return -1

4. 旋转数组查找(LeetCode 33 Search in Rotated Sorted Array)

4.1题目

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.

4.2思路

使用二分查找,在查找过程中,变换查找区间时需注意该位置左右两边是否有旋转点。可以将旋转列表分为左右两个部分,则左右两个部分都是递增列表,而且左部分第一个数要比右部分最后一个数要大。故在改变候选区间时,需要判断,mid的位置是在左区间还是右区间。

4.3代码

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        size = len(nums)
        first = 0
        last = size
        while first != last:
            mid = (first + last)//2
            if nums[mid] == target:
                return mid
            if nums[first] <= nums[mid]:
                if nums[first] <= target and target < nums[mid]:
                    last = mid
                else:
                    first = mid +1
            else:
                if nums[mid] < target and target <= nums[last-1]:
                    first = mid + 1
                else:
                    last = mid
        return -1

5.分治算法与归并排序

这里的基础知识主要参考博客http://blog.csdn.net/l597692583/article/details/51131694
分治法是将原来的问题的分解为几个规模较小的但与原问题类似的子问题,然后使用算法多次递归调用自身来解决这些紧密相关的若干子问题,然后再合并这些子问题来解决原问题。
分治法(Divide and Conquer)解决问题遵循三个步骤:
①:分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。
②:解决这些子问题,递归求解这些子问题。若子问题规模足够小,则直接求解。
③:合并这些子问题的解构成原问题的解。

归并排序遵循分治思想:
①:分解n个元素的待排序列为2个各具n/2个元素的待排序列。
②:使用归并排序递归的排序两个子序列。
③:合并两个已排序的子序列的结果。
归并排序的关键点在于合并子序列结果。
归并排序是稳定的,任何情况下的时间复杂度均为O(nlgn),但是需要O(n)的额外辅助空间以及需要递归调用自身。实际情况中,若是大规模问题,O(n)的额外空间开销值得思考。

6. K个排序链表的合并(LeetCode 23 Merge k Sorted Lists)

6.1题目

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

6.2思路

先实现链表的两两合并,直到最后两个进行合并,使用递归的思想

6.3代码

class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        size = len(lists)
        if size == 0:
            return None
        if size == 1:
            return lists[0]
        n = size//2
        temp1 = self.mergeKLists(lists[:n])
        temp2 = self.mergeKLists(lists[n:])
        return self.mergeTwoLists(temp1, temp2)
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        ans = ListNode(0)
        temp = ans
        if l1 == None and l2 == None:
            return None
        while l1 !=None or l2 != None:
            if l1 == None:
                while l2 != None:
                    temp.val = l2.val
                    l2 = l2.next
                    if l2 == None:
                        break
                    temp.next = ListNode(0)
                    temp = temp.next
                break
            if l2 == None:
                while l1 != None:
                    temp.val = l1.val
                    l1 = l1.next
                    if l1 == None:
                        break
                    temp.next = ListNode(0)
                    temp = temp.next
                break
            if l1.val <= l2.val:
                temp.val = l1.val
                l1 = l1.next
            else:
                temp.val = l2.val
                l2 = l2.next
            temp.next = ListNode(0)
            temp = temp.next
        return ans

7. 逆序数计算 (LeetCode 315 Count of Smaller Numbers After Self)

7.1题目

You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example:

Given nums = [5, 2, 6, 1]

To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.
Return the array [2, 1, 1, 0].

7.2思路

列表中某个数右边比其小的数,正是那些在一个排序过程中,需要将其从该数的右边挪到左边的数,所以可以使用归并排序的思想,将那些需要执行从右到左的数进行累加。

7.3代码

class Solution(object):
    def countSmaller(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        def sort(enum):
            half = len(enum) / 2
            if half:
                left, right = sort(enum[:half]), sort(enum[half:])
                for i in range(len(enum))[::-1]:
                    if not right or left and left[-1][1] > right[-1][1]:
                        smaller[left[-1][0]] += len(right)
                        enum[i] = left.pop()
                    else:
                        enum[i] = right.pop()
            return enum
        smaller = [0] * len(nums)
        sort(list(enumerate(nums)))
        return smaller

8. 不同的括号方法 (LeetCode 241 Different Ways to Add Parentheses)

8.1题目

Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +, - and *.

Example 1
Input: “2-1-1”.

((2-1)-1) = 0
(2-(1-1)) = 2
Output: [0, 2]

Example 2
Input: “2*3-4*5”

(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10
Output: [-34, -14, -10, -10, 10]

8.2思路

归并递归,逐一将+,-,*左右两边看成运算式

8.3代码

class Solution(object):
    def diffWaysToCompute(self, input):
        """
        :type input: str
        :rtype: List[int]
        """
        ans = []
        for i in range(len(input)):
            c = input[i]
            if c in '+-*':
                a = self.diffWaysToCompute(input[:i])
                b = self.diffWaysToCompute(input[i + 1:])
                for m in a:
                    for n in b:
                        if c == '+':
                            ans.append(m + n)
                        elif c == '-':
                            ans.append(m - n)
                        elif c == '*':
                            ans.append(m * n)

        if not ans:
            ans.append(int(input))

        return ans

猜你喜欢

转载自blog.csdn.net/haiyu94/article/details/79468289