python數據結構之二分查找與分治算法

目錄

  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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章