算法与数据结构总结1 二分查找与旋转排序数组

一. 二分搜索(Binary Search)模板及其理解

1.通用模板,解决start, end, mid, <(<=), >(>=)等问题

http://www.lintcode.com/en/problem/binary-search/

class Solution {
public:
    /**
     * @param nums: The integer array.
     * @param target: Target number to find.
     * @return: The first position of target. Position starts from 0.
     */
    int binarySearch(vector<int> &array, int target) {
        // write your code here
        if(array.size() == 0){
            return -1;
        }
        int start = 0, end = array.size() - 1;
        while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(array[mid] == target){
                end =  mid;
            }else if(array[mid] < target){
                start = mid;
            }
            else{
                end = mid;
            }
        }
        if(array[start] == target){
            return start;
        }
        if(array[end] == target){
            return end;
        }
        return -1;
    }
};

注意事项:

1). start + 1 < end (最后会剩下start, end两项);

2). mid = start + (end - start) / 2; (避免出现start + end越界情况);

3). A[mid] 依次比较 ==, <, >;

4). A[start], A[end]与target比较。 (对应first / last position问题,对应的比较顺序不用)

2. 对二分搜索的理解

1) 字面意义的二分搜索,对有序数组取中间值,比较并折半删除

2)first / last position问题,利用模板可以很好地解决

3)只有能判断解在某部分集合里,就可以将规模缩小(不一定是简单的在中间位置左侧或右侧),若每次规模近似缩小一半,仍然是二分的思路;

即将一个O(n)问题通过O(1)操作转化成一个O(k/2)的问题。

4)当复杂度要求O(logn)时,往往可以考虑二分搜索

二 常见问题

2.1 基础二分问题变种

1)Search a 2D matrix i

https://leetcode.com/problems/search-a-2d-matrix/

思路:将二维矩阵看做线性序列,则为典型二分搜索问题,所以只需要线性序列与二维数组小标的对应

即: mid / n , mid % n

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size(), n = matrix[0].size();
        int start = 0, end = m*n - 1;
        while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(matrix[mid/n][mid%n] == target){
                return 1;
            }
            else if(matrix[mid/n][mid%n] < target){
                start = mid;
            }
            else{
                end = mid;
            }
        }
        if (matrix[start/n][start%n] == target) {
            return 1;
        }
        if (matrix[end/n][end%n] == target) {
            return 1;
        }
        return 0;
    }
};

Search a 2D matrix i

2)Search a 2D matrix ii

https://leetcode.com/problems/search-a-2d-matrix-ii/

思路:根据矩阵的性质,从左下角向右上寻找。

如果target大于当前元素,当前列上方元素可删除,所以向右走一步;

如果target小于当前元素,当前行右方元素可删除,所以向上走一步。

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size();
        int n = matrix[0].size();
        int i = m - 1, j = 0;
        while(i >= 0 && j < n){
            if(matrix[i][j] == target){
                return 1;
            }
            else if(matrix[i][j] < target){
                j++;
            }
            else{
                i--;
            }
        }
        return 0;
    }
};

Search a 2D matrix ii

2.2 first/last position 问题

1)Search for a range

https://leetcode.com/problems/search-for-a-range/

思路:使用两遍模板,分别找到first 和 last position,则找到对应区间

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size() == 0){
            return vector<int>{0,0};
        }
        int start = 0, end = nums.size() - 1;
        vector<int> range;
        // search for left bound
        while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(nums[mid] == target){
                end = mid;
            }
            else if(nums[mid] < target){
                start = mid;
            }
            else{
                end = mid;
            }
        }
        if(nums[start] == target){
            range.push_back(start);
        }
        else if(nums[end] == target){
            range.push_back(end);
        }
        else{
            range.push_back(-1);
        }
        start = 0;
        end = nums.size() - 1;
        // search for right bound
         while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(nums[mid] == target){
                start = mid;
            }
            else if(nums[mid] < target){
                start = mid;
            }
            else{
                end = mid;
            }
        }
        if(nums[end] == target){
            range.push_back(end);
        }
        else if(nums[start] == target){
            range.push_back(start);
        }
        else{
            range.push_back(-1);
        }
        return range;
    }
};

Search for a range

2) Search insert position

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

思路:在数组中找到第一个大于等于target的位置(find first position)

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        if(nums.size() == 0){
            return 0;
        }
        int start = 0,end = nums.size() - 1;
        while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(nums[mid] == target){
                end = mid;
            }
            else if(nums[mid] < target){
                start = mid;
            }
            else{
                end = mid;
            }
        }
        if(nums[start] >= target){
            return start;
        }
        else if(nums[end] >= target){
            return end;
        }
        else{
            return end + 1;
        }
    }
};

