Divide and conquer algorithm and binary search

First and second search

Introduction : Binary search is also called binary search. It is a more efficient search method, provided that the data structure must be sorted first, and the search can be completed within the logarithmic time complexity of the data scale. However, binary search requires that the linear table has the characteristics of random access (such as an array), and it also requires the linear table to be able to speculate the nature of the elements on both sides according to the characteristics of the middle element, so as to reduce the scale of the problem.
Code template:

#常规
def bsearch(l, r, target):
	while l <= r:
		mid = (l + r) // 2
		if nums[mid] > target:
			right = mid - 1
		elif  nums[mid] < target:
			left = mid + 1
	return mid

'''
特殊情况:由于部分题左侧区间保留中间值(因为我们计算中间值向下取整,
这是为什么左侧区间保留中间值,右侧不保留的原因),保留中间值就需要
考虑啊是否会出现死循环的问题。
'''
def bsearch(l, r, target):
	while l <= r:
		mid = (l + r) // 2
		if nums[mid] > target:
			right = mid			#保留中间值
		elif  nums[mid] < target:
			left = mid + 1
		else:					#防止出现死循环
			break			
	return mid

Example 1 ( leetcode33 )
Suppose that the array sorted in ascending order is rotated at a point unknown in advance.
(For example, the array [0,1,2,4,5,6,7] may become [4,5,6,7,0,1,2]).
Search for a given target value, if the target value exists in the array, then return its index, otherwise it returns -1.
You can assume that there are no duplicate elements in the array.
Your algorithm time complexity must be O (log n) level.

样例:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
解法一:一边循环直接出结果
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        #由于旋转导致一边符合升序,另一边有可能不符合升序
        left, right = 0, len(nums) - 1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target: return mid
            #等号满足区间长度为2,左侧为正确答案
            if nums[left] <= nums[mid]:	
                if nums[left] <= target <= nums[mid]:
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                if nums[mid] <= target <= nums[right]:
                    left = mid + 1
                else:
                    right = mid - 1
        return -1
        
