• Chapter 2: Binary Search & Sorted Array


    二分查找的前提条件是数组是完全有序或者部分有序,其时间复杂度通常为O(logn).

    二分查找模板(可应对多种不同需求)

    public class BinarySearchTemplate {
    public int binarySearch(int[] nums, int target) {
    	if (nums == null || nums.length == 0) {
    		return -1;
    	}
    	
    	int begin = 0, end = nums.length - 1;
    	while (begin + 1 < end) {
    		int mid = begin + (end - begin)/2;
    		if (nums[mid] == target) {
    			return mid; // can be modified
    		}
    		if (nums[mid] < target) {
    			begin = mid;
    		}else {
    			end = mid;
    		}
    	}
    	
    	if (nums[begin] == target) {
    		return begin; // can be modified
    	}
    	if (nums[end] == target) {
    		return end; // can be modified
    	}
    	
    	return -1;
    }
    }
    

    可使用上述模板解决的问题

    Problem 1: Search Insert Position

    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.

    Here are few examples.
    [1,3,5,6], 5 → 2
    [1,3,5,6], 2 → 1
    [1,3,5,6], 7 → 4
    [1,3,5,6], 0 → 0

    (leetcode: https://leetcode.com/problems/search-insert-position/)

    Solution

    public class Solution {
        public int searchInsert(int[] nums, int target) {
    		if (nums == null || nums.length == 0) {
    			return 0;
    		}
    
    		int begin = 0, end = nums.length - 1;
    		while (begin + 1 < end) {
    			int mid = begin + (end - begin) / 2;
    			if (nums[mid] < target) {
    				begin = mid;
    			} else {
    				end = mid;
    			}
    		}
    
    		if (target <= nums[begin]) {
    		    return begin;
    		}
    		if (target > nums[end]) {
    		    return end + 1;
    		}
    		return end;
        }
    }
    

    Problem 2: Search for a Range

    Given a sorted array of integers, 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].

    Solution

    public class Solution {
        public int[] searchRange(int[] nums, int target) {
            int[] res = {-1, -1};
            if (nums == null || nums.length == 0) {
    			return res;
    		}
    
    		res[0] = getLowerIdx(nums, target);
    		if (res[0] == -1) {
    		    return res;
    		}
    		res[1] = getHigherIdx(nums, target);
    		return res;
        }
        
        public int getLowerIdx(int[] nums, int target) {
            int begin = 0, end = nums.length - 1;
    		while (begin + 1 < end) {
    			int mid = begin + (end - begin) / 2;
    			if (nums[mid] < target) {
    				begin = mid;
    			} else {
    				end = mid;
    			}
    		}
    		if (nums[begin] == target) {
    			return begin; 
    		}
    		if (nums[end] == target) {
    			return end; 
    		}
    		return -1;
        }
        
        public int getHigherIdx(int[] nums, int target) {
            int begin = 0, end = nums.length - 1;
    		while (begin + 1 < end) {
    			int mid = begin + (end - begin) / 2;
    			if (nums[mid] <= target) {
    				begin = mid;
    			} else {
    				end = mid;
    			}
    		}
    		if (nums[end] == target) {
    			return end; 
    		}
    		if (nums[begin] == target) {
    			return begin; 
    		}
    		return -1;
        }
    }
    

    Problem 3: Search in Rotated Sorted Array

    Suppose a sorted array 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.

    Solution

    public class Solution {
        public int search(int[] nums, int target) {
            if (nums == null || nums.length == 0) {
    			return -1;
    		}
    
    		int begin = 0, end = nums.length - 1;
    		while (begin + 1 < end) {
    			int mid = begin + (end - begin) / 2;
    			if (nums[mid] == target) {
    				return mid;
    			}
    			if (nums[mid] > target) {
    			    if (target < nums[begin] && nums[mid] > nums[begin]) {
    			        begin = mid;
    			    } else {
    			        end = mid;
    			    }
    			} else {
    			    // note: boundary
    			    if (target >= nums[begin] && nums[mid] < nums[begin]) {
    			        end = mid;
    			    } else {
    			        begin = mid;
    			    }
    			}
    		}
    
    		if (nums[begin] == target) {
    			return begin; 
    		}
    		if (nums[end] == target) {
    			return end; 
    		}
    
    		return -1;
        }
    }
    

    容易错误的Case:

    [5, 1, 3] , 5;

    [1, 3, 5], 1;

    Problem 4: Square Root

    用最快的方法求一个数的平方根。

    public class SquareRoot {
    	public double getSquareRoot(double d) {
    		if (d < 0) {
    			return -1;
    		}
    		if (d == 0 || d == 1) {
    			return d;
    		}
    
    		double low = 0.0;
    		double high = d < 1.0 ? 1 : d;
    		double epsilo = 1e-6;
    		double mid = 0;
    
    		while (true) {
    			mid = low + (high - low) / 2.0;
    			if (Math.abs(mid * mid - d) < epsilo) {
    				break;
    			}
    			if (mid * mid < d) {
    				low = mid;
    			} else {
    				high = mid;
    			}
    		}
    
    		return mid;
    	}
    }
    

    Problem 5: Find Minimum in Rotated Array

    Suppose a sorted array 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).

    Find the minimum element.

    You may assume no duplicate exists in the array.

    public class Solution {
        public int findMin(int[] nums) {
            if (nums == null || nums.length == 0) {
                return 0;
            }
            
            int low = 0, high = nums.length - 1;
            if (nums[high] > nums[low]) {
                return nums[low];    
            }
            
            while (low + 1 < high) {
                int mid = low + (high - low) / 2;
                if (nums[mid] < nums[mid - 1] && nums[mid] < nums[mid + 1]) {
                    return nums[mid];
                }
                if (nums[mid] > nums[low]) {
                    low = mid;
                } else {
                    high = mid;
                }
            }
            
            return nums[low] < nums[high] ? nums[low] : nums[high];
        }
    }
    

    Follow up

    Follow up for "Find Minimum in Rotated Sorted Array":
    What if duplicates are allowed?

    Would this affect the run-time complexity? How and why?

    Suppose a sorted array 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).

    Find the minimum element.

    The array may contain duplicates.

    public class Solution {
        public int findMin(int[] nums) {
            if (nums == null || nums.length == 0) {
                return 0;
            }
            int len = nums.length;
            // increasing...
            if (nums[0] < nums[len - 1]) {
                return nums[0];
            }
            
            int begin = 0, end = len - 1;
            while (begin + 1 < end) {
                int mid = begin + (end - begin) / 2;
                if (nums[mid] < nums[mid - 1] && nums[mid] < nums[mid + 1]) {
                    return nums[mid];
                }
                if (nums[mid] > nums[begin]) {
                    begin = mid;
                }else if (nums[mid] < nums[begin]) {
                    end = mid;
                } else if (nums[mid] > nums[end]) {
                    begin = mid;
                } else {
                    // nums[begin], nums[end]和nums[mid]均相等,则顺序遍历。
                    return traversal(nums);
                }
            }
            if (nums[begin] < nums[end]) {
                return nums[begin];
            }
            return nums[end];
        }
        
        private int traversal(int[] nums) {
            int min = Integer.MAX_VALUE;
            for (int n : nums) {
                min = Math.min(min, n);
            }
            return min;
        }
    }
    

    Sorted Array

    Problem 1: Remove Duplicates from Sorted Array I

    Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.

    Do not allocate extra space for another array, you must do this in place with constant memory.

    For example,
    Given input array nums = [1,1,2],

    Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the new length.

    public class Solution {
        public int removeDuplicates(int[] nums) {
            if (nums == null || nums.length == 0) {
                return 0;
            }
            int len = nums.length;
            int p1 = 0, p2 = 1;
            while (p2 < len) {
                if (nums[p1] != nums[p2]) {
                    ++p1;
                    ++p2;
                    continue;
                }
                while (p2 < len && nums[p2] == nums[p1]) {
                    ++p2; // find the first elem not equal to nums[p1]
                }
                if (p2 == len) {
                    break;
                }
                nums[p1 + 1] = nums[p2];
                ++p1;
            }
            return p1 + 1;
        }
    }
    

    Problem 2: Remove Duplicates from Sorted Array II

    Follow up for "Remove Duplicates":
    What if duplicates are allowed at most twice?

    For example,
    Given sorted array nums = [1,1,1,2,2,3],

    Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3. It doesn't matter what you leave beyond the new length.

    解决思路

    前指针的起始位置为第二个元素,后指针需要根据指向元素和前指针以及前指针的前一个元素的比较进行移动。

    public class Solution {
        public int removeDuplicates(int[] nums) {
            if (nums == null) {
                return 0;
            }
            if (nums.length < 3) {
                return nums.length;
            }
            int len = nums.length;
            int p1 = 1, p2 = 2;
            while (p2 < len) {
                if (nums[p1 - 1] == nums[p1] && nums[p1] == nums[p2]) {
                    while (p2 < len && nums[p2] == nums[p1]) {
                        ++p2;
                    }
                    if (p2 == len) {
                        break;
                    }
                }
                // swap
                int tmp = nums[p1 + 1];
                nums[p1 + 1] = nums[p2];
                nums[p2] = tmp;
                ++p1;
                ++p2;
            }
            return p1 + 1;
        }
    }
    

    Problem 3:Merge Sorted Array

    Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array.

    Note:
    You may assume that nums1 has enough space (size that is greater or equal to m + n) to hold additional elements from nums2. The number of elements initialized in nums1and nums2 are m and n respectively.

    解决思路

    双指针 + 从后往前插入。

    程序

    public class Solution {
        public void merge(int[] nums1, int m, int[] nums2, int n) {
            int i = m - 1;
            int j = n - 1;
            int k = nums1.length - 1;
            while (i >= 0 && j >= 0) {
                if (nums1[i] > nums2[j]) {
                    nums1[k--] = nums1[i--];
                } else {
                    nums1[k--] = nums2[j--];
                }
            }
            while (i >= 0) {
                nums1[k--] = nums1[i--];
            }
            while (j >= 0) {
                nums1[k--] = nums2[j--];
            }
        }
    }
    

    Problem 4: Median of Two Sorted Array

    There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

    解决思路

    递归,二分;注意边界条件;

    程序

    public class Solution {
        public double findMedianSortedArrays(int[] nums1, int[] nums2) {
            int len = nums1.length + nums2.length;
            if (len % 2 == 0) {
                // 偶数时,返回中间两个数的均值
                return (findKthElem(nums1, 0, nums2, 0, len / 2) + findKthElem(nums1, 0, nums2, 0, len / 2 + 1)) / 2.0;
            }
            return findKthElem(nums1, 0, nums2, 0, len / 2 + 1);
        }
        
        private double findKthElem(int[] a, int a_start, int[] b, int b_start, int k) {
            if (a_start >= a.length) {
                return b[b_start + k - 1];
            }
            if (b_start >= b.length) {
                return a[a_start + k - 1];
            }
            if (k == 1) {
                // 返回a和b数组中首元素较小的那个
                return Math.min(a[a_start], b[b_start]);
            }
            int ak = a_start + k / 2 - 1 < a.length ? a[a_start + k / 2 - 1] : Integer.MAX_VALUE;
            int bk = b_start + k / 2 - 1 < b.length ? b[b_start + k / 2 - 1] : Integer.MAX_VALUE;
            if (ak < bk) {
                return findKthElem(a, a_start + k / 2, b, b_start, k - k / 2);
            } else {
                return findKthElem(a, a_start, b, b_start + k / 2, k - k / 2);
            }
        }
    }
    

    总结

    1. 数组有序或者部分有序:考虑二分查找;

    2. 数组无序:转成有序,考虑二分。

  • 相关阅读:
    RepVGG:VGG,永远的神! | CVPR 2021
    GFLV2:边界框不确定性的进一步融合,提点神器 | CVPR 2021
    CAP:多重注意力机制,有趣的细粒度分类方案 | AAAI 2021
    MobileNext:打破常规,依图逆向改造inverted residual block | ECCV 2020
    NFNet:NFResNet的延伸,不用BN的4096超大batch size训练 | 21年论文
    Involution:空间不共享?可完全替代卷积的高性能算子 | CVPR 2021
    OWOD:开放世界目标检测,更贴近现实的检测场景 | CVPR 2021 Oral
    聊聊C#中的composite模式
    元宇宙 3D 开荒场 探味奇遇记
    restful是个好的范式吗?
  • 原文地址:https://www.cnblogs.com/harrygogo/p/4675315.html
Copyright © 2020-2023  润新知