最常见的程序员面试题(3)置换算法

数组置换的题目是一类非常简单的动态规划。因为没有确定的递归步骤和算法。

(1) 例如,给出两个数组,要求调换数组当中的某些元素使得两个数组的各自元素之和,这两个和之间的绝对值之差最小。

     怎么办呢? 循环扫描第一个数组当中的元素,比较第二个数组中的所有元素,如果某次对比发现交换他们能够使得交换之后的和的绝对值差最小,就交换这两个元素。反复进行直到扫描第一个数组每个元素都不需要再交换元素为止。用C++和Java分别实现:

  1. #include<cstdlib>   
  2. #include<algorithm>   
  3. using namespace std;  
  4. size_t s=0;  
  5. void f(int* A,int* B){  
  6.     int sumA=0;  
  7.     int sumB=0;  
  8.     for(size_t i=0;i<s;++i){  
  9.         sumA+=A[i];  
  10.     }  
  11.     for(size_t i=0;i<s;++i){  
  12.         sumB+=B[i];  
  13.     }  
  14.     bool change=true;  
  15.     while(change){  
  16.         change=false;  
  17.         for(size_t a=0;a<s;++a){  
  18.             for(size_t b=0;b<s;++b){  
  19.                 int ab1=abs(sumA-sumB);  
  20.                 int ab2=abs(sumA+2*B[b]-sumB-2*A[a]);  
  21.                 if(ab1>ab2){  
  22.                     sumA+=B[b];  
  23.                     sumA-=A[a];  
  24.                     sumB-=B[b];  
  25.                     sumB+=A[a];  
  26.                     swap(A[a],B[b]);  
  27.                     change=true;  
  28.                 }  
  29.             }  
  30.         }  
  31.     }  
  32.     printf("\nA=");  
  33.     for_each(A,A+s,[]( const int& i ){printf("%d,",i);});  
  34.     printf("\nB=");  
  35.     for_each(B,B+s,[]( const int& i ){printf("%d,",i);});  
  36. }  
  37. int main(void){  
  38.     int A[]={3,4,12};  
  39.     int B[]={2,3,9};  
  40.     int C[]={1,2,3};  
  41.     int D[]={0,999,0};  
  42.     s=_countof(A);  
  43.     f(A,B);  
  44.     printf("\n.........................\n");  
  45.     f(C,D);  
  46.     return 0;  
  47. }  
#include<cstdlib>
#include<algorithm>
using namespace std;
size_t s=0;
void f(int* A,int* B){
	int sumA=0;
	int sumB=0;
	for(size_t i=0;i<s;++i){
		sumA+=A[i];
	}
	for(size_t i=0;i<s;++i){
		sumB+=B[i];
	}
	bool change=true;
	while(change){
		change=false;
		for(size_t a=0;a<s;++a){
			for(size_t b=0;b<s;++b){
				int ab1=abs(sumA-sumB);
				int ab2=abs(sumA+2*B[b]-sumB-2*A[a]);
				if(ab1>ab2){
					sumA+=B[b];
					sumA-=A[a];
					sumB-=B[b];
					sumB+=A[a];
					swap(A[a],B[b]);
					change=true;
				}
			}
		}
	}
	printf("\nA=");
	for_each(A,A+s,[]( const int& i ){printf("%d,",i);});
	printf("\nB=");
	for_each(B,B+s,[]( const int& i ){printf("%d,",i);});
}
int main(void){
	int A[]={3,4,12};
	int B[]={2,3,9};
	int C[]={1,2,3};
	int D[]={0,999,0};
	s=_countof(A);
	f(A,B);
	printf("\n.........................\n");
	f(C,D);
	return 0;
}
  1. public class my{  
  2.     static void f(int[] A,int[] B){  
  3.         int sumA=0;  
  4.         int sumB=0;  
  5.         forint a: A ){  
  6.             sumA+=a;  
  7.         }  
  8.         forint b: B ){  
  9.             sumB+=b;  
  10.         }  
  11.         boolean change=true;  
  12.         while(change){  
  13.             change=false;  
  14.             for(int a=0;a<A.length;++a){  
  15.                 for(int b=0;b<B.length;++b){  
  16.                     int ab1=Math.abs(sumA-sumB);  
  17.                     int ab2=Math.abs(sumA+2*B[b]-sumB-2*A[a]);  
  18.                     if(ab1>ab2){  
  19.                         sumA+=B[b];  
  20.                         sumA-=A[a];  
  21.                         sumB-=B[b];  
  22.                         sumB+=A[a];  
  23.                         int tmp=A[a];  
  24.                         A[a]=B[b];  
  25.                         B[b]=tmp;  
  26.                         change=true;  
  27.                     }  
  28.                 }  
  29.             }  
  30.         }  
  31.         System.out.println("=>A");  
  32.         forint a: A ){  
  33.             System.out.print(a);  
  34.             System.out.print(',');  
  35.         }  
  36.         System.out.println("\n=>B");  
  37.         forint b: B ){  
  38.             System.out.print(b);  
  39.             System.out.print(',');  
  40.         }  
  41.     }  
  42.     public static void main(String[] args) {  
  43.         int A[]={3,4,12};  
  44.         int B[]={2,3,9};  
  45.         int C[]={1,2,3};  
  46.         int D[]={0,999,0};  
  47.         my.f(A, B);  
  48.         System.out.println("==========");  
  49.         my.f(C, D);  
  50.     }  
  51. }  