Search insert position

3) Find bad version

https://leetcode.com/problems/first-bad-version/

思路:找到第一个bad version(first position)

// Forward declaration of isBadVersion API.
bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        if(n == 0){
            return 0;
        }
        int start = 1, end = n;
        while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(isBadVersion(mid)){
                end = mid;
            }
            else {
                start = mid;
            }
        }
        if(isBadVersion(start)){
            return start;
        }
        if(isBadVersion(end)){
            return end;
        }
    }
};

find bad version

4) Sqrt(x)

https://leetcode.com/problems/sqrtx/

思路:找到最后一个平方小于等于x的数

class Solution {
public:
    int mySqrt(int x) {
        long long start = 0, end = x;
        while(start + 1 < end){
            long long mid = start + (end - start) / 2;
            if(mid*mid == x){
                return mid;
            }
            else if(mid * mid < x){
                start = mid;
            }
            else{
                end = mid;
            }
        }
        if(end * end <= x){
            return end;
        }
        return start;
    }
};

Sqrt(x)

3 较复杂二分搜索问题(包含旋转排序数组内搜索)

这几个问题仍然采用二分思想,但二分后删除部分集合的调节并不明显,往往需要通过画图帮助理解。

同时此类问题很多也可以转换成增加条件的first position / last position问题,从而使用二分模板。

1) Find peak element

https://leetcode.com/problems/find-peak-element/

思路:因为题目假设了num[-1] = num[n] = - 所以对于nums[mid]来讲,其与左右元素之间无非只有三种情况:

(1) nums[mid-1] < nums[mid] && nums[mid] > nums[mid+1] ,此时mid即为peak element;

(2) nums[mid-1] < nums[mid] && nums[mid] < nums[mid+1],此时删除数组左半部分仍然可保证有peak element, mid = start;

(3)nums[mid-1] > nums[mid] && nums[mid] > nums[mid+1], 此时删除数组左半部分仍然可保证有peak element,mid = end;

(4)在波谷位置,随意左右均可。

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int start = 0, end = nums.size() - 1;
        while(start + 1 < end ){
            int mid = start + (end - start) / 2;
            if(nums[mid - 1] < nums[mid] && nums[mid] > nums[mid+1]){
                return mid;
            }
            else if(nums[mid - 1] < nums[mid] && nums[mid] < nums[mid + 1]){
                start = mid;
            }
            else{
                end = mid;
            }
        }
        if(nums[start] > nums[end]){
            return start;
        }
        return end;
    }
};

find peak element

2) Find Minimum in Rotated Sorted Array

https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/

思路: 根据旋转排序数组性质,问题转化为find first element which is bigger than nums[nums.size() - 1];

class Solution {
public:
    int findMin(vector<int>& nums) {
        int start = 0, end = nums.size() - 1;
        int target = nums[nums.size() - 1];
        while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(nums[mid] > target){
                start = mid;
            }
            else{
                end = mid;
            }
        }
        if(nums[start] < target){
            return nums[start];
        }
        return nums[end];
    }
};

Find Minimum in rotated sorted array

3) Search in Rotated Sorted Array

https://leetcode.com/problems/search-in-rotated-sorted-array/

思路:分情况进行“二分”的删除区间操作

首先区分target与nums[nums.size() - 1]的关系

其次在不同区间内,根据target与nums[mid]的关系进行相关删除区间操作。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.size() == 0){
            return -1;
        }
        int start = 0, end = nums.size() - 1;
        int temp = nums[end];
        while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(nums[mid] == target){
                return mid;
            }
            if(temp == target){
                return nums.size() - 1;
            }
            if(temp < target){
                if(nums[mid] < target && nums[mid] > temp){
                    start = mid;
                }
                else{
                    end = mid;
                }
            }
            else{
                if(nums[mid] > target && nums[mid] < temp){
                    end = mid;
                }
                else{
                    start = mid;
                }
            }
        }
        if(nums[start] == target){
            return start;
        }
        if(nums[end] == target){
            return end;
        }
        return -1;
    }
};

Search in rotated sorted array

4) Median of Two Sorted Arrays

https://leetcode.com/problems/median-of-two-sorted-arrays/

思路:取两个数组中长度较小的那个,利用归并排序的思想,我们从a和b中一共拿k个数看下假设 length(a) < length(b)

