微软算法问题全集

微软面试100题【01-20】http://blog.csdn.net/v_JULY_v/article/details/6126406

微软面试100题【21-40】http://blog.csdn.net/v_july_v/article/details/6126444


2,从1~n中求其组合sum

题目:输入两个整数n 和m,从数列1,2,3.......n 中随意取几个数,使其和等于m ,要求将其中所有的可能组合列出来.
思路:纯编程题,只能暴力求解。

list<int>list1;  
void printList(){
	// 反转list  
    list1.reverse();  
    for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)  
        cout << *iter << " ";  
    cout <<endl;  
    list1.reverse();      
}
void find_factor(int sum, int n)   
{  
	//回溯
    if(n < 0 || sum < 0)  {
		return;
	}//递归出口  
	else if(sum == 0){
		printList();
        return;  
	}

	//放入n
    list1.push_front(n);   
    find_factor(sum-n, n-1);   
	//不放入n
    list1.pop_front();  
    find_factor(sum, n-1);    

}  

3,从1 到n 的正数中1 出现的次数

题目:输入一个整数n,求从1 到n 这n 个整数的十进制表示中1 出现的次数。 例如输入12,从1 到12 这些整数中包含1 的数字有1,10,11 和12,1 一共出现了5 次。

思路:最简单算法就是暴力解题

//计算某个数字num的所有1的个数
int Count1InAnInteger(int num){
	assert(num>0);
	int count=0;
	while(num>0){
		if(num%10==1){
			count++;
		}
		num/=10;
	}
	return count;
}
//计算1~N的所有数字组合后的1的个数
int f(int N){
	assert(N>0);
	int count=0;
	while(N>0){
		count+=Count1InAnInteger(N);
		N--;
	}
	return count;
}
但是这个算法的致命问题是效率,它的时间复杂度是O(N)*计算一个整数数字里面“1”的个数的复杂度=O(N*logN)。

其他方法:找数字规律

http://www.cnblogs.com/jy02414216/archive/2011/03/09/1977724.html


4,交换序列AB中某些数使得二者差最小

题目:有两个序列a,b,大小都为n,序列元素的值任意整数,无序; 要求:通过交换a,b 中的元素,使[序列a 元素的和]与[序列b 元素的和]之间的差最小。

例如:   
int[] a = {100,99,98,1,2, 3};
int[] b = {1, 2, 3, 4,5,40};
求解思路:
当前数组a和数组b的和之差为:A = sum(a) - sum(b)
a的第i个元素和b的第j个元素交换后,a和b的和之差为:A' = sum(a) - a[i] + b[j] - (sum(b) - b[j] + a[i]) = sum(a) - sum(b) - 2 (a[i] - b[j]) = A - 2 (a[i] - b[j]) 。

我们假设初始情况sum(a)>=sum(b),由于对称的关系,我们一定可以调整ab之间的元素,仍然保持性质sum(a)>=sum(b),并且使得二者之差最小(获得最终解)。具体的方法是,每次在找a[i]和b[j]的时候,都保证a[i]>=b[j]的情况,也必须保证A'仍然>=0。也即保证A/2>=a[i]-a[j]>=0,即可。

void minDiff(int[] a, int[] b) {
	//每个部分求和
	int sum1 = sum(a);
	int sum2 = sum(b);
	if (sum1 == sum2) return;
	
	//保证sum(a)>=sum(b)
	int[] big = b;
	int[] small = a;
	if (sum1 > sum2) {		
		big = a;
		small = b;
	} 
	// the boolean value "shift" is used to denote that whether there exists 
    // a pair of elements, ai and bj, such that (ai-bj) < diff/2;
	boolean shift = true;
	int diff = 1; 
	while (shift == true  && diff > 0) {
		shift = false;
		diff = sum(big) - sum(small);
		if (diff < 0) return;
		int maxDiff = Integer.MAX_VALUE;
		//pa and pb records the switch position
		int pa = -1;
		int pb = -1;
		//the two for loops are used to find the pair of elements ai and bj 
		//such that (ai-bj) < diff/2 when sum(a) > sum(b).
		for (int i = 0; i < big.length; i++) {
			for (int j = 0; j < small.length; j++) {
				if (big[i] > small[j]) {
					int tempDiff = Math.abs(diff - 2*(big[i] - small[j]));
					//每次只要"tempDiff < diff",则说明还有优化空间
					if (tempDiff < maxDiff && tempDiff < diff) {
						shift = true;
						maxDiff = tempDiff;
						pa = i;pb = j;
					}
				}
			}
		}
		if (shift == true) {
			int temp = big[pa];
			big[pa] = small[pb];
			small[pb] = temp;
		}
	}
 }

5,excel的26进制