解法二:更为常规的解法
思路:两次运用二分查找,第一次寻找旋转点,第二次寻找目标值。
其中第二次直接套用模板,第一次对于边界条件考虑比较麻烦。
```python
class Solution:
    def search(self, nums, target):
    	#通过二分算法获取转折点
        def bfind_rotate(left, right):
            while left <= right:
                mid = (left + right) // 2
                if mid < len(nums) - 1 and nums[mid] > nums[mid + 1]:
                    return mid
                #由于二分往往向下取整,所以当选左侧的时候可以加上右边界
                elif nums[mid] < nums[left]:
                    right = mid				
                elif nums[right] < nums[mid]:
                    left = mid + 1
                #如果左侧区间右边界保留中间值,就需要考虑是否出现死循环
                else:
                    break
            return -1
        #通过二分算法获取目标值索引           
        def bfind(left, right, target):
            while left <= right:
                mid = (left + right) // 2
                if nums[mid] == target:
                    return mid
                elif nums[mid] < target:
                    left = mid + 1
                elif nums[mid] > target:
                    right = mid - 1
                #else:  由于左侧区间没有保留右边界,所以不需要考虑死循环
                #   break
            return -1
        #特殊情况处理
        if not nums: return -1
        if len(nums) == 1 and nums[0] == target: return 0
        elif len(nums) == 1: return -1
        left, right = 0, len(nums) - 1
        if nums[right] > nums[left]: return bfind(left, right, target)
        else: rotate = bfind_rotate(left, right)
        #判断目标值在旋转前还是旋转后
        if target >= nums[0]: return bfind(0, rotate, target)
        return bfind(rotate + 1, right, target)

Example 2 ( 74. Search for a two-dimensional matrix )
write an efficient algorithm to determine whether there is a target value in the mxn matrix. The matrix has the following characteristics:
the integers in each row are arranged in ascending order from left to right.
The first integer on each line is greater than the last integer on the previous line.

示例 1:
输入:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 3
输出: true
思路:本题先对于第一列使用二分查找寻找到目标值所在的行row,条件是target值大于当前行起始值,小于下一行起始值
然后对于row行进行第二次二分查找,寻找目标target.

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
            if not matrix or not matrix[0]: return False	#特殊情况处理
            #对行应用二分查找,由于二分查找过程中会忽略最后一行,所以直接初始化row为最后一行
            row, bottom, top= len(matrix) - 1, 0, len(matrix) - 1
            while bottom <= top:
                mid = (bottom + top) // 2
                if matrix[mid][0] == target: return True
                elif mid < len(matrix) - 1 and matrix[mid][0] < target and matrix[mid + 1][0] > target:
                    row = mid
                    break
                elif matrix[mid][0] < target: bottom = mid + 1
                elif matrix[mid][0] > target: top = mid - 1
            #判断当row是最后一行时,是否符合题意
            if matrix[row][-1] < target : return False

			#对第row行进行二分查找
            left, right = 0, len(matrix[0]) - 1
            while left <= right:
                mid = (left + right) // 2
                if matrix[row][mid] == target:
                    return True
                if matrix[row][mid] < target:
                    left = mid + 1
                elif matrix[row][mid] > target:
                    right = mid - 1
            return False              

Two, divide and conquer algorithm

Introduction: In computer science, the divide-and-conquer method is a very important algorithmic paradigm based on multi-branch recursion. The literal interpretation is "divide and conquer", which is to divide a complex problem into two or more sub-problems that are the same or similar. Until the last sub-problem can be simply solved directly, the solution of the original problem is the combination of the sub-problem solutions .
This technique is the basis of many efficient algorithms, such as sorting algorithms (quick sorting, merge sorting), Fourier transform (fast Fourier transform).
Insert picture description here

Example one quick sort

class Solution {
    public int[] smallestK(int[] arr, int k) {
        helper1(0, arr.length - 1, arr);
        return Arrays.copyOf(arr, k);
    }
    public void quickSort(int start, int end, int[] nums){
    	//当排序数组长度小于等于1时直接中止排序
        if(start >= end) return;
        //在start处挖个坑, 保存起始位置
        int temp = nums[start], t0 = start, t1 = end;
        while(start < end){
        	//注:先向前遍历,当队尾的元素大于等于基准数据时,向前挪动high指针
        	//start > end 防止start越界end
            while(start < end && nums[end] >= temp) end--;
            //将队尾元素小于基准数据的值放到坑里,将坑变为end所在处
            swap(start, end, nums);
            //向后遍历,当队首的元素小于等于基准数据时,向后挪动start指针
            //start < end 防止start越界end
            while(start < end && nums[start] <= temp) start++;
            //将队首元素小于基准数据的值放到坑里,将坑变为start所在处
            swap(start, end, nums);
        }
        //对temp左右两侧继续排序
        quickSort(t0, start - 1, nums);
        quickSort(start + 1, t1, nums);
    }
    
    public void swap(int start, int end, int[] nums){
        int temp = nums[start];
        nums[start] = nums[end];
        nums[end] = temp;
    }
}

Example II beautiful array
for some fixed N, if the arrangement of the array A is an integer of 1, 2, ..., N of the composition, such that:
for each i <j, k satisfies not exist i <k <j such that A [k ] * 2 = A [i] + A [j].
Then array A is a beautiful array.
Given N, return any pretty array A (guarante one exists).

示例 1:
输入:4
输出:[2,1,4,3]
思路:漂亮数组有以下性质:
(1)A是一个漂亮数组,如果对A中所有元素乘以一个常数,那么A还是一个漂亮数组。
(2)A是一个漂亮数组,如果删除一些A中所有元素,那么A还是一个漂亮数组。
(3)A是一个奇数构成的漂亮数组,B是一个偶数构成的漂亮数组,那么A+B也是一个漂亮数组
#我的做法,非分治
class Solution:
    def beautifulArray(self, N: int) -> List[int]:
        def DAC(memo):
            if len(memo) >= N: return memo
            left = [i * 2 - 1 for i in memo]
            right = [i * 2 for i in memo]
            memo = left + right
            return DAC(memo)
        memo = DAC([1])
        length = len(memo)
        for i in range(N, length):
            memo.remove(i + 1)
        return memo
        
#官方题解 上一种解法的性质依然继续沿用
class Solution:
    def beautifulArray(self, N):
        memo = {1: [1]}
        def f(N):
            if N not in memo:
            	#性质一
                odds = f((N+1)//2)	#奇数数组是由(N+1)/2个数字组成的漂亮数组放射而成
                evens = f(N//2)		#偶数数组是由	N/2个数字组成的漂亮数组放射而成
                #性质三
                memo[N] = [2*x-1 for x in odds] + [2*x for x in evens]
            return memo[N]
        return f(N)

The third question merge sort
Given an array of integers nums, sort the array in ascending order.

class Solution {
    int[] arr;
    public int[] sortArray(int[] nums) {
        arr = nums;
        helper(0, nums.length - 1);
        return arr;
    }
	//分治算法
    public void helper(int start, int end){
        if(start >= end) return;
        int mid = (start + end) / 2;
        //将整体分为左右两个子区间进行排序
        helper(start, mid);
        helper(mid + 1, end);
        //合并子区间后重新排序
        insertSort(start, end);
    }
    public void insertSort(int start, int end){
        if(start >= end) return; //只有一个数字不需要排序
        int mid = (start + end) / 2; int i, j; //由归并排序可知待排数组左右两区间均完成排序
        //插入排序
        for(i = mid; i<=end; i++){
            int index = i - 1, temp = arr[i];
            //注意index >= start而不是0,用于规范排序区间 
            //注意arr[index] >= temp 而不是 arr[index] >= arr[i] 因为arr[i]会发生变化
            while(index >= start && arr[index] >= temp) {arr[index + 1] = arr[index]; index -= 1;}
            arr[index + 1] = temp;
        }
    }
}
Published 16 original articles · Like1 · Visits 379

Guess you like

Origin blog.csdn.net/qq_41174940/article/details/104231474