从a中取pa = min(k / 2, length(a))个元素,从b中取pb = k – pa个元素;

如果a[pa - 1] < b [pb - 1], 说明a取少了,第k个在a后半部分和b前半部分,反之同理。

 

时间: 2024-08-09 06:33:57

算法与数据结构总结1 二分查找与旋转排序数组的相关文章

专题总结—二分查找与旋转排序数组

1.二分搜索的模板. 算法面试中,如果需要优化O(n)的时间复杂度,那么只能是O(logn)的二分法. 注意二分法大多数情况都是适用于排序数组. http://www.lintcode.com/zh-cn/problem/first-position-of-target/ class Solution { public: /** * @param nums: The integer array. * @param target: Target number to find. * @return:

(算法:二分查找)在排序数组中,找出给定数字出现的次数

题目: 在排序数组中,找出给定数字出现的次数 思路: 既然出现排序数组,很容易想到二分查找,时间复杂度为O(logn): 先通过二分查找找到最左边出现该数字的下标left(如果没找到,则返回-1),然后通过二分查找找到最右边出现该数字的下表right(如果没找到,则返回-1),然后right-left+1就是出现的次数: 代码: #include <iostream> using namespace std; int BinarySearchCount(int *array,int len,i

二分查找--34. 在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置. 你的算法时间复杂度必须是 O(log n) 级别. 如果数组中不存在目标值,返回 [-1, -1]. 示例 1: 输入: nums = [5,7,7,8,8,10], target = 8输出: [3,4] 代码实现 class Solution { public: int left_index(vector<int>& nums,int target) { int be

Python 迭代器&amp;生成器,装饰器,递归,算法基础:二分查找、二维数组转换,正则表达式,作业:计算器开发

本节大纲 迭代器&生成器 装饰器  基本装饰器 多参数装饰器 递归 算法基础:二分查找.二维数组转换 正则表达式 常用模块学习 作业:计算器开发 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式,运算后得出结果,结果必须与真实的计算器所得出的结果一致 迭代器&

leetcode题解: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 du

算法学习一~分治法~二分查找,快速的找~

现在编程也算是走上门了,但是没有把算法掌握下来只能说自己还是门外汉,所以以后我们就开始努力的学习算法,现在把自己每天的学习分享在这里希望大家能喜欢,并且我也要在这里整理我一天的学习和思路,. 二分查找..大家经常需要在一个数组中寻找某个值.如果是一个已经拍好序的话那么可以很快的找到.我们考虑下暴力查找,就是a[n],中找一个m,那么最好时是o(1),最差时是0(n),最终平均情况就是长度n/2.这样的时间复杂度不算很高但是可以改进到logn的级别. 1 #include<stdio.h>//算

JDK自带的二分查找算法和自己写的普通二分查找算法的比较(java二分查找源代码)

一.描述 解析和比较JDK自带的二分查找算法和自己写的普通二分查找算法,使用二进制位无符号右移来代替除2运算,并使用产生随机数的方法产生一定范围的随机数数组,调用Arrays类的sort()静态方法,对int类型数组进行排序. Math.random()的用法:会产生一个[0,1)之间的随机数(注意能取到0,不能取到1),这个随机数的是double类型,要想返回指定范围的随机数如[m,n]之间的整数的公式:(int)(Math.random()*(m-n+1)+m) 二.源代码 <span st

python常用的简单算法,二分查找、冒泡排序、数组翻转等

1.二分查找:主要用于查询元素数量较多的列表,采用二分查找,取中位值,进行大小比较,可以提高效率 1 #二分查找,用于在较大的数据列表中查询某个值,考虑到元素比较多,单纯的遍历会造成内存压力过大,考虑使用二分查找 2 #二分查找的关键在于查询中间值,将需要查找的值与中间值进行比较,然后确定查找方向 3 def binary_search(data_source,find_n): 4 #取中位数 5 mid=int(len(data_source)/2) 6 7 if len(data_sourc

算法回顾(三) 二分查找

二分查找,顾名思义,它的原理是,将排序好的数列分成两部分,判断期待值在高位部分还是在低位部分,然后再将期待值所在的那个区间的数列重新按照这个规则划分成两部分,再比较,直到最后不能划分为止. 优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且插入删除困难.因此,折半查找方法适用于不经常变动而查找频繁的有序列表. 假设其数组长度为n,那么该算法复杂度为o(log(n)).代码如下: 1 int binarySearch( int *a , int begin, int end,