题目:在Excel中,列的名称是这样一个递增序列:A、B、C、…、Z、AA、AB、AC、…、AZ、BA、BB、BC、…、BZ、CA、…、ZZ、AAA、AAB…。我们需要将上述列名序列和以下自然数序列相互转换:1、2、3、…。

string table[] = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};

string ToNumberSystem26(int num){
	if(num<=0)
		return string("");
	return ToNumberSystem26((num-1)/26).append(table[(num-1)%26]);
}


6,寻找两个有序数组的中位数

http://blog.csdn.net/hhygcy/article/details/4584064

int FindMiddleElm(int A[], int aStart, int aEnd, int B[], int bStart, int bEnd){
	int aLen = aEnd-aStart+1;
	int bLen = bEnd-bStart+1;
	int aMid = (aStart+aEnd)/2;
	int bMid = (bStart+bEnd)/2;

	//数组A为空
	if(aLen<=0)
		return B[bMid];
	if(bLen<=0)
		return A[aMid];
	if(aLen==1){
		if(bLen%2==1){
			if(A[aStart]>B[bMid])
				return B[bMid];
			else
				return B[bMid-1]>A[aStart]?B[bMid-1]:A[aStart];
		}
		else{
			if(A[aStart]>B[bMid])
				return B[bMid+1]>A[aStart]?A[aStart]:B[bMid+1];
			else
				return B[bMid];
		}
	}
	if(bLen==1){
		if(aLen%2==1){
			if(B[bStart]>A[aMid])
				return A[aMid];
			else
				return A[aMid-1]>B[bStart]?A[aMid-1]:B[bStart];
		}
		else{
			if(B[bStart]>A[aMid])
				return A[aMid+1]>B[bStart]?B[bStart]:A[aMid+1];
			else
				return A[aMid];
		}
	}

	int aHalfLen = aMid-aStart+1;
	int bHalfLen = bMid-bStart+1;
	int minHalfLen = aHalfLen<bHalfLen?aHalfLen:bHalfLen;

	//每种情况能去掉1/2
	if(A[aMid]==B[bMid])
		return A[aMid];

	//没有必要搜索A[aStart...aMid] B[bMid...bEnd]这些元素
	if (A[aMid] < B[bMid]){  
		return FindMiddleElm(A, aStart+minHalfLen, aEnd, B, bStart, bEnd-minHalfLen);  
	}  
	//没有必要搜索B[bStart...bMid] A[aMid...aEnd]这些元素
	else{//A[mid]>B[Mid]   
		return FindMiddleElm(A, aStart, aEnd-minHalfLen, B, bStart+minHalfLen, bEnd);  
	}  
}  

7,寻找两个有序数组的第K大数

//Notice : K > 0  
int FindKthElm(int A[], int aStart, int aEnd, int B[], int bStart, int bEnd, int k){
	//数组A为空
	if(aStart>aEnd)
		return B[bStart+k-1];
	if(bStart>bEnd)
		return A[aStart+k-1];

	int aMid = aStart+(aEnd-aStart)/2;
	int bMid = bStart+(bEnd-bStart)/2;
	//从A和B的开始位置到两个数组中间位置的元素个数 
	int halfLen = (aMid-aStart+1)+(bMid-bStart+1);

	//每种情况只能去掉1/4
	if (A[aMid] < B[bMid])  
	{  
		if (halfLen > k)  
		{  
			// 没有必要搜索 B[bMid...bEnd]这些元素  
			return FindKthElm(A, aStart, aEnd, B, bStart, bMid - 1, k);  
		}  
		else  
		{  
			// 没有必要搜索 A[aStart...aMid]这些元素,个数为k - (aMid - aStart + 1)  
			return FindKthElm(A, aMid + 1, aEnd, B, bStart, bEnd, k - (aMid - aStart + 1));  
		}  
	}  
	else  
	{  
		//注释与上面相似  
		if (halfLen > k)  
		{  
			return FindKthElm(A, aStart, aMid - 1, B, bStart, bEnd, k);  
		}  
		else  
		{  
			return FindKthElm(A, aStart, aEnd, B, bMid + 1, bEnd, k - (bMid - bStart + 1));  
		}  
	}  
}  

8,和谐系统:实现一个挺高级的字符匹配算法

http://www.cnblogs.com/tianshuai11/archive/2012/04/22/2477165.html

例如目的串:123
则:1******3***2 ,12*****3这些都要找出来

思路:因为只要目的串在应用字符串中存在,而无需考虑出现的顺序,因此可以直接hash得到:

int main()
{
	char *src="anbmcj"; 
	char *des="abc";

	//创建一个哈希表,并初始化
	const int tableSize = 256; //因为ASCII码共有256个 
	int hashTable[tableSize];
	int len,i;

	for(i = 0; i < tableSize; i++)
		hashTable[i] = 0;

	len = strlen(src);
	for(i = 0; i < len; i++)
		hashTable[src[i]] = 1;

	len = strlen(des);
	for(i = 0; i < len; i++)
	{
		if(hashTable[des[i]] == 0)//如果des中的任何一个字母没有,则匹配失败 
		{
			cout<<"和谐失败,为正规内容"<<endl;  
		}     
	}

	cout<<"您的内容被和谐"<<endl; 

	return 1;    //匹配成功
}


9,生产者消费者队列

队列的应用场景为:一个生产者线程将int类型的数入列,一个消费者线程将int类型的数出列

思路:信号量内核对象如下所示
信号量(Semaphore)它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。
    WaitForSingleObject();//等待信号量有效   
    CreatSemaphore();//申请信号量   
    OpenSemaphore();//打开信号量   
    ReleaseSemaphore();//释放信号量 

#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <iostream>
#include <queue>
using namespace std;


HANDLE ghSemaphore; //信号量
const int gMax = 100; //生产(消费)总数
std::queue<int> q; //生产入队,消费出队

//生产者线程
unsigned int __stdcall producerThread(void* pParam)
{
    int n = 0;
    while(++n <= gMax)
    {
        //生产
        q.push(n);
        cout<<"produce "<<n<<endl;
        ReleaseSemaphore(ghSemaphore, 1, NULL); //对信号量ghSemaphore增加1
        Sleep(300);//生产间隔的时间,可以和消费间隔时间一起调节
    }
    _endthread(); //生产结束
    return 0;
}
//消费者线程
unsigned int __stdcall customerThread(void* pParam)
{
    int n = gMax;
    while(n--)
    {
        //等待信号量。
        //当指定的对象ghSemaphore处于有信号状态或者等待时间10000结束的状态时,此函数返回,执行后面的代码
        WaitForSingleObject(ghSemaphore, 10000);
        //消费
        cout<<"custom "<<q.front()<<endl;
        q.pop();
        Sleep(500);//消费间隔的时间,可以和生产间隔时间一起调节
    }
    //消费结束
    CloseHandle(ghSemaphore);
    cout<<"working end."<<endl;
    _endthread();
    return 0;
}

void threadWorking()
{
    ghSemaphore = CreateSemaphore(NULL, 0, gMax, NULL); //信号量来维护线程同步
    cout<<"working start."<<endl;
    unsigned threadID;
    HANDLE handles[2];
	handles[0] = (HANDLE)_beginthreadex(
		NULL,
		0,
		producerThread,
		nullptr,
		0,
		&threadID);
	handles[1] = (HANDLE)_beginthreadex(
		NULL,
		0,
		customerThread,
		nullptr,
		0,
		&threadID);
   
    //等待以上创建的两个线程执行结束
    WaitForMultipleObjects(2, handles, TRUE, INFINITE);
}


int main()
{
    threadWorking();
    getchar();
    return 0;
}


10,求一个数组中最大的子序列(元素和最大)

题目: 输入一个整形数组,数组里有正数也有负数。 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。 求所有子数组的和的最大值。要求时间复杂度为O(n)。 例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2, 因此输出为该子数组的和18。

思路:贪心算法,遍历一遍,如果sum<0,则reset sum =0

int maxSubarray(int a[], int size) { 
	if (size<=0) 
		error("error array size"); 

	int sum = 0; 
	int max = - (1 << 31); 

	for(int i=0; i<n; i++){
		sum+=a[i];
		//不断更新max
		if(sum>max){
			max = sum;
		}
		//不断更新sum
		if(sum<0){
			sum = 0;
		}
	}
	return max; 
} 

你也可以这么理解:

定义b[j]为数组中包含a[j]的最大连续子序列和,注意一个误区,b[j] 并不是1-j中最大的连续子序列的和,只是包含a[j]的最大子序列的和。而我们所要求的是求出b[j]中最大的值,即为所求:
状态方程为: b[j] = max(b[j-1] + a[j] , a[j]) 


11,求一个矩阵中最大的二维矩阵(元素和最大)

如:
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
中最大的是:
4 5
5 3
要求:(1)写出算法;(2)分析时间复杂度;(3)用C写出关键代码

思路1:这是最容易想到也是最容易实现的方法。遍历矩阵(行迭代器为i,列迭代器为j),以当前遍历到的元素为首a[i,j],计算二维子矩阵的和(sum=a[i,j]+a[i+1,j]+a[i,j+1]+a[i+1,j+1]),并找出和最大的二维矩阵,注意矩阵的最后一行和最后一列不用遍历。时间复杂度为O(i*j)

#include <iostream>
#include <assert.h>
using namespace std;

