[算法专题] 二分搜索&排序数组

时间:2021-01-08 13:58:51

基础知识

二分非递归写法:

int binary_search(const int a[], const int size, const int val) {
int lower = 0;
int upper = size-1;
/* invariant: if a[i]==val for any i, then lower <= i <= upper */
while (lower <= upper) {
int i = lower + (upper-lower)>>1;
if (val == a[i]) {
return i;
} else if (val < a[i]) {
upper = i-1;
} else { /* val > a[i] */
lower = i+1;
}
}
return -1;
}

二分递归写法

int binarySearch(int *array, int left, int right, int value) {
if (left > right) {
// value not found
return -1;
} int mid = left + (right - left) / 2;
if (array[mid] == value) {
return mid;
} else if (array[mid] < value) {
return binarySearch(array, mid + 1, right, value);
} else {
return binarySearch(array, left, mid - 1, value);
}
}

刷题

1 Find a Fixed Point in a given array

http://www.geeksforgeeks.org/find-a-fixed-point-in-a-given-array/

Given an array of n distinct integers sorted in ascending order, write a function that returns a Fixed Point in the array, if there is any Fixed Point present in array, else returns -1. Fixed Point in an array is an index i such that arr[i] is equal to i. Note that integers in array can be negative.

Examples:

  Input: arr[] = {-10, -5, 0, 3, 7}
Output: 3 // arr[3] == 3 Input: arr[] = {0, 2, 5, 8, 17}
Output: 0 // arr[0] == 0
  Input: arr[] = {-10, -5, 3, 4, 7, 9}
Output: -1 // No Fixed Point

[算法专题] 二分搜索&排序数组

来看代码:

int indexSearch(int *array, int left, int right) {
if (left > right) {
// value not found
return -1;
} int mid = right - (right - left) / 2;
if (array[mid] == mid) {
return mid;
} else if (array[mid] < mid) {
return indexSearch(array, mid + 1, right);
} else {
return indexSearch(array, left, mid - 1);
}
}

2 Search Insert Position

https://leetcode.com/problems/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

/**
* 假设sorted array为:a b c d e f g
* 如果以下方式进行二分排除,假设排除了d e f g,可以一定保证target不再[d,g]中,但是在不在[a,c]中其实并不能保证
*
* 因此当left <= right时,有target不在[最左边元素,left左边相邻的元素]与[right右边相邻的元素, 最右边元素]中,分析以下情况:
*
* 1.
* 当left往右越过right时,排除了right以及right左边的元素,同时target本身就不在[right右边相邻的元素即现在的left, 最右边元素]中,
* 因此有nums[right] < target < nums[left]
*
* 2.
* 当right往左越过left时,排除了left以及left右边的元素,同时target本身就不在[最左边元素,left左边相邻的元素即现在的right]中,
* 因此有nums[right] < target < nums[left]
*
* 综上:
* 当right < left时,可以得知:target不在[左边元素,right]和[left,右边元素]中,因此必然有nums[right] < target < nums[left]
*/ class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while (left <= right) {
int middle = left + ((right - left) >> 1);
if (target == nums[middle]) {
return middle;
} else if (target < nums[middle]) {
right = middle - 1;
} else {
left = middle + 1;
}
} // 注意:当right < left退出循环时,一定可以保证nums[right] < target < nums[left]
// 边界条件,当right = -1时,有target < nums[left] = nums[0]
// 边界条件,当left = nums.size()时,有nums[nums.size() - 1] = nums[right] > target
return right + 1;
}
};

3 Search in Rotated Sorted Array

https://leetcode.com/problems/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.

[算法专题] 二分搜索&排序数组

思路:

1. 经过rotate,有序数组分割成两个有序数组。

2. 二分后,[left,middle)和(middle,end]必然有一个是有序的。我们可以通过其中的一个有序数组来排除元素。