public class my{
	static void f(int[] A,int[] B){
		int sumA=0;
		int sumB=0;
		for( int a: A ){
			sumA+=a;
		}
		for( int b: B ){
			sumB+=b;
		}
		boolean change=true;
		while(change){
			change=false;
			for(int a=0;a<A.length;++a){
				for(int b=0;b<B.length;++b){
					int ab1=Math.abs(sumA-sumB);
					int ab2=Math.abs(sumA+2*B[b]-sumB-2*A[a]);
					if(ab1>ab2){
						sumA+=B[b];
						sumA-=A[a];
						sumB-=B[b];
						sumB+=A[a];
						int tmp=A[a];
						A[a]=B[b];
						B[b]=tmp;
						change=true;
					}
				}
			}
		}
		System.out.println("=>A");
		for( int a: A ){
			System.out.print(a);
			System.out.print(',');
		}
		System.out.println("\n=>B");
		for( int b: B ){
			System.out.print(b);
			System.out.print(',');
		}
	}
	public static void main(String[] args) {
		int A[]={3,4,12};
		int B[]={2,3,9};
		int C[]={1,2,3};
		int D[]={0,999,0};
		my.f(A, B);
		System.out.println("==========");
		my.f(C, D);
	}
}

(2) 还有一类问题时和排序/分段相关的。例如partition程序要把一个数组分成前后两段,好比说把一个整数数组分成奇数部分和偶数部分。O(n)就可以实现,C++代码:

  1. #include <iostream>   
  2. #include <cstdio>   
  3. using namespace std;  
  4. //前后各设置一个指针,向中间移动   
  5. void partition1( int* pi, size_t size )  
  6. {  
  7.     int* pH = pi;  
  8.     int *pT = pi+size;  
  9.     while(pH<pT){  
  10.         while( ( (*pH)%2 == 1 ) && pH<pT )++pH;  
  11.         if(pH<pT)  
  12.             while( (*--pT)%2 == 0 );  
  13.         if(pH!=pT){  
  14.             int temp=*pH;  
  15.             *pH=*pT;  
  16.             *pT=temp;  
  17.         }  
  18.     }  
  19. }  
  20. int main()  
  21. {  
  22.     int buf[]={3,6,7,7,2,3,4,2,1,8,9,10,5};//奇数放前面,偶数放后面   
  23.     size_t size=sizeof(buf)/sizeof(buf[0]);  
  24.     partition1(buf,size);  
  25.     for(size_t s=0;s<size;++s){  
  26.         printf("%d,",buf[s]);  
  27.     }  
  28.     return 0;  
  29. }  
