精选微软等公司数据结构+算法面试100题带答案(11-30)

11、如果我们把二叉树看成一个图,父子节点之间的连线看成是双向的,我们姑且定义"距离"为两节点之间边的个数。写一个程序,求一棵二叉树中相距最远的两个节点之间的距离。

    思路:求树的高度。参考:http://blog.csdn.net/dazhong159/article/details/7862774

12、题目:求1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。

    利用&&的短路性质,结束递归。

int add_fun(int n)
{
	int sum=0;
	n && (sum=add_fun(n-1));
	return sum+n;
}

13、输入一个单向链表(带头结点),输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针(最后一个元素)。
    思路:用两个指针,开始时均指向头结点,一个指针先走k+1步,然后两个指针同时走。

struct Node
{
	int m_nKey;
	Node* next;
};

//带头节点的链表
Node* lastK(Node *head, int k) 
{
	if(head==NULL)
	{
		cout<<"链表不能为空!"<<endl;
		return NULL;
	}
	if (k<0) 
	{
		cout<<"k不能为负值!"<<endl;
		return NULL;
	}

	Node *p=head, *pk=head;
	for (int i=0;i<=k;i++)   //pk先走k+1步
	{
		if (pk!=NULL) 
			pk = pk->next;
		else 
			return NULL;
	}

	while (pk!=NULL) 
	{
		p=p->next;
		pk=pk->next;
	} 
	
	return p;
}

14、输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。

void find2Number(int a[], int n, int dest) 
{
	int *f = a, *e = a+n-1;
	int sum = *f + *e;
	while ((sum = *f + *e) != dest && f < e) 
	{
		if (sum < dest) 
			++f;
		else 
			--e;
	}
	if (sum == dest) 
		printf("%d, %d\n", *f, *e);
}

15、输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点。用递归和循环两种方法完成树的镜像转换。

struct Node
{
	int data;
	Node *left;
	Node *right;
};

//交换两个节点
void swap(Node ** l, Node ** r) 
{
	Node * p = *l;
	*l = *r;
	*r = p;
}

//递归法
void mirror(Node * root) 
{
	if (root == NULL) 
		return;
	swap(&(root->left), &(root->right));
	mirror(root->left);
	mirror(root->right);
}

//循环法
void mirrorIteratively(Node * root) 
{
	if (root == NULL) 
		return;

	stack<Node*> buf;
	buf.push(root);
	while (!buf.empty()) 
	{
		Node *n = buf.top();
		buf.pop();
		swap(&(n->left), &(n->right));
		if (n->left != NULL) 
			buf.push(n->left);
		if (n->right != NULL) 
			buf.push(n->right);
	}
}
    

    由于递归的本质是编译器生成了一个函数调用的栈,因此用循环来完成同样任务时最简单的办法就是用一个辅助栈来模拟递归。首先我们把树的头结点放入栈中。在循环中,只要栈不为空,弹出栈的栈顶结点,交换它的左右子树。如果它有左子树,把它的左子树压入栈中;如果它有右子树,把它的右子树压入栈中。这样在下次循环中就能交换它儿子结点的左右子树了

16、输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。

    见: http://blog.csdn.net/dazhong159/article/details/7862774

17、在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。

char firstSingle(char * str) 
{
	int a[255];  //存储对应字符出现的次数
	memset(a, 0, 255*sizeof(int));

	char *p=str;
	while (*p!='\0')  //第一次遍历,记录字符以及相对应出现的次数
	{
		a[*p]++;
		p++;
	}

	p = str;
	while (*p!='\0')  //第二次遍历,求取第一次且只出现一次的字符
	{
		if (a[*p] == 1) 
			return *p;
		p++;
	} 
	return '\0'; 
}

18、n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。当一个数字删除后,从被删除数字的下一个继续删除第m个数字。求出在这个圆圈中剩下的最后一个数字。

    见:http://blog.csdn.net/dazhong159/article/details/7849235

int joseph(int n, int m) 
{
	int fn=0;
	for (int i=2; i<=n; i++) 
	{
		fn = (fn+m)%i; 
	}

	return fn;
}

19、Fibonacci数列(f(0)=0, f(1)=1),输入n,用最快的方法求该数列的第n项。

    见:http://blog.csdn.net/dazhong159/article/details/7943803

20、输入一个表示整数的字符串,把该字符串转换成整数并输出。例如输入字符串"345",则输出整数345。

    见:http://blog.csdn.net/dazhong159/article/details/7827952

21、输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来.

#include "stdafx.h"
#include "stdlib.h"
#include <iostream.h>
#include <string.h>

#define N 1000
int a[N];  //辅助数组,用来存储满足条件的组合
int c=0, n=20, m=30; 