现在假设[left,middle)这个区间中元素是有序,如果key在[left,middle)这个区间中,则可将(middle,end]的元素均排除;如果key不在[left,middle)这个区间中,则可将[left,middle)排除。

3. 其实不难发现,只要middle有一边的元素是有序的,即可使用二分。

class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while (left <= right) {
int middle = left + ((right - left) >> 1);
if (target == nums[middle]) {
return middle;
} // 左半部分有序
if (nums[left] <= nums[middle]) {
if (nums[left] <= target && target < nums[middle]) {
right = middle - 1;
} else {
left = middle + 1;
}
} // 右半部分有序
if (nums[middle] < nums[right]) {
if (nums[middle] < target && target <= nums[right]) {
left = middle + 1;
} else {
right = middle - 1;
}
}
} return -1;
}
};

4 Sqrt(x)

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

Implement int sqrt(int x).

Compute and return the square root of x.

class Solution {
public:
int mySqrt(int x) { if (x == 0) {
return x;
} int left = 1;
int right = x; while (left <= right) {
int middle = left + ((right - left) >> 1); if (middle == x / middle) {
return middle;
} else if (middle < x/middle) {
left = middle + 1;
} else {
right = middle - 1;
}
} return right;
}
};

以下为浮点数版本:

double mySqrtHelper(double x, double lowBound, double highBound) {
double precision = 0.00001;
double sqrt = lowBound / 2 + highBound / 2;
if (abs(sqrt * sqrt - x) < precision) {
return sqrt;
} else if (sqrt * sqrt - x > 0) {
return mySqrtHelper(x, lowBound, sqrt);
} else {
return mySqrtHelper(x, sqrt, highBound);
}
} double mySqrt(double x) {
if (x < 0)
return ERROR; if (x == 0) {
return 0;
} if (x == 1) {
return 1;
} if (x < 1) {
return mySqrtHelper(x, x, 1);
} else {
return mySqrtHelper(x, 1, x);
}
}

5 矩阵搜索

[算法专题] 二分搜索&排序数组

bool isElementInMatrix(int **matrix, int M, int N, int target) {
int row = 0;
int column = N - 1; while (row < M && column >= 0) {
if (matrix[row][column] == target) {
return true;
} else if (matrix[row][column] < target) {
row++;
} else {
column--;
}
}
return false;
}

复杂度O(m+n)。

6 Range search

[算法专题] 二分搜索&排序数组

void searchRangeHelper(int array[], int left, int right, int target, int &begin, int &end) {
if (left > right) {
return;
} int mid = right - (right - left) / 2;
if (array[mid] == target) {
if (mid < begin || begin == -1) {
begin = mid;
}
if (mid > end) {
end = mid;
}
searchRangeHelper(array, left, mid - 1, target, begin, end);
searchRangeHelper(array, mid + 1, right, target, begin, end);
}
else if (array[mid] < target) {
searchRangeHelper(array, mid + 1, right, target, begin, end);
}
else {
searchRangeHelper(array, left, mid - 1, target, begin, end);
}
} vector<int> searchRange(int A[], int n, int target) {
int begin = -1, end = -1; searchRangeHelper(A, 0, n - 1, target, begin, end); vector<int> ans;
ans.push_back(begin);
ans.push_back(end);
return ans;
}

7 Search a 2D Matrix

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

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

  • Integers in each row are sorted from left to right.
  • The first integer of each row is greater than the last integer of the previous row.

For example,

Consider the following matrix:

[
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int left = 0;
int right = matrix.size() * matrix[0].size() - 1; while (left <= right) { int middle = left + ((right - left) >> 1);
int row = middle / matrix[0].size();
int col = middle % matrix[0].size(); if (target == matrix[row][col]) {
return true;
} else if (target < matrix[row][col]) {
right = middle - 1;
} else {
left = middle + 1;
} } return false;
}
};

8 Find Peak Element

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

A PEAK element is an element that is greater than its neighbors.