#include <iostream>
#include <cstdio>
using namespace std;
//前后各设置一个指针,向中间移动
void partition1( int* pi, size_t size )
{
    int* pH = pi;
    int *pT = pi+size;
    while(pH<pT){
        while( ( (*pH)%2 == 1 ) && pH<pT )++pH;
        if(pH<pT)
            while( (*--pT)%2 == 0 );
        if(pH!=pT){
            int temp=*pH;
            *pH=*pT;
            *pT=temp;
        }
    }
}
int main()
{
    int buf[]={3,6,7,7,2,3,4,2,1,8,9,10,5};//奇数放前面,偶数放后面
    size_t size=sizeof(buf)/sizeof(buf[0]);
    partition1(buf,size);
    for(size_t s=0;s<size;++s){
        printf("%d,",buf[s]);
    }
    return 0;
}

 (3) 第3类问题是和求字典序有关的问题。如何求一个排列的"下一个排列"? 这个经典的字典序问题。例如我们有一个数组: 

1,2,3,5,7,6,2,2,1 要求下一个升序的排列。那么首先从后往前找一个正序,也就是(5,7),然后从5开始往后找到比5大的最小的数6,交换(5,6)得到

1,2,3,6,7,5,2,2,1 可以看出来此时6后面的数是严格的降序,那么用前后指针的方法(就像前面所说的partiion算法类似的),调整为1,2,3,6,1,2,2,5,7就是要求的结果。

C++源代码如下:

  1. #include <iostream>   
  2. #include <cstdlib>   
  3. using namespace std;  
  4. void my_permutation(int* buf, size_t nCount){  
  5.     //注释的部分解释第一次函数调用的结果。   
  6.     //首先从后往前找到第一个正序,第一次调用函数时,正序位置是5,9   
  7.     size_t idx = nCount - 1;  
  8.     for( ; idx >=1; --idx ){ // 反向查找   
  9.         if( buf[idx-1] >= buf[idx] )continue;//跳过重复的值: 1,2,2,7,9,5找到5   
  10.         else break;  
  11.     }  
  12.     --idx;  
  13.     if(idx<=0)return;  
  14.     //此时idx=3 cout<<idx<<endl;   
  15.       
  16.     //从5向后找到比5大的最小的数7,交换5和7。   
  17.     size_t nSwap = 0; //数的位置   
  18.     int nStore = buf[idx]; //寻找7   
  19.     bool fSwap = false// 找到了一个比5大的数   
  20.     forsize_t i = idx+1; i < nCount; ++ i ){  
  21.         if( buf[i] > buf[idx] ){// 大于5   
  22.             if( fSwap == false ){  
  23.                 fSwap = true;  
  24.                 nStore = buf[i]; // 找到了第一个比5大的数: 9   
  25.                 nSwap  = i;  
  26.             }  
  27.             else// 之前已经更新过fSwap了   
  28.                 if( buf[i] < nStore ){  
  29.                     nStore = buf[i]; //选一个更小的值: 7   
  30.                     nSwap = i;  
  31.                 }  
  32.             }  
  33.         }  
  34.     }  
  35.     //此时nSwap = 5, cout<<nSwap<<endl;   
  36.     swap( buf[idx], buf[nSwap] );  
  37.     ++idx; // 从idx后面的位置开始   
  38.     //交换后7之后的数字首位两两交换,得到第一正序   
  39.     size_t indexH = idx;  
  40.     size_t indexT = nCount -1;  
  41.     while( indexH <= indexT ){  
  42.         swap( buf[indexH++], buf[indexT--] );  
  43.     }  
  44.     forsize_t p = 0; p < nCount; ++ p ){  
  45.         cout<<buf[p]<<',';  
  46.     }  
  47.     cout<<endl;  
  48. }  
  49. int main(){   
  50.     int buf[]={1,2,3,5,7,6,2,2,1};  
  51.     my_permutation(buf,_countof(buf));  
  52.     my_permutation(buf,_countof(buf));  
  53.     my_permutation(buf,_countof(buf));  
  54.     my_permutation(buf,_countof(buf));  
  55.     my_permutation(buf,_countof(buf));  
  56.     my_permutation(buf,_countof(buf));  
  57.     return 0;  
  58. }  
