1、写一个函数找出一个整数数组中第二大的数。
// 时间复杂度O(n)
const int MINNUMBER =-32767;
int find_sec_max(int data[], int count)
{数组数组
int maxnumber = data[0];
int sec_max = MINNUMBER;
for(int i = 1; i < count; i++)
{
if(data[i] > maxnumber)
{
sec_max = maxnumber;
maxnumber = data[i];
}
else
{
if(data[i] > sec_max)
sec_max = data[i];
}
}
return sec_max;
}
2、寻找平衡点问题
平衡点:比如int numbers[]={1,3,5,7,8,25,4,20}; 25前面的总和为24,25后面的总和也是24,25这个点就是平衡点。假如一个数组中的元素,其前面的部分等于后面的部分,那么这个点的位序就是平衡点,要求返回任何一个平衡点。
int calcBalance(int arr[], int length)
{
int *left = new int[length]; //left[i]为从第0个到第i-1个的和
int *right = new int[length]; //right[i]为从第i+1个到第len-1个的和
int b = length-1;
for(int i=0; i<length; i++)
{
if(i == 0)
left[i] = 0;
else
left[i] = left[i-1]+arr[i-1];
}
for(; b>=0; b--)
{
if(b == length-1)
right[b] = 0;
else
right[b] = right[b+1]+arr[b+1];
if(left[b] == right[b])
{
delete[] left;
delete[] right;
return b;
}
}
delete[] left;
delete[] right;
return -1;
}
3、输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。 比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。
bool IsPossiblePopOrder(const int* pPush, const int* pPop, int nLen)
{
stack<int> stackData;
int i = 0;
int j = 0;
while(i < nLen || j < nLen)
{
if(i < nLen)
{
stackData.push(pPush[i]);
++ i;
}
while(!stackData.empty() &&stackData.top() == pPop[j])
{
stackData.pop();
++ j;
}
if(j == nLen) break;
}
return (stackData.empty() && i == nLen);
}
4、一个int 数组,里面数据无任何限制,要求求出所有这样的数a[i],其左边的数都小于等于它,右边的数都大于等于它。
方法一:将数组a[]排序为数组b[],比较两个数组,如果a[i]==b[i],则a[i]即为一个这样的数。时间复杂度为O(NlogN)。
方法二:维护两个数组min[]和max[],max[i]为a[i]左边的数的最大值,min[i]为a[i]右边的最小值,扫描两遍数组就可得到,再比较,如果max[i]<=a[i]<=min[i],则a[i]是一个这样的数。
void FindElements(int *pArray, int len)
{
if(pArray == NULL || len <= 0 )
return ;
int *pMin = new int[len];
int *pMax = new int[len];
int i;
pMax[0] = pArray[0];
for(i = 1; i < len; i++) //计算自i往前最大值的辅助数组
pMax[i] = (pMax[i-1] >= pArray[i])?pMax[i-1]:pArray[i];
pMin[len-1] = pArray[len-1];
for(i = len - 2; i >= 0; i--) //计算自i开始最小值的辅助数组
pMin[i] = (pMin[i+1] <= pArray[i])?pMin[i+1]:pArray[i];
if(pArray[0] <= pMin[0]) //检查第1个元素是否满足条件
cout<<pArray[0]<<' ';
for(i = 1; i < len - 1; i++)
{
if(pArray[i] >= pMax[i] && pArray[i]<= pMin[i]) //满足这个关系式的元素符合要求
cout<<pArray[i]<<' ';
}
if(pArray[len-1] >= pMax[len-1]) //检查第len个元素是否满足条件
cout<<pArray[i];
cout<<endl;
delete [] pMin;
delete [] pMax;
pMin = pMax = NULL;
}
5、N个元素的数组循环右移K位,要求时间复杂度为O(N)
//普通实现
void RightShift(int* arr, int N, int K)
{
K %= N;
while(K--)
{
int t = arr[N-1];
for(int i = N-1; i > 0; i --)
arr[i] = arr[i-1];
arr[0] = t;
}
}
//递归实现
void MoveCirce(int *data, int n, int k)
{
int temp = data[n-1];
for(int i = n-1; i > 0; --i)
data[i] = data[i-1];
data[0] = temp;
-- k;
if(k > 0) MoveCirce(data,n,k);
}
假设原数组序列为abcd1234,要求变换成的数组序列为1234abcd,即循环右移了4位。比较之后,不难看出,其中有两段的顺序是不变的:1234和abcd,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。变换的过程通过以下步骤完成:
1. 逆序排列abcd:abcd1234 → dcba1234; 2. 逆序排列1234:dcba1234 → dcba4321; 3. 全部逆序:dcba4321 → 1234abcd。
//翻转函数
void Reverse(int* arr, int b, int e)
{
for(; b < e; b++, e--)
{
int temp = arr[e];
arr[e] = arr[b];
arr[b] = temp;
}
}
//循环右移
void RightShift(int* arr, int N, int K)
{
K %= N;
Reverse(arr, 0, N-K-1);
Reverse(arr, N-K, N-1);
Reverse(arr, 0, N-1);
}
//循环左移
void LeftShift(int* arr, int N, int K)
{
K %= N;
Reverse(arr, 0, K-1);
Reverse(arr, K, N-1);
Reverse(arr, 0, N-1);
}
6、发帖水王:“水王”发帖数目超过了帖子总数的一半
如果一个ID出现的次数超过总数N的一半。那么,无论水王的ID是什么,这个有序的ID列表中的第N/2项(从0开始编号)一定会是这个ID(读者可以试着证明一下)。省去重新扫描一遍列表,可以节省一点算法耗费的时间。如果能够迅速定位到列表的某一项(比如使用数组来存储列表),除去排序的时间复杂度,后处理需要的时间为O(1)。
如果每次删除两个不同的ID(不管是否包含“水王”的ID),那么,在剩下的ID列表中,“水王”ID出现的次数仍然超过总数的一半。看到这一点之后,就可以通过不断重复这个过程,把ID列表中的ID总数降低(转化为更小的问题),从而得到问题的答案。新的思路,避免了排序这个耗时的步骤,总的时间复杂度只有O(N),且只需要常数的额外内存。伪代码如下:
int Find(int* ID, int N)
{
int candidate, nTimes, i;
for(i = nTimes = 0; i < N; i++)
{
if(nTimes == 0)
{
candidate = ID[i], nTimes = 1;
}
else
{
if(candidate == ID[i])
nTimes++;
else
nTimes--;
}
}
return candidate;
}
扩展问题:统计结果表明,有3个发帖很多的ID,他们的发帖数目都超过了帖子总数目N的1/4。你能从发帖ID列表中快速找出他们的ID吗?
void Find(int* ID, int N, int & candidate1, int & candidate2, int & candidate3)
{
int nTimes1 = 0;
int nTimes2 = 0;
int nTimes3 = 0;
for(int i = 0; i < N; i++)
{
if (nTimes1 == 0)
{
candidate1 = ID[i], nTimes1 = 1;
}
else
{
if (candidate1 == ID[i])
{
nTimes1++;
}
else if (nTimes2 == 0)
{
candidate2 = ID[i], nTimes2 = 1;
}
else
{
if (candidate2 == ID[i])
{
nTimes2++;
}
else
{
if (nTimes3 == 0)
{
candidate3 = ID[i],nTimes3 = 1;
}
else if (candidate3 == ID[i])
{
nTimes3++;
}
else
{
nTimes1--;
nTimes2--;
nTimes3--;
}
}
}
}
}
}
7、寻找最大的K个数
寻找N个数中最大的K个数,本质上就是寻找最大的K个数中最小的那个,也就是第K大的数。
线性时间选择算法:RandomizedSelect()算法
//随机划分函数
int random_partition(int a[],int l,int r)
{
int i = l+rand()%(r-l+1);//生产随机数
int temp = a[i];
a[i] = a[l];
a[l] = temp;
return partition(a,l,r);//调用普通划分函数
}
//线性寻找第k大的数
int random_select(int a[],int l,int r,int k)
{
int i,j;
if(l == r) //递归结束
{
return a[l];
}
i =random_partition(a,l,r);//划分
j = i-l+1;
if(k == j) //递归结束,找到第K大的数
return a[i];
if(k < j)
{
return random_select(a,l,i-1,k);//递归调用,在前面部分查找第K大的数
}
else
return random_select(a,i+1,r,k-j);//递归调用,在后面部分查找第K大的数
}
具体实现:http://www.cnblogs.com/luxiaoxun/archive/2012/08/06/2624799.html
8、快速寻找和等于一个给定的数字的两个数
解法一:穷举法,从数组中取出任意两个数字,计算两者之和是否为给定的数字。时间复杂度为O(N^2)
解法二:假设和为Sum,对于数组中每个数字arr[i]都判断Sum-arr[i]是否在数组中,就变成一个查找问题。提高查找效率,先排序,再用二分查找法等方法进行查找,查找的时间复杂度从O(N)降到O(logN),总的时间复杂度为O(N*logN)。 更快的查找方法:hash表。给定的一个数字,根据hash映射查找另一个数字是否在数组中,只需O(1)的时间,这样总的时间复杂度降低到O(N),但这需要额外的O(N)的hash表存储空间。
解法三:先对数组排序sort(a,n),时间复杂度为O(N*logN),然后按下面的算法(O(N)的时间复杂度)查找,总的时间复杂度为O(N*logN)。用两个指针i和j指向第一和最后一个元素,如果a[j]+a[i]<sum时,++i,增大和值,否则--j,减小和值。
void FindTwoNumbers(int *a, int n, int sum)
{
sort(a,a+n); // use the STL sort algorithm
int i = 0;
int j = n-1;
while(i < j)
{
if(a[i]+a[j] == sum)
{
cout<<a[i]<<" "<<a[j]<<endl;
return;
}
else if(a[i]+a[j] < sum)
++ i;
else
-- j;
}
cout<<"No answer!"<<endl;
}
类似的一道题目:求数组中差为给定数字的两个数,先对数组排序,然后用两个指针i和j指向第一和第二个元素,如果a[j]-a[i]<dif时,++j,增大差值,否则++i,减小差值。
void FindTwoNumbers(int *a, int n, int dif)
{
sort(a,a+n); // use the STL sort algorithm
int i = 0;
int j = 1;
dif = abs(dif);
while(i < j && j < n)
{
if(a[j]-a[i] == dif)
{
cout<<a[j]<<" "<<a[i]<<endl;
return;
}
else if(a[j]-a[i] < dif)
++ j;
else
++ i;
}
cout<<"No answer!"<<endl;
}
9、子数组的最大乘积:给定一个长度为N的整数数组,只能用乘法,不能用除法,计算任意N-1个数组合中乘积最大的一组。
分析:如果可以用除法:那么用整个数组的乘积除以每个元素a[i],结果就是除了除数a[i]的剩下N-1个数的乘积。
方法一:不能用除法,数组为a[],s[i]表示数组前i个元素的乘积,s[0]=1(边界条件),s[i]=s[i-1]*a[i-1](1<=i<=N)。t[i]为数组后(N-i)个元素的乘积,t[N+1]=1(边界条件),t[i]=t[i+1]*a[i](1<=i<=N)。则除了第i个元素外,其他N-1个元素的乘积为:p[i]=s[i-1]*t[i+1]。 从头到尾扫描得到s[i],从尾到头扫描得到t[i],进而线性时间就可以得到p[i]。
方法二:利用N个数的正负分布情况。先扫描一遍,统计处数组中正数个数p,负数个数n,零的个数z,绝对值最小的正数a和负数 b。
如果 零的个数 z >= 2 结果为0
如果 零的个数 z = 1
如果负数个数n为奇数,结果为0
如果负数个数n为偶数,结果为除0外的乘积
如果 零的个数 z =0
如果负数个数n为奇数,结果为去掉绝对值最小负数后的乘积
如果负数个数n为偶数,结果为去掉绝对值最小正数后的乘积
10、题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。例如输入数组{32,321},则输出这两个能排成的最小数字32132。请给出解决问题的算法,并证明该算法。
根据题目的要求,两个数字m和n排成的数字mn和nm,如果mn<nm,那么我们应该输出mn,也就是m应该排在n的前面。但是m不一定就小于n,如{32, 321},321>32,但是32132<32321。 这道题其实是希望我们能找到一个排序规则,根据这个规则排出来的数组能排成一个最小的数字。要确定排序规则,就得比较两个数字,也就是给出两个数字m和n,我们需要确定一个规则来比较m和n组合的数哪个更小。我们知道m和n组合出来的数位数是一定的,如果直接通过组合出来的数的数值去比较,有点麻烦,需要考虑到的一个潜在问题是m和n都在int能表达的范围内,但把它们拼起来的数字mn和nm就不一定能用int表示了。所以一个直观的方法就是把数字转换成字符串,把组合出来的数转化字符串后,直接比较字符串的大小就可以,通过这个来确定m和n的组合顺序。
来自:http://zhedahht.blog.163.com/blog/static/25411174200952174133707/
#include<iostream>
using namespace std;
// 数字最大位数为10
const intg_MaxNumberLength = 10;
// 比较 strNumber1 和 strNumber2
// if [strNumber1][strNumber2]> [strNumber2][strNumber1], return value > 0
// if [strNumber1][strNumber2]= [strNumber2][strNumber1], return value = 0
// if [strNumber1][strNumber2]< [strNumber2][strNumber1], return value < 0
int compare(const void* strNumber1, const void* strNumber2)
{
char g_StrCombine1[2*g_MaxNumberLength+1];
char g_StrCombine2[2*g_MaxNumberLength+1];
//[strNumber1][strNumber2]
strcpy(g_StrCombine1, *(const char**)strNumber1);
strcat(g_StrCombine1, *(const char**)strNumber2);
//[strNumber2][strNumber1]
strcpy(g_StrCombine2, *(const char**)strNumber2);
strcat(g_StrCombine2, *(const char**)strNumber1);
return strcmp(g_StrCombine1, g_StrCombine2);
}
// 输出正整数数组组合的最小数
void PrintMinNumber(int* numbers, int length)
{
if(numbers == NULL || length <= 0)
return;
// 将数字转换为字符串
char** strNumbers = new char*[length];
for(int i = 0; i < length; ++i)
{
strNumbers[i] = new char[g_MaxNumberLength+ 1];
sprintf(strNumbers[i], "%d",numbers[i]);
}
// 以 compare() 函数为比较函数,对数字数组进行排序
qsort(strNumbers, length, sizeof(char*), compare);
for(int i = 0; i < length; ++i)
printf("%s", strNumbers[i]);
printf("\n");
for(int i = 0; i < length; ++i)
delete[] strNumbers[i];
delete[] strNumbers;
}
int main()
{
int a[] = {5,32,123};
PrintMinNumber(a,3);
return 0;
}
11、给定如下的n*n的数字矩阵,每行从左到右是严格递增, 每列的数据也是严格递增
1 3 7 15 16
2 5 8 18 19
4 6 9 22 23
10 13 17 24 28
20 21 25 26 33
现在要求设计一个算法, 给定一个数k 判断出k是否在这个矩阵中。 描述算法并且给出时间复杂度。
//从右上角开始(从左下角开始也是一样的),然后每步往左或往下走。时间复杂度O(N)。
bool stepWise(int mat[][N] , int key , int &row , int &col)
{
if(key < mat[0][0] || key > mat[N-1][N-1])
return false;
row = 0;
col = N-1;
while(row < N&& col >= 0)
{
if(mat[row][col] == key ) //查找成功
return true;
else if(mat[row][col] < key )
++row;
else
--col;
}
return false;
}
12、给定一个整数数组a[],求最接近0的子数组和。
解法:构造从第一个元素开始的子数组和sum[i]=a[0]+..+a[i],那么sum[j]-sum[i]就是a[i+1]+...+a[j],即为一个子数组的和,原数组最接近0的子数组和就是sum[]中两数之差绝对值最小的值,两两比较,得到差值的最小值,就是最接近0的子数组和。
int compare(const void* key1, const void* key2)
{
return *(int*)(key1) - *(int*)(key2);
}
int SubArraySum(const int a[], int len)
{
int* pCumuSum = new int[len];
pCumuSum[0] = a[0];
for(int i = 1; i < len; ++i)
{
pCumuSum[i] = pCumuSum[i-1] + a[i];
}
qsort(pCumuSum, len, sizeof(int), compare);
int absDiff = abs(pCumuSum[0]);
int tempAbsDiff;
for(int i = 1; i < len; ++i)
{
tempAbsDiff = abs(pCumuSum[i] -pCumuSum[i-1]);
if(tempAbsDiff < absDiff)
{
absDiff = tempAbsDiff;
}
}
delete [] pCumuSum;
return absDiff;
}
13、小飞电梯调度问题:电梯从1层往上走,只允许电梯停在某一层,所有的乘客都从一楼上电梯,到达停下来的那层,所有的乘客从这里爬到自己的目的层。电梯停在哪一层,使得所有乘客爬的楼梯层数之和最少?
方法一:穷举所有的层,计算乘客爬的楼梯层数,得到最优的那一层。
方法二:假设停在第i层,所有爬的楼梯层数为Y。如果N1个乘客目的层在第i层楼以下,有N2个乘客在第i层楼,N3个乘客目的层在第i层楼以上。如果电梯改停在第i+1层,则爬层楼数为Y+(N1+N2-N3)。如果电梯改停在第i-1层,则爬层楼数为Y+(N2+N3-N1)。由此得出:当N1+N2<N3时,电梯改停在第i+1层好,当N2+N3<N1时,电梯改停在第i-1层好。
int getTargetFloor1(int N, int person[])
{
int targetFloor = -1;
int minFloor = 65535;
int nFloor;
for(int i = 1; i <= N; ++i)
{
nFloor = 0;
for(int j = 1; j < i; ++j)
nFloor += person[j]*(i-j);
for(int j = i+1; j <= N; ++j)
nFloor += person[j]*(j-i);
if(targetFloor == -1 || nFloor < minFloor)
{
targetFloor = i;
minFloor = nFloor;
}
}
return targetFloor;
}
int getTargetFloor2(int N, int person[])
{
int targetFloor = -1;
int minFloor;
int N1, N2, N3, i;
targetFloor = 1;
minFloor = 0;
for(N1 = 0, N2 = person[1], N3 = 0, i = 2; i <= N; ++i)
{
N3 += person[i];
minFloor += person[i]*(i-1);
}
for(i = 2; i <= N; ++i)
{
if(N1 + N2 < N3)
{
targetFloor = i;
minFloor += (N1+N2-N3);
N1 += N2;
N2 = person[i];
N3 -= person[i];
}
else break;
}
return targetFloor;
}