再发一组关于数组的题目,其中部分来自一些公司的面试题(没有合适的oj来测试),部分来自leetcode。
暂时不给出解题思路和代码,待节后有时间再整理补充。(已全部更新)
1.
有序数组的二分查找
int binSearch(int array[], int low, int high, int target)
{ // 递归
if (low > high) return -1;
int mid = low + ((high - low) >> 1);
if (array[mid] > target)
return binSearch(array, low, mid -1, target);
if (array[mid] < target)
return binSearch(array, mid+1, high, target);
return mid;
}
int binSearch(int array[], int low, int high, int target)
{ // 非递归
while(low <= high)
{
int mid = low + low + ((high - low) >> 1);
if (array[mid] > target)
high = mid - 1;
else if (array[mid] < target)
low = mid + 1;
else return mid;
}
return -1;
}
扩展:1). 关于数组的面试题1总结中的循环数组的查找;找到循环数组中的最小值;
2). 求整数的平方根sqrt(x),仅返回整数部分,可以采用二分查找的方法去试根。
2.
数组的排序(插入排序、冒泡排序、堆排序、快速排序和归并排序)
// 插入排序 ,虽然查找位置的时候可以用二分查找,但是同样需要移动后半部分的元素,复杂度并不减少
void InsertSort(vector<int> &array)
{
int i, j;
for(i = 1; i < array.size(); ++i)
{
int tmp = array[i];
for(j = i-1; j >= 0 && array[j] > tmp; --j)
{
array[j+1] = array[j];
}
array[j+1] = tmp;
}
}
// 冒泡排序
void BubbleSort(vector<int> &array)
{
int n = array.size();
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n-i-1; ++j)
{
if(array[j] > array[j+1])
swap(array[j], array[j+1]);
}
}
}
// 改进的冒泡排序
void BubbleSort2(vector<int> &array)
{
int n = array.size();
for(int i = 0; i < n; ++i)
{
bool hasChange = false;
for(int j = 0; j < n-i-1; ++j)
{
if(array[j] > array[j+1])
{
swap(array[j], array[j+1]);
hasChange = true;
}
}
// 若本趟扫描没有任何替换,则数组已经有序
if(!hasChange) break;
}
}
堆排序,需要注意两点:1). 很多教科书中计算左右子孩子一般是以数组下标为1开始来计数的,当我们从0开始计数时有差异;
2). 在调整堆的函数中一定要添加数组的长度n,而不是直接取vector的size!
// 调整大顶堆
void maxHeapify(vector<int> &array, int idx, int n)
{ // 注意调整函数中需要带上数组的大小,因为排序的时候这个n一直在减小
int maxIdx = idx;
if(2*idx+1 < n && array[2*idx+1] > array[maxIdx])
{
maxIdx = 2*idx + 1;
}
if(2*idx+2 < n && array[2*idx+2] > array[maxIdx])
{
maxIdx = 2*idx + 2;
}
if(maxIdx != idx)
{
swap(array[maxIdx], array[idx]);
maxHeapify(array, maxIdx, n);
}
}
// 堆排序
void HeapSort(vector<int> &array)
{
int n = array.size();
for(int i = (n-1)/2; i >= 0; --i)
{
maxHeapify(array, i, n);
}
for(int i = n-1; i > 0; --i)
{
swap(array[0], array[i]);
maxHeapify(array, 0, i);
}
}
快速排序有两种不同的划分方法,第一种方法是算法导论正文中讲述的,它保证i左边(包括i)所有元素都是小于或者等于pivot,然后采用j从左到右依次判断,若遇到比pivot小的元素就与第i+1位(程序中先++i了,即第一个大于pivot的元素)互换。
另一种方法则是从左右两边同时扫描,保证l左边部分都是较小的元素,r右边部分都是较大的元素,但是尤其需要注意的是while语句中要检查边界,while结束后赋值。若想在左右两边的while循环都结束后再swap两者的值,会很容易出错,最好不要这样写。
// 快速排序
int Partition(vector<int> &array, int l, int r)
{
int pivot = array[r];
int i = l - 1; // i及左边的元素都较小
for(int j = l; j < r; ++j)
{
if(array[j] <= pivot)
{
++i;
swap(array[i], array[j]);
}
}
swap(array[i+1], array[r]);
return i+1;
}
int Partition2(vector<int> &array, int l, int r)
{
int pivot = array[l];
while(l < r)
{ // 从两端到中间
while(l < r && array[r] >= pivot)
--r;
if(l < r) array[l++] = array[r];
while(l < r && array[l] <= pivot)
++l;
if(l < r) array[r--] = array[l];
}
array[l] = pivot;
return l;
}
void QuickSortHelper(vector<int> &array, int l, int r)
{
if(l >= r) return;
int mid = Partition(array, l, r);
//int mid = Partition2(array, l, r);
QuickSortHelper(array, l, mid-1);
QuickSortHelper(array, mid+1, r);
}
void QuickSort(vector<int> &array)
{
int n = array.size();
QuickSortHelper(array, 0, n-1);
}
用递归写,需要额外的空间。
// 归并排序
void Merge(vector<int> &array, int low, int mid, int high)
{
vector<int> tmp;
int i = low;
int j = mid + 1;
while(i <= mid && j <= high)
{
if(array[i] <= array[j])
tmp.push_back(array[i++]);
else tmp.push_back(array[j++]);
}
while(i <= mid)
{
tmp.push_back(array[i++]);
}
while(j <= high)
{
tmp.push_back(array[j++]);
}
for(int i = 0; i < tmp.size(); ++i)
{
array[low + i] = tmp[i];
}
}
void MergeSortHelper(vector<int> &array, int l, int r)
{
if(l >= r) return;
int mid = l + ((r-l) >> 1);
MergeSortHelper(array, l, mid);
MergeSortHelper(array, mid+1, r);
Merge(array, l, mid, r);
}
void MergeSort(vector<int> &array)
{
int n = array.size();
MergeSortHelper(array, 0, n-1);
}
3.
循环数组中的最大字段和(《编程之美》)
long long maxSumSubArray(const vector<int> &array)
{
long long sum = 0;
long long tmp = 0, tmp1 = 0;
long long max = 0;
long long min = INT_MAX;
for(int i = 0; i < array.size(); ++i)
{
sum += array[i];
if(tmp > 0) tmp += array[i];
else tmp = array[i];
if(tmp > max) max = tmp;
if(tmp1 < 0) tmp1 += array[i];
else tmp1 = array[i];
if(tmp1 < min) min = tmp1;
}
max = max > sum - min ? max : sum - min;
return max;
}
4.
求整数数组中最长的连续序列(Longest Consecutive Sequence)
例如,给定数组
[100, 4, 200, 1, 3, 2]
,
最长的连续序列是[1,2,3,4],应该输出其长度4
int longestConsecutive(vector<int> &num)
{
if(num.size() == 0) return 0;
int maxlen = 1;
map<int, int> record;
for(size_t i = 0; i < num.size(); ++i)
{
int val = num[i];
if(record[val] != 0) continue;
record[val] = 1;
int l = record[val - 1];
int r = record[val + 1];
//cout<<val<<" "<<l<<" "<<r<<endl;
record[val - l] = 1 + r + l;
record[val + r] = 1 + r + l;
if(1 + r + l > maxlen)
maxlen = 1 + r + l;
}
return maxlen;
}
5.
求随机整数数组中最长等差数列长度
int longestSubArray(vector<int> &array)
{
int n = array.size();
vector<vector<int> > dp;
vector<int> tmp(n, 2);
dp.assign(n, tmp);
sort(array.begin(), array.end());
int maxLen = 2;
for(int i = 1; i < n-1; ++i)
{
int j = i-1, k = i+1;
while(j >= 0 && k < n)
{
if(array[j] + array[k] > 2 * array[i])
--j;
else if(array[j] + array[k] < 2 * array[i])
++k;
else {
dp[i][k] = 1+dp[j][i];
if(dp[i][k] > maxLen) maxLen = dp[i][k];
--j;
++k;
}
}
}
return maxLen;
}
6.
找出大于0的丢失的最小数字
给定一个无序的整数数组,怎么找到第一个大于0,并且不在此数组的整数。比如[1,2,0] 返回 3, [3,4,-1,1] 返回 2。最好能O(1)空间和O(n)时间。
int findLostPosNum(vector<int> &array)
{
int n = array.size();
for(int i = 0; i < n; )
{
if(array[i] == i+1 || array[i] > n || array[i] <= 0)
{
++i;
} else swap(array[array[i]-1], array[i]);
}
for(int i = 0; i < array.size(); ++i)
{
//cout << array[i] << endl;
if(array[i] != i+1) return i+1;
}
return array.back()+1;
}
7.
最长递增子数组
// 方法一: dp
int LIS1(const vector<int> &array)
{
// 用dp[i]记录以第i个元素作为最后一个元素的递增子数组的长度
int n = array.size();
vector<int> dp(n, 0);
int lis = 0;
for(int i = 0; i < n; ++i)
{
dp[i] = 1;
for(int j = 0; j < i; ++j)
{
if(array[i] > array[j] && dp[i] < dp[j] + 1)
{
dp[i] = dp[j] + 1;
if(dp[i] > lis)
{
lis = dp[i];
}
}
}
}
return lis;
}
// 方法二: dp + binary search
int binSearch(const vector<int> num, int target)
{
int l = 0, r = (int)num.size() - 1;
while(l <= r)
{
int mid = l + ((r - l) >> 1);
if(num[mid] == target) return mid;
else if(num[mid] < target) l = mid+1;
else r = mid - 1;
}
return l;
}
int LIS2(const vector<int> &array)
{
// 用dp[i]记录长度为i+1的递增子数组的最小的末尾元素值
if(0 == array.size()) return 0;
vector<int> dp;
dp.push_back(array[0]);
for(int i = 1; i < array.size(); ++i)
{
int pos = binSearch(dp, array[i]);
if(pos == dp.size()) dp.push_back(array[i]);
else if(dp[pos] > array[i]) dp[pos] = array[i];
}
return dp.size();
}
扩展: 叠罗汉。给出几组数据,如(20,30),(24,25),(30,40),(32,41),(x,y)中,x 代表身高,y代表体重。叠罗汉,下面的人要比上面的人身高低、体重小。问最多叠几层?
8.
两个有序数组A,B(长度不等),从A,B中分别任意选取a,b,求min{|a-b|}
int minDistofAB(const vector<int> &A, const vector<int> &B)
{ // 采用归并的思想,但是不需要真正的归并,时间复杂度O(m+n)
if(0 == A.size() || 0 == B.size()) return -1;
int idxa = 0, m = A.size();
int idxb = 0, n = B.size();
int minDist = INT_MAX;
while(idxa < m && idxb < n)
{
int tmp = abs(A[idxa] - B[idxb]);
if(tmp < minDist) minDist = tmp;
if(0 == minDist) return minDist;
if(A[idxa] <= B[idxb]) ++idxa;
else ++idxb;
}
return minDist;
}
扩展:有n个有序数组,每个数组中取一个数字,使得得到的数列的最大值与最小值的差异最小。
对于n个有序数组,同样可以采用归并的思想,但是需要利用最小堆保存从每个数组中选取的数字,可以快速计算这些值的最小值,而最大值用一个临时变量保存即可。
关于最小堆的调整,每次移走最小堆的堆顶元素x,而补充该位置的元素则来自与x相同的那个数组,具体做法可以参见败者树。
9.
分糖果问题,n个孩子站成一排,每个人有一个分数值,要求每个孩子至少有一个糖果,且比相邻孩子分数高的孩子得到的糖果也应该比邻居多。
int candy(vector<int> &ratings)
{ // 从左向右扫描一遍,再从右向左扫描一遍
int n = ratings.size();
if(0 == n) return 0;
vector<int> candy(n, 1);
for(int i = 1; i < n; ++i)
{
if(ratings[i] > ratings[i-1])
candy[i] = candy[i-1] + 1;
}
int ret = candy[n-1];
for(int i = n-2; i >= 0; --i)
{
if(ratings[i] > ratings[i+1])
{
candy[i] = max(candy[i], candy[i+1] + 1);
}
ret += candy[i];
}
return ret;
}
10. 最大蓄水(Container With Most Water),数组a[0..n-1],其中a[i]表示坐标(i, a[i])有一条垂直于x轴的直线,两条直线和x轴一起可以组成一个蓄水池,根据数组a求出最大蓄水量(宽*高)。
int maxArea(vector<int> &height)
{
size_t len = height.size();
if(len <= 1) return 0;
size_t low = 0, high = len - 1;
int maxa = 0;
while (low < high)
{
int h = 0, wide = high - low;
if(height[low] < height[high])
{
h = height[low++];
} else {
h = height[high--];
}
int tempa = h * wide;
if(tempa > maxa) maxa = tempa;
}
return maxa;
}