Given an input array where num[i] ≠ num[i+1], find a peak element and return its index.

The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.

You may imagine that num[-1] = num[n] = -∞.

For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2.

class Solution {
public:
int findPeakElement(vector<int>& nums) {
int len = nums.size();
if (len == 1) {
return 0;
} if (nums[0] > nums[1]) {
return 0;
} if (nums[len - 1] > nums[len - 2]) {
return len - 1;
} int left = 1;
int right = len - 2;
int mid;
// 只要没有都排除完,就继续循环
while (left <= right) {
mid = left + ((right - left) >> 1);
if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1])
{
return mid;
} else if (nums[mid] > nums[mid + 1]) { // 降序
right = mid - 1;
} else if (nums[mid] < nums[mid + 1]) { // 升序
left = mid + 1;
}
}
}
};

9 Remove Duplicates from Sorted Array

http://www.lintcode.com/en/problem/remove-duplicates-from-sorted-array/

给定一个排序数组,在原数组中删除重复出现的数字,使得每个元素只出现一次,并且返回新的数组的长度。

不要使用额外的数组空间,必须在原地没有额外空间的条件下完成。

样例

给出数组A =[1,1,2],你的函数应该返回长度2,此时A=[1,2]

class Solution {
public:
/**
* @param A: a list of integers
* @return : return an integer
*/
int removeDuplicates(vector<int> &nums) {
int len = nums.size();
if (len <= 1) {
return len;
} int fast = 1;
int slow = 0;
while (fast < len) {
if (nums[slow] != nums[fast]) {
nums[++slow] = nums[fast];
}
++fast;
}
return slow + 1;
}
};

10 Remove Duplicates from Sorted Array II

http://www.lintcode.com/zh-cn/problem/remove-duplicates-from-sorted-array-ii/

上题的一个follow up问题,

如果可以允许出现两次重复将如何处理?

样例

给出数组A =[1,1,1,2,2,3],你的函数应该返回长度5,此时A=[1,1,2,2,3]

class Solution {
public:
/**
* @param A: a list of integers
* @return : return an integer
*/
int removeDuplicates(vector<int> &nums) {
int len = nums.size();
if (len <= 1) {
return len;
} int fast = 1;
int slow = 0;
int dup;
/**
* 思路:在没有重复时,以及重复一次时,数组需要刷新
*/
while (fast < len) {
if (nums[slow] != nums[fast]) {
nums[++slow] = nums[fast];
dup = 0;
} else {
++dup;
if (dup < 2) {
nums[++slow] = nums[fast];
}
}
++fast;
} return slow + 1;
}
};

11 求两个排序数组的交集

Intersection of 2 sorted array

example

array1: [2 3 4 6]

array2: [3 6 9 10]

return [3,6]

Algorithm Intersection(arr1[], arr2[]):

For Intersection of two arrays, print the element only if the element is present in both arrays.

1) Use two index variables i and j, initial values i = 0, j = 0

2) If arr1[i] is smaller than arr2[j] then increment i.

3) If arr1[i] is greater than arr2[j] then increment j.

4) If both are same then print any of them and increment both i and j.

Time Complexity: O(m+n)

#include <iostream>
#include <vector>
using namespace std; // 求两个排序数组的交集
vector<int> findIntersection(const vector<int> &A, const vector<int> &B) {
vector<int> res; int len1 = A.size();
int len2 = B.size();
int i = 0;
int j = 0; while (i < len1 && j < len2) {
if (A[i] == B[j]) {
res.push_back(A[i]);
i++;
j++;
} else if (A[i] < B[j]) {
i++;
} else {
j++;
}
} return res;
} int main(int argc, const char *argv[]) {
vector<int> A = { 1, 2, 3, 4, 5, 9 };
vector<int> B = { 2, 4, 5, 6, 9 };
vector<int> intersection = findIntersection(A, B); for (const auto &elem : intersection) {
cout << elem << " ";
}
cout << endl; system("pause");
}

