最近参加了一个公司的笔试,其中有一道题如下:给定一个array,判断是否存在3个元素的和为0?
最开始看到这题没有细想特别优化的方法,居然直接采用了暴力搜索((⊙﹏⊙)b汗!),直接用了一个三层循环求解,代码如下:
最开始看到这题没有细想特别优化的方法,居然直接采用了暴力搜索((⊙﹏⊙)b汗!),直接用了一个三层循环求解,代码如下:
bool judge(int *arr,int n){
for(int i = 0;i<n-2;++i){
for(int j = i+1;j<n-1;++j){
for(int k = j+1;k<n;++k)
if(arr[i]+arr[j]+arr[k]==0)
return true;
}
}
return false;
}
很显然这样的方法是不推荐的,复杂度太高。
改进1:首先对原数组进行排序,再用两层for循环固定前面两个数,然后第三个数用和减去前面2个数,最后采用二分查找的方法检查是否存在。该方法的复杂度为O(n^2*log(n))。
改进2:首先对原数组进行排序,除去一个固定的数字之后剩下两个数字,然后用两个指向数组的指针,一个从前往后扫描,一个从后往前扫描,记为first和last,如果 fist + last < sum 则将fist向前移动,如果fist + last > sum,则last向后移动。该方法的复杂度为O(n^2)。代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
void printPairSums(int data[], int size, int sum)
{
int first = 0;
int last = size -1;
int s = 0;
while (first < last)
{
s = data[first] + data[last];
if (s == sum)
{
cout << data[first] << " + " << data[last] << " = " << sum << endl;
first++;
last--;
}
else if (s < sum)
first++;
else
last--;
}
}
int main(int argc, char* argv[])
{
int data[] = {1, 5, 9, -1, 4, 6, -2, 3, -8};
int size = sizeof(data) / sizeof(data[0]);
int i;
sort(data, data + size);
printPairSums(data, size, 8);
system("pause");
return 0;
}
改进3:采用回溯法。代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int S = 22;
int sumCheck(int A[], int start, int end, int arr[], int cnt){
if (cnt == 2){
int sum = arr[0] + arr[1] + arr[2];
if (sum == S)
cout << arr[0] << "\t" << arr[1] << "\t" << arr[2] << endl;
return 0;
}
if (start > end || cnt > 2)
return -1;
int i = start;
arr[++cnt] = A[i];
sumCheck(A, start + 1, end, arr, cnt);
arr[cnt] = 0;
cnt--;
sumCheck(A, start + 1, end, arr, cnt);
}
int main(int argc, char* argv[])
{
int A[] = {1, 4, 45, 6, 10, 8};
int arr_size = sizeof(A) / sizeof(A[0]);
int cnt = -1;
int arr[3] = {0, 0, 0};
sumCheck(A,0, arr_size-1, arr, cnt);
system("pause");
return 0;
}
问题的推广可参考博客求数组中多个数相加等于某一值以及面试题:检查一个数组里是否存在m个数的和等于某个值。