#include <iostream>
#include <cstdlib>
using namespace std;
void my_permutation(int* buf, size_t nCount){
    //注释的部分解释第一次函数调用的结果。
    //首先从后往前找到第一个正序,第一次调用函数时,正序位置是5,9
    size_t idx = nCount - 1;
    for( ; idx >=1; --idx ){ // 反向查找
        if( buf[idx-1] >= buf[idx] )continue;//跳过重复的值: 1,2,2,7,9,5找到5
        else break;
    }
    --idx;
    if(idx<=0)return;
    //此时idx=3 cout<<idx<<endl;
    
    //从5向后找到比5大的最小的数7,交换5和7。
    size_t nSwap = 0; //数的位置
    int nStore = buf[idx]; //寻找7
    bool fSwap = false; // 找到了一个比5大的数
    for( size_t i = idx+1; i < nCount; ++ i ){
        if( buf[i] > buf[idx] ){// 大于5
            if( fSwap == false ){
                fSwap = true;
                nStore = buf[i]; // 找到了第一个比5大的数: 9
                nSwap  = i;
            }
            else{ // 之前已经更新过fSwap了
                if( buf[i] < nStore ){
                    nStore = buf[i]; //选一个更小的值: 7
                    nSwap = i;
                }
            }
        }
    }
    //此时nSwap = 5, cout<<nSwap<<endl;
    swap( buf[idx], buf[nSwap] );
    ++idx; // 从idx后面的位置开始
    //交换后7之后的数字首位两两交换,得到第一正序
    size_t indexH = idx;
    size_t indexT = nCount -1;
    while( indexH <= indexT ){
        swap( buf[indexH++], buf[indexT--] );
    }
    for( size_t p = 0; p < nCount; ++ p ){
        cout<<buf[p]<<',';
    }
    cout<<endl;
}
int main(){ 
    int buf[]={1,2,3,5,7,6,2,2,1};
    my_permutation(buf,_countof(buf));
    my_permutation(buf,_countof(buf));
    my_permutation(buf,_countof(buf));
    my_permutation(buf,_countof(buf));
    my_permutation(buf,_countof(buf));
    my_permutation(buf,_countof(buf));
    return 0;
}

运行结果是:
1,2,3,6,1,2,2,5,7,
1,2,3,6,1,2,2,7,5,
1,2,3,6,1,2,5,2,7,
1,2,3,6,1,2,5,7,2,
1,2,3,6,1,2,7,2,5,
1,2,3,6,1,2,7,5,2,

等效的stl代码调用如下:

  1. #include <iostream>   
  2. #include <cstdlib>   
  3. #include <algorithm>   
  4. int main(){   
  5.     int buf[]={1,2,3,5,7,6,2,2,1};  
  6.     do{  
  7.         static int count = 0;  
  8.         forsize_t i = 0; i < _countof(buf); ++ i ){  
  9.             cout<<buf[i]<<',';  
  10.         }  
  11.         cout<<endl;  
  12.         if(++count>6)break;  
  13.     }while( std::next_permutation( buf, buf+_countof(buf) ) );  
  14.     return 0;  
  15. }  
#include <iostream>
#include <cstdlib>
#include <algorithm>
int main(){ 
    int buf[]={1,2,3,5,7,6,2,2,1};
    do{
        static int count = 0;
        for( size_t i = 0; i < _countof(buf); ++ i ){
            cout<<buf[i]<<',';
        }
        cout<<endl;
        if(++count>6)break;
    }while( std::next_permutation( buf, buf+_countof(buf) ) );
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值