void work(int sum, int cc)
{
	//和等于m,递归终止,输出组合
	if(sum == m)  
	{ 
		for(int i = 0; i < c; ++i)
			printf("%d ", a[i]);
		printf("\n");
		return;
	}
	
	//数列从1到n遍历一遍
	for(int i = cc; i <= n; ++i)  
	{
		//和大于m,此次递归终止
		if(sum + i > m)  
			return;

		//和小于m,继续递归
		if(sum + i <= m) 
		{
			a[c++] = i;
			work(sum + i, i + 1);
			--c;   //回溯控制
		}
	}
}

int main()
{
	work(0, 1); //初始条件(和初始值为0,数列起始值为1)
	
	return 0;
}

22、有4张红色的牌和4张蓝色的牌,主持人先拿任意两张,再分别在A、B、C三人额头上贴任意两张牌,A、B、C三人都可以看见其余两人额头上的牌,看完后让他们猜自己额头上是什么颜色的牌,A说不知道,B说不知道,C说不知道,然后A说知道了。请教如何推理,A是怎么知道的。如果用程序,又怎么实现呢?
23、用最简单, 最快速的方法计算出下面这个圆形是否和正方形相交。"
3D坐标系 原点(0.0,0.0,0.0)
圆形:
半径r = 3.0
圆心o = (*.*, 0.0, *.*)

正方形:
4个角坐标;
1:(*.*, 0.0, *.*)
2:(*.*, 0.0, *.*)
3:(*.*, 0.0, *.*)
4:(*.*, 0.0, *.*)

24、链表操作,(1)单链表就地逆置(2)合并链表

    见:http://blog.csdn.net/dazhong159/article/details/7796344

25、写一个函数,它的原形是int continumax(char *outputstr,char *intputstr)

功能:在字符串中找出连续最长的数字串,并把这个串的长度返回,并把这个最长数字串付给其中一个函数参数outputstr所指内存。
例如:"abcd12345ed125ss123456789"的首地址传给intputstr后,函数将返回9,outputstr所指的值为123456789

int continumax(char *outputstr, char *inputstr) 
{
	int len = 0,max=0;    //len更新存储数字字串长度,max存储最长数字字串
	char *pstart = NULL;
	while (*inputstr!='\0')
	{
		if (*inputstr >= '0' && *inputstr <='9') 
		{
			len ++;
		} 
		else 
		{
			if (len > max)  //跟新最大长度和字符串起始位置
			{
				max=len;
				pstart = inputstr-len;
			}
			len = 0;
		}
		inputstr++;
	}
	
	if (len > max) //当一个字符串末尾字符是数字时
	{
		max=len;
		pstart = inputstr-len;
	}

	for (int i=0; i<max; i++)
		*outputstr++ = *pstart++;
	*outputstr = '\0';
	
	return max;
}

26、左旋转字符串:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。
      利用数学中的矩阵转置规律。</P>
      见:
http://blog.csdn.net/dazhong159/article/details/7768548

27、一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。求总共有多少总跳法,并分析算法的时间复杂度。</P>
       假设函数f(n)表示台阶为n级的总跳数,由于最后一跳只可能有两种情形(跳1级和跳2级),所有f(n) = f(n-1) + f(n-2)。而f(1)=1,f(2)=2,令f(0)=1,则求解问题类似fib数列。
28、输入一个整数,求该整数的二进制表达中有多少个1。例如输入10,由于其二进制表示为1010,有两个1,因此输出2。
       xxxxxx10000 & (xxxxxx10000-1) = xxxxxx00000</P>
       见:
http://blog.csdn.net/dazhong159/article/details/7801060
29、输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。
比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。因为可以有如下的push和pop序列:push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,这样得到的pop序列就是4、5、3、2、1。但序列4、3、5、1、2就不可能是push序列1、2、3、4、5的pop序列。

#include "stdafx.h"
#include "stdlib.h"
#include <iostream>
#include <string>
#include <stack>
using namespace std;

int IndexOfPushArray(int *pushArray,int start,int n,int val)
{
	for (int i=start;i<n;i++)
	{
		if (pushArray[i]==val)
			return i;
	}

	return -1;
}