int get_max_22Matrix(int *a,int row,int col,int *result){
	//用一维数组存储二维数组
	#define a(i,j) *(a+(i)*col+(j))    
	//访问四个元素并相加得到当前的和 
	#define sum(i,j)  (a(i,j)+a(i+1,j)+a(i,j+1)+a(i+1,j+1))
	//返回的二维数组
	#define result(i,j) *(result+(i)*2+(j))  

	int max_sum = 0;
	int result_i,result_j;
	assert(row>=2&&col>=2);
	for(int i=0;i<row-1;i++){
		for(int j=0;j<col-1;j++){
			int cur_sum = sum(i,j);
			if(cur_sum>max_sum){
				max_sum = cur_sum;
				result_i=i;
				result_j=j;
			}
		}
	}
	/* 将结果存储到result二维数组中*/  
	result(0,0)=a(result_i,result_j);  
	result(1,0)=a(result_i+1,result_j);  
	result(0,1)=a(result_i,result_j+1);  
	result(1,1)=a(result_i+1,result_j+1);  
	return max_sum;
}

int main(){

	int matrix[]={1,2,0,3,4,2,3,4,5,1,1,1,5,3,0};
	int result[4];
	cout<<get_max_22Matrix(matrix,3,5,result)<<endl;
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			cout<<result[i+j*2]<<" ";
		}
		cout<<endl;
	}
	system("pause");
	return 0;
}

思路2,思路3:

列大于行的矩阵使用方法二,也就是横向遍历
行大于列的矩阵使用方法三,也就是纵向遍历

复杂度没有变,只是小做改进,详细图文参见博客:

http://blog.csdn.net/zhanxinhang/article/details/6731134


12,求一个矩阵中最大子矩阵(元素和最大)???

http://blog.csdn.net/wangyangkobe/article/details/6358884

http://www.cppblog.com/superKiki/archive/2010/05/27/116465.html


13,多支队伍比赛结果???

n支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间的实力对比关系,存储在一个二维数组w[n][n]中,w[i][j] 的值代表编号为i,j的队伍中更强的一支。所以w[i][j]=i 或者j,现在给出它们的出场顺序,并存储在数组order[n]中,比如order[n] = {4,3,5,8,1......},那么第一轮比赛就是 4对3, 5对8。.......胜者晋级,败者淘汰,同一轮淘汰的所有队伍排名不再细分,即可以随便排,下一轮由上一轮的胜者按照顺序,再依次两两比,比如可能是4对5,直至出现第一名。

编程实现,给出二维数组w,一维数组order 和 用于输出比赛名次的数组result[n],求出result。


14,连接若干段字符串问题???

有n个长为m+1的字符串,如果某个字符串的最后m个字符与某个字符串的前m个字符匹配,则两个字符串可以联接,问这n个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。


15,求一个有向连通图的割点???

割点的定义是,如果除去此节点和与其相关的边, 有向图不再连通,描述算法。

16,电子灯变化问题

有电子LED灯,将一盏灯比如4转换为5,需要关闭和打开一些灯,求输入数字n转变为m需要的变化次数。

  

思路:一个LED灯最多7个灯管,用一个char数组来记录每个数字的7个灯管的亮灭(硬编码),然后n^m中的1的个数就是要转变的次数。

#include <iostream>
#include <assert.h>
using namespace std;
#define NUM_OF_DIGIT 10
char digits[NUM_OF_DIGIT];
void init(){
	digits[0]=0x77;
	digits[1]=0x12;
	digits[2]=0x5d;
	digits[3]=0x5b;
	digits[4]=0x3a;
	digits[5]=0x6b;
	digits[6]=0x6f;
	digits[7]=0x52;
	digits[8]=0x7f;
	digits[9]=0x73;
}
int transfer(char num1,char num2){
	assert(num1>='0'&&num1<='9'&&num2>='0'&&num2<='9');
	char temp = digits[num1-'0']^digits[num2-'0'];
	//计算temp中1的个数
	int count=0;
	while(temp){
		temp&=temp-1;
		count++;
	}
	return count;
}
int main(){
	init();
	char n,m;
	cin>>n>>m;
	cout<<transfer(n,m)<<endl;
	system("pause");
	return 0;
}

17,字符串匹配问题

思路:用递归来实现

//用m去匹配s
int strMatch(char * s,char * m){
	int len_s = strlen(s);
	int len_m = strlen(m);
	if(len_m==0)
		return 1;
	int count=0;
	for(int i=0;i<len_s;i++){
		if(s[i]==m[0]){
			count+= strMatch(s+i,m+1);
		}
	}
	return count;
}

18,一串首位相连的珠子

一串首尾相连的珠子(m 个),有N 种颜色(N<=10),设计一个算法,取出其中一段,要求包含所有N 中颜色,并使长度最短。 


        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值