您的位置:首页 > 理论基础 > 数据结构算法

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

2012-08-30 17:14 513 查看
41、求固晶机的晶元查找程序。晶元盘由数目不详的大小一样的晶元组成,晶元并不一定全布满晶元盘,照相机每次这能匹配一个晶元,如匹配过,则拾取该晶元,若匹配不过,照相机则按测好的晶元间距移到下一个位置。求遍历晶元盘的算法 求思路。(不懂)

42、两个非降序链表的并集,1->2->3 和 2->3->5 并为 1->2->3->5。另外只能输出结果,不能修改两个链表的数据。

思路:只用输出结果,并不需要用链表将元素链接起来。注意链表自身中相同的元素只输出一次,链表1与链表2中相同的元素也只输出一次。

#include <stdio.h>
#include <string>
#include <iostream>
using namespace std;

typedef struct List
{
int data;
List *next;
}*pList;

//合并两个链表
void mergeListOutput(pList first,pList second)
{
pList p1=first->next,p2=second->next;

while(p1!=NULL&&p2!=NULL)  //同时遍历两个链表
{
while(p1!=NULL&&p1->next!=NULL&&p1->data==p1->next->data) //滤去p1中相同的元素
p1=p1->next;
while(p2!=NULL&&p1->next!=NULL&&p2->data==p2->next->data) //滤去p2中相同的元素
p2=p2->next;

if(p1->data<p2->data)   //p1节点元素较小,输出元素,p1后移一位
{
cout<<p1->data<<" ";
p1=p1->next;
}
else if(p2->data<p1->data) //p2节点元素较小,输出元素,p2后移一位
{
cout<<p2->data<<" ";
p2=p2->next;
}
else         //p1、p2节点元素相等,输出该元素,p1、p2均后移一位
{
cout<<p1->data<<" ";
p1=p1->next;
p2=p2->next;
}
}

while(p1!=NULL) //p1剩余的元素
{
while (p1!=NULL&&p1->next!=NULL&&p1->data==p1->next->data)
{
p1=p1->next; //滤去p1中相同的元素
}
cout<<p1->data<<" ";
p1=p1->next;
}
while(p2!=NULL) //p2剩余的元素
{
while (p2!=NULL&&p2->next!=NULL&&p2->data==p2->next->data)
{
p2=p2->next; //滤去p2中相同的元素
}
cout<<p2->data<<" ";
p2=p2->next;
}
cout<<endl<<endl;
}

//用数组创建一个带头节点单链表
List* createList(int *data,int n)
{
pList pHead=new List;
pHead->next=NULL;

pList p=pHead,pNew;
for (int i=0;i<n;i++)
{
pNew=new List;
pNew->data=data[i];
pNew->next=p->next;
p->next=pNew;
p=pNew;
}

return pHead;   //返回头节点
}

void printList(List *l)
{
l=l->next;
while(l!=NULL)
{
cout<<l->data<<" ";
l=l->next;
}
}

int main(int argc, char* argv[])
{
int data[]={1,2,2,3,3,4,4,5,6,7};
int len=sizeof(data)/sizeof(int);
int data1[]={2,2,3,3,4,4,5,5,5,6,7,8,8,9,10};
int len1=sizeof(data1)/sizeof(int);

pList p1=createList(data,len);
pList p2=createList(data1,len1);

cout<<"合并之前,链表p1:";
printList(p1);
cout<<endl;
cout<<"合并之前,链表p2:";
printList(p2);
cout<<endl<<endl;

cout<<"p1、p2合并之后的元素:";
mergeListOutput(p1,p2);

cout<<"合并之后,链表p1:";
printList(p1);
cout<<endl;
cout<<"合并之后,链表p2:";
printList(p2);
cout<<endl;

return 0;
}

43、递归和非递归俩种方法实现二叉树的前序遍历。

见:/article/7799371.html

44、腾讯面试题:

1)设计一个魔方(六面)的程序。

2)有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。请用5分钟时间,找出重复出现最多的前10条。

hash映射+hash统计+最小根堆排序

3)收藏了1万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似)

采样字典树存储和查找?

45、雅虎:

1)对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)某一个元素也加一,现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。

2)一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值。比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1; {3,6}{2,4,3} m=2;{3,3}{2,4}{6} m=3 所以m的最大值为3。

46、搜狐:四对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(())

思路:Suppose k parenthesis has f(k) permutations, k is large enough. Check the first parenthesis, if there are i parenthesis in it then, the number of permutations inside it and out of it are f(i) and f(k-i-1), respectively. That is f(k) = Sum_i=[0,k-1]_(f(i)*f(k-i-1));
f(1)=1,f(2)=2,令f(0)=1.参考(Catalan数——卡特兰数)

47、创新工场:求一个数组的最长递减子序列 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}

48、微软:一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}。是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。

思路:类似于折半查找法

//数组a,起始s,结束t,待查找元素k
int helper(int a[], int k, int s, int t)
{
if (s>t)  //未找到
return -1;

int m =(s+t)/2;
if (a[m] == k)  //找到,返回k所在位置
return m;

else if (a[s] >= k && k > a[m])   //在左半边
return helper(a, k, s, m-1);

else                              //在右半边
return helper(a, k, m+1, t);
}

//数组a,长度n,待查找元素k
int shiftedBinarySearch(int a[], int k, int n)
{
return helper(a, k, 0, n-1);
}

49、如何对n个整数序列进行排序,已知这些数的范围为(0-65535),要求时间复杂度O(n),空间复杂度O(1)。

思路:可以申请一个大小为65536的数组A,数组的x下标代表数字x,A[x]代表x 在整数序列中出现的次数。扫描一遍整数序列就可以完成对该整数序列的排序,时间复杂度为O(n)。因为整数范围已知,申请大小为65536的数组,大小为常量,所以空间复杂度为O(1)。

const int nSize=65536;
int array[nSize];
void rangeSort(int *data,int len)
{
int k=0;
for (int i=0;i<len;i++)  //统计
{
array[data[i]]++;
}

for (i=0;i<nSize;i++)   //排序
{
int j=0;
while(j<array[i])
{
data[k++]=i;
j++;
}
}
}

50、网易有道笔试:

1)求一个二叉树中任意两个节点间的最大距离,两个节点的距离的定义是 这两个节点间边的个数,比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复杂度。

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

51、和为n连续正数序列。题目:输入一个正数n,输出所有和为n连续正数序列。例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。

思路:Suppose n=i+(i+1)+...+(j-1)+j, then n = (i+j)(j-i+1)/2 = (j*j - i*i + i + j)/2

=> j^2 + j + (i-i^2-2n) = 0 => j=sqrt(i^2-i+1/4+2n) - 1/2

//i+ i+1 +...+ j-1 +j=n
//j^2 + j + (i-i^2-2n) = 0
//j=sqrt(i^2-i+1/4+2n) - 1/2
void findConsecutiveSequence(int n)
{
int sum=0;
for (int i=1; i<=n/2; i++)
{
sum=0;
int sqroot = (sqrt(4*i*i+8*n-4*i+1)-1)/2;
for (int j=i;j<=sqroot;j++)
{
sum+=j;
}
if(sum==n)
cout<<i<<"-"<<j-1<<endl;
}
}

52、二元树的深度。输入一棵二元树的根结点,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

见:/article/7799371.html

53、字符串的排列。输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

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

54、输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。要求时间复杂度为O(n)。

void swap(int *a,int i,int j)
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;

}

void partition(int a[], int n)
{
int i=0,j=0;
while (i < n && (a[i] & 1)==0)  //前面的都是偶数
i++;
if (i==n)
return;
swap(a, i++, j++);
while (i<n)
{
if ((a[i] & 1) == 1)    //碰到奇数,与前面的偶数交换位置
swap(a, i, j++);
i++;
}
}

55、类CMyString的声明如下:

class CMyString

{

public:

CMyString(char* pData = NULL);

CMyString(const CMyString& str);

~CMyString(void);

CMyString& operator = (const CMyString& str);

private:

char* m_pData;

};

请实现其赋值运算符的重载函数,要求异常安全,即当对一个对象进行赋值时发生异常,对象的状态不能改变。

见:/article/7799376.html

56、如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串。注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。例如:输入两个字符串BDCABA 和ABCBDAB,字符串BCBA 和BDAB 都是是它们的最长公共子串,则输出它们的长度4,并打印任意一个子串。

57、用俩个栈实现队列。

相信大家对栈和队列的基本性质都非常了解了:栈是一种后入先出的数据容器,因此对队列进行的插入和删除操作都是在栈顶上进行;队列是一种先入先出的数据容器,我们总是把新元素插入到队列的尾部,而从队列的头部删除元素。

//队列类结构
template<class T> class CQueue
{
public:
CQueue() {}
~CQueue() {}
void Enqueue(const T& node); // append a element to tail
T Dequeue(); // remove a element from head
private:
stack<T> m_stack1;
stack<T> m_stack2;
};

//进队
template<class T>void CQueue<T>::Enqueue(const T& node)
{
m_stack1.push(node);  //数据压进m_stack1
}

//出队
template<class T>T CQueue<T>::Dequeue()
{
if (!m_stack2.empty())  //m_stack2不为空,弹出数据
{
T data=m_stack2.top();
m_stack2.pop();
return data;
}

if (m_stack1.empty())
{
throw new exception("stack empty!");
}
while (!m_stack1.empty()) //m_stack2为空,且m_stack1不为空,将m_stack1中数据弹出并压进m_stack2中
{
T data=m_stack1.top();
m_stack1.pop();
m_stack2.push(data);
}

T data=m_stack2.top();   //弹出m_stack2的数据
m_stack2.pop();
return data;
}

int main(int argc, char* argv[])
{
int data[]={13,12,23,3,2,1,1,2,3,46,465,243,4,46,4,4,4,4,46,46,45,45};
int len=sizeof(data)/sizeof(int);

CQueue<int> queue;  //实例化模板类

for (int i=0;i<len;i++)
{
queue.Enqueue(data[i]);  //进队
}

for (i=0;i<len;i++)
{
cout<<queue.Dequeue()<<" ";  //出队
}
cout<<endl;

return 0;
}

58、从尾到头输出链表。输入一个链表的头结点,从尾到头反过来输出每个结点的值。

思路:从头到尾遍历链表,每经过一个结点的时候,把该结点放到一个栈中。当遍历完整个链表后,再从栈顶开始输出结点的值,此时输出的结点的顺序已经反转过来了。该方法需要维护一个额外的栈,实现起来比较麻烦。既然想到了栈来实现这个函数,而递归本质上就是一个栈结构。于是很自然的又想到了用递归来实现。要实现反过来输出链表,我们每访问到一个结点的时候,先递归输出它后面的结点,再输出该结点自身,这样链表的输出结果就反过来了。

struct ListNode
{
int  m_nKey;
ListNode*  m_pNext;
};

void PrintListReversely(ListNode* pListHead)
{
if(pListHead != NULL)
{
// Print the next node first
if (pListHead->m_pNext != NULL)
PrintListReversely(pListHead->m_pNext);

// Print this node
printf("%d", pListHead->m_nKey);
}
}

59、不能被继承的类。用C++设计一个不能被继承的类。

首先想到的是在C++ 中,子类的构造函数会自动调用父类的构造函数。同样,子类的析构函数也会自动调用父类的析构函数。要想一个类不能被继承,我们只要把它的构造函数和析构函数都定义为私有函数。那么当一个类试图从它那继承的时候,必然会由于试图调用构造函数、析构函数而导致编译错误。

class FinalClass1
{
public :
static FinalClass1* GetInstance()
{
return new FinalClass1;
}

static void DeleteInstance( FinalClass1* pInstance)
{
delete pInstance;
pInstance = NULL;
}

private :
FinalClass1() {}
~FinalClass1() {}
};

这个类是不能被继承,但在总觉得它和一般的类有些不一样,使用起来也有点不方便。比如,我们只能得到位于堆上的实例,而得不到位于栈上实例。能不能实现一个和一般类除了不能被继承之外其他用法都一样的类呢?办法总是有的,不过需要一些技巧。请看如下代码:

template<typename T> class MakeFinal
{
friend T;
private :
MakeFinal() {}
~MakeFinal() {}
};

class FinalClass2 : virtual public MakeFinal<FinalClass2>
{
public :
FinalClass2() {}
~FinalClass2() {}
};

这个类使用起来和一般的类没有区别,可以在栈上、也可以在堆上创建实例。尽管类MakeFinal <FinalClass2> 的构造函数和析构函数都是私有的,但由于类
FinalClass2是它的友元函数,因此在FinalClass2 中调用MakeFinal
<FinalClass2> 的构造函数和析构函数都不会造成编译错误。

但当我们试图从 FinalClass2继承一个类并创建它的实例时,却不同通过编译。

class Try : public FinalClass2
{
public :
Try() {}
~Try() {}
};

由于类 FinalClass2是从类MakeFinal <FinalClass2> 虚继承过来的,在调用 Try 的构造函数的时候,会直接跳过 FinalClass2而直接调用MakeFinal
<FinalClass2> 的构造函数。非常遗憾的是, Try 不是MakeFinal <FinalClass2> 的友元,因此不能调用其私有的构造函数。
60、在O(1)时间内删除链表结点。给定链表的头指针和一个结点指针(非尾节点),在O(1)时间删除该结点。链表结点的定义如下:
思路:将pToBeDeleted的数据改为pToBeDeleted->m_pNext的数据。然后删除pToBeDeleted->m_pNext。特殊情况下,pToBeDeleted是尾节点,那么我们必须通过迭代找到它的前身,然后删除该节点。
struct ListNode
{
int  m_nKey;
ListNode*  m_pNext;
};

void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted)
{
pToBeDeleted->m_nKey=pToBeDeleted->m_pNext->m_nKey; //将pToBeDeleted的数据改为pToBeDeleted->m_pNext的数据

ListNode *temp=pToBeDeleted->m_pNext; //删除pToBeDeleted->m_pNext
pToBeDeleted->m_pNext=temp->m_pNext;
delete temp;
temp=NULL;
}


文章转载于:/article/1439407.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