bool IsPossiblePop(int *pushArray,int *popArray,int n)
{
	//index1记录popArray[i]在pushArray中的位置,index2记录popArray[i+1]在pushArray中的位置
	int i,j,index1=-1,index2; 
	stack<int> s; //辅助栈
	for (i=0;i<n;i++)
	{	
		//s不为空
		if (!s.empty())
		{
			int m=s.top();
			//栈顶元素等于popArray[i],之间pop
			if (s.top()==popArray[i])
			{
				s.pop();
				continue;
			}
			//栈顶元素不等于popArray[i],继续在pushArray中查找
			else
			{
				index1++;
				index2=IndexOfPushArray(pushArray,index1,n,popArray[i]);
				if (index2!=-1)
				{
					for (j=index1;j<=index2;j++)
					{
						s.push(pushArray[j]);
					}
					s.pop();
					index1=index2;
				}
				else
					return false;
			}
		}
		//s为空时,在pushArray中查找
		else
		{
			index1++;
			index2=IndexOfPushArray(pushArray,index1,n,popArray[i]);
			if (index2!=-1)
			{
				for (j=index1;j<=index2;j++)
				{
					s.push(pushArray[j]);
				}
				s.pop();
				index1=index2;
			}
		}
	}

	return true;
}

void main()
{
	int a[]={1,2,3,4,5};
	int b1[]={1,3,2,4,5};
	int b2[]={1,3,4,2,5};
	int b3[]={1,3,5,4,2};
	int b4[]={1,4,2,3,5};
	int b5[]={1,5,2,4,3};

	cout<<IsPossiblePop(a,b1,5)<<endl; //1
	cout<<IsPossiblePop(a,b2,5)<<endl; //1
	cout<<IsPossiblePop(a,b3,5)<<endl; //1
	cout<<IsPossiblePop(a,b4,5)<<endl; //0
	cout<<IsPossiblePop(a,b5,5)<<endl; //0
}

30、输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。

      用一个稍微大一点的数字21345作为例子来分析。我们把从1到21345的所有数字分成两段,即1-1345和1346-21345。
先来看1346-21345中1出现的次数。1的出现分为两种情况:一种情况是1出现在最高位(万位)。从1到21345的数字中,1出现在10000-19999这10000个数字的万位中,一共出现了10000(104)次;另外一种情况是1出现在除了最高位之外的其他位中。例子中1346-21345,这20000个数字中后面四位中1出现的次数是2000次(2*103,其中2的第一位的数值,103是因为数字的后四位数字其中一位为1,其余的三位数字可以在0到9这10个数字任意选择,由排列组合可以得出总次数是2*103)。
至于从1到1345的所有数字中1出现的次数,我们就可以用递归地求得了。这也是我们为什么要把1-21345分为1-1235和1346-21345两段的原因。因为把21345的最高位去掉就得到1345,便于我们采用递归的思路。
        分析到这里还有一种特殊情况需要注意:前面我们举例子是最高位是一个比1大的数字,此时最高位1出现的次数104(对五位数而言)。但如果最高位是1呢?比如输入12345,从10000到12345这些数字中,1在万位出现的次数就不是104次,而是2346次了,也就是除去最高位数字之后剩下的数字再加上1。
基于前面的分析,我们可以写出以下的代码。在参考代码中,为了编程方便,我把数字转换成字符串了。

#include "stdafx.h"
#include "stdlib.h"
#include <iostream>
#include <string>
using namespace std;

// 计算10^n 
int PowerBase10(unsigned int n) 
{ 
	int result = 1;
	for(unsigned int i = 0; i < n; ++ i) 
		result *= 10; 
	return result; 
}

// Find the number of 1 in an integer with radix 10 
int NumberOf1(const char* strN) 
{ 
	//递归终止条件1
	if(!strN || *strN < '0' || *strN > '9' || *strN == '\0')
		return 0; 
	int firstDigit = *strN - '0'; 
	unsigned int length = static_cast<unsigned int>(strlen(strN)); 

	// 递归终止条件2 
	if(length == 1 && firstDigit == 0) 
	    return 0; 
	if(length == 1 && firstDigit > 0) 
		return 1;

	// suppose the integer is 21345 (12345) 
	// numFirstDigit is the number of the first digit 
	int numFirstDigit = 0;
	if(firstDigit > 1) 
		numFirstDigit = PowerBase10(length - 1); 
	else if(firstDigit == 1) 
		numFirstDigit = atoi(strN + 1) + 1; 

	// numOtherDigits is the number of 1 01346-21345 due to all digits 
	int numOtherDigits = firstDigit * (length - 1) * PowerBase10(length - 2); 

	// numRecursive is the number of 1 of integer 1345 
	int numRecursive = NumberOf1(strN + 1); 
	
	return numFirstDigit + numOtherDigits + numRecursive; 
} 

// Find the number of 1 in an integer with radix 10 
int NumberOf1BeforeBetween1AndN_Solution2(int n) 
{ 
	if(n <= 0) 
		return 0; 
	char strN[50]; 
	sprintf(strN, "%d", n); 
	
	return NumberOf1(strN); 
}
void main()
{
	cout<<NumberOf1BeforeBetween1AndN_Solution2(21345)<<endl;
}


文章转载于:http://blog.csdn.net/v_JULY_v/article/details/5968678

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值