16、找出数列中符合条件的数对的个数
引申:已知大小分别为m,n的两个无序数组A,B和一个常数C,求满足A[i]+B[j]=C的所有A[i]和B[j]
题目:一个整型数组,元素取值可能为1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现,找出数列中符合条件的数对的个数,满足数对中两数的和为N+1
分析:先对数组进行排序,然后使用二分查找方法,用两个指针分别指向第一个和最后一个元素,然后从两端同时向中间遍历,直到连个指针交叉。
(1)如果A[front]+A[back]>N+1 则back--
(2)如果A[front]+A[back]=N+1 则计数器加1,back--,同时front++
(3)如果A[front]+A[back]<N+1 则front++
void FixedSum(int a[],int n,int d)
{
for(int i=0,j=n-1;i<n && j>=0 && i<j;)
{
if(a[i]+a[j] <d)
++i;
else if(a[i]+a[j]==d)
{
cout << a[i] << ' ' << a[j] <<endl;
++i;
--j;
}
else
--j;
}
}
方法1:排序+线性扫描法,对A和B进行排序,然后用指针P从头扫描A,用指针q从尾扫描B,
如果A[P]+B[q]==c 则输出A[p]和B[q],且p++,q--;如果A[P]+B[q]>c,则q--;否则p++ ( O(mlogm+nlongn) )
void Print_Pairs_With_Sum(int a[],int b[],int m,int n,int sum)
{
sort(a,a+m);
sort(b,b+n);
int p=0;
int q=n-1;
while(p<m && q>=0)
{
if(a[p]+b[q] == sum)
{
cout << '(' << a[p] <<',' << b[q] << ')' << endl;
p++;
q--;
}
else if(a[p]+b[q] > sum)
q--;
else
p++;
}
}
方法二:Hash法,首先将两个数组中较小的数组保存到HashTable中,然后,对于B中每个元素B[i],也采用相同的hash算法。在HashTable中查找c-B[i]是否存在,如果存在,则输出,时间复杂度:O(m+n)
void Print_Pairs_With_Sum2(int a[],int b[],int m,int n,int sum)
{
map<int,bool> hash_table;
int *psmall=a;
int *pbig=b;
int nsmall=(m>=n) ? n :m;
int nbig=(m>=n) ? m : n;
if(m>n)
{
psmall=b;
pbig=a;
}
for(int i=0;i<nsmall;i++)
{
hash_table.insert(pair<int,bool>(psmall[i],true));
}
for(int i=0;i<nbig;i++)
{
if(hash_table.find(sum-pbig[i])!=hash_table.end())
cout << '(' << pbig[i] << ',' << sum-pbig[i] << ')' << endl;
}
}
17、判定数组是否存在重复元素
题目:假设数组a有n个元素,元素取值范围为1~n,如何判定数组是否存在重复元素
分析:
方法一:对数组进行排序,然后比较相邻的元素是否相同。 时间复杂度O(nlogn)
int isArrayRepeat(int a[],int n)
{
if(a==NULL || n <=0)
return -1;
sort(a,a+n);
for(int i=0;i<n-1;i++)
{
if(a[i] == a[i+1])
return 1;
}
return 0;
}
方法二:遍历数组,假设第i个位置的数字为j,则通过交换将j换到下标为j的位置,直到所有的数字都出现在自己对应的下标处,或发生了冲突。时间复杂度为O(n)
int isArrayRepeat1(int a[], int n)
{
int i=0;
int j=-1;
for(i=0;i<n;i++)
{
j=a[i];
if(i==j)
continue;
if(a[i]==a[j])
return 1;
a[i]=a[j];
a[j]=j;
}
return 0;
}
18、重新排列数组使得数组左边为奇数,右边为偶数
题目:给定一个整型数组,如何重新排列数组使得数组的左边为奇数,右边为偶数。 要求时间复杂度为O(N),空间复杂度为O(1)
分析:类似快速排序,可以用两个指针分别指向数组的头和尾。头指针正向遍历数组,找到第一个偶数,尾指针逆向遍历数组,找到第一个奇数,交换两个指针指向的数字,然后两个指针继续移动直到头指针大于等于尾指针为止
void AdjustArray(int a[], int n)
{
int begin=0;
int end=n-1;
while(begin < end)
{
while(a[begin]%2 == 1 && begin < end)
++begin;
while(a[end]%2 == 0 && begin < end)
--end;
swap(a[begin],a[end]);
}
}
19、找出数组中的第二大数
题目:找出数组中的第二大数
分析: 只通过一次遍历求出数组中的第二大数,通过设置两个变量来进行判断,首先定义一个变量来存储数组的最大数,初始值是数组首元素,另一个变量存储第二大数,初始值为最小负数,然后遍历数组元素,如果数组元素比最大值变量大,则将第二大变量的值设为最大数变量的值,最大数变量的值为该元素,如果比最大值小,则和第二大值比较;如果大,则更新第二大数的值为该数组元素的值
const int MINNUM = -2147483647-1;
int FinfSecMax(int a[],int n)
{
int max=a[0];
int sec_max=MINNUM;
for(int i=0;i<n;i++)
{
if(a[i] > max)
{
sec_max=max;
max=a[i];
}
else
{
if(a[i] > sec_max)
sec_max=a[i];
}
}
return sec_max;
}
注: 上式 const int MINNUM = -2147483647-1 写成 const int MINNUM = -2147483648 会有警告
原因如下:一元负运算符应用于无符号类型,结果仍为无符号类型。无符号类型只能保存非负值,所以一元负(非)应用于无符号类型时通常无意义。操作数和结果都是非负的。 实际上,当程序员试图表达最小整数值-2147483648 时,发生此问题。该值不能写为-2147483648,因为表达式处理分两个步骤: 计算数字2147483648。因为2147483648 大于最大整数值2147483647,所以其类型不是int,而是unsigned int。
20、寻找数组中的最小值和最大值
题目:寻找数组中的最小值和最大值
分析:
方法一:维持两个变量min和max,min标记最小值,max标记最大值,每次比较相邻两个数,较大者与max比较,较小者与min比较,找出最大值和最小值
void FindMaxAndMin(int a[], int n, int& max, int& min)
{
max=a[0];
min=a[0];
for(int i=1;i<n-1;i=i+2)
{
if(a[i]==a[i+1])
{
if(a[i]>max)
max=a[i];
if(a[i]<min)
min=a[i];
}
else if(a[i]>a[i+1])
{
if(a[i]>max)
max=a[i];
if(a[i+1]<min)
min=a[i+1];
}
else
if(a[i+1]>max)
max=a[i+1];
if(a[i]<min)
min=a[i];
}
}
方法二:分治法:将数组划分为两半,分别找出两边的最小值和最大值,则最大值,最小值分别是两边最小值的较小值、两边最大值的较大者
void FindMaxAndMin1(int a[],int begin,int end,int& max,int& min)
{
int k,max1,min1,max2,min2;
if(end-begin == 1 || end-begin==0)
a[begin] > a[end] ? (max=a[begin],min=a[end]) : (max=a[end],min=a[begin]);
else
{
k=(begin+end)/2;
FindMaxAndMin1(a,begin,k,max1,min1);
FindMaxAndMin1(a,k+1,end,max2,min2);
max=max1>max2 ? max1 : max2;
min=min1<min2 ? min1:min2;
}
}