12 求两个排序数组的并集

http://www.geeksforgeeks.org/union-and-intersection-of-two-sorted-arrays-2/

For example, if the input arrays are:

arr1[] = {1, 3, 4, 5, 7}

arr2[] = {2, 3, 5, 6}

Then your program should print Union as {1, 2, 3, 4, 5, 6, 7}

Algorithm Union(arr1[], arr2[]):

For union of two arrays, follow the following merge procedure.

1) Use two index variables i and j, initial values i = 0, j = 0

2) If arr1[i] is smaller than arr2[j] then print arr1[i] and increment i.

3) If arr1[i] is greater than arr2[j] then print arr2[j] and increment j.

4) If both are same then print any of them and increment both i and j.

5) Print remaining elements of the larger array.

Time Complexity: O(m+n)

#include <iostream>
#include <vector>
using namespace std; // 求两个排序数组的并集
vector<int> findUnion(const vector<int> &A, const vector<int> &B) {
vector<int> res; int len1 = A.size();
int len2 = B.size();
int i = 0;
int j = 0; while (i < len1 && j < len2) {
if (A[i] < B[j]) {
res.push_back(A[i]);
i++;
} else if (B[j] < A[i]) {
res.push_back(B[j]);
j++;
} else if (A[i] == B[j]) {
res.push_back(A[i]);
i++;
j++;
}
} while (i < len1) {
res.push_back(A[i]);
i++;
} while (j < len2) {
res.push_back(B[j]);
j++;
} return res;
} int main(int argc, const char *argv[]) {
vector<int> A = { 1, 2, 3, 4, 5, 9 };
vector<int> B = { 2, 4, 5, 6, 9 };
vector<int> intersection = findUnion(A, B); for (const auto &elem : intersection) {
cout << elem << " ";
}
cout << endl; system("pause");
}

13 Merge Sorted Array

https://leetcode.com/problems/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.

/**
* 注意:两个指针分别指向nums1和nums2的末尾,是为了防止破坏nums1原先的元素
*/
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m - 1;
int j = n - 1;
int pos = m + n;
while (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) {
nums1[--pos] = nums1[i--];
} else if (nums2[j] > nums1[i]) {
nums1[--pos] = nums2[j--];
} else {
nums1[--pos] = nums1[i--];
nums1[--pos] = nums2[j--];
}
} /*
这句不写也是OK的,因为其实该数组的元素已经在正确的位置上了,不用重复拷贝
while (i >= 0) {
nums1[--pos] = nums1[i--];
}
*/ while (j >= 0) {
nums1[--pos] = nums2[j--];
}
}
};

14 Merge k Sorted Lists

https://leetcode.com/problems/merge-sorted-array/

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

最容易想到的办法是,比较每个链表的当前元素,找到当前最小的(假设有k个链表,时间复杂度O(k)),然后将该链表往后走一位。假设每个链表的size都是n,总共有k个链表,那么总共有kn个元素,最坏情况下要比较kn次,因此时间复杂度为kn * O(k) = O(nk2)。

有没有什么好的办法来加速这个过程呢,显然找到当前最小这一步骤可以使用最小堆从而达到O(logk)的复杂度,从而最终的时间复杂度为O(nklogk)。

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/ struct cmp {
bool operator() (const ListNode *lhs, const ListNode *rhs) {
if (lhs->val < rhs->val) {
return false;
} else {
return true;
}
}
}; class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode *, vector<ListNode *>, cmp> heap; for (int i = 0; i < lists.size(); i++) {
if (lists[i]) {
heap.push(lists[i]);
}
} ListNode *dummyNode = new ListNode(0);
ListNode *cur = dummyNode;
while (!heap.empty()) {
cur->next = heap.top();
heap.pop(); cur = cur->next;
if (cur->next) {
heap.push(cur->next);
}
} return dummyNode->next;
}
};

999

999

999