您的位置:首页 > 职场人生

几道经典笔试题目

2011-09-21 13:03 344 查看
1.递归合并有序链表

2.寻找二叉树中两个节点的最近的公共祖先

3.进制算法转换

4.大小写转换

5.求两同长数组的中位数问题

6.求数组的第k大的数字

7. 最接近S的中位数的k个数

1.递归合并有序链表

Node* mergAction(Node* head1,Node *head2)
{
Node *p=NULL;
if(head1==NULL&&head2==NULL)
return p;
else if(head1==NULL)
return head2;
else if(head2==NULL)
return head1;
else
{
if(head1->data < head2->data)
{
p = head1;
p->next = mergAction(head1->next,head2);
}
else
{
p = head2;
p->next = mergAction(head1,head2->next);
}
return p;
}
}


2.寻找二叉树中两个节点的最近的公共祖先

class Node
{
Node * left;
Node * right;
Node * parent;
};
/*查找p,q的最近公共祖先并将其返回。*/
Node * NearestCommonAncestor(Node * p,Node * q);

Node * NearestCommonAncestor(Node * root,Node * p,Node * q)
{
Node * temp;
while(p!=NULL)
{
p=p->parent;
temp=q;
while(temp!=NULL)
{
if(p==temp->parent)
return p;
temp=temp->parent;
}
}
}

解法2:算法思想:如果一个节点的左子树包含p,q中的一个节点,右子树包含另一个,则这个节点就是p,q的最近公共祖先。

/*查找a,b的最近公共祖先,root为根节点,out为最近公共祖先的指针地址*/
int FindNCA(Node* root, Node* a, Node* b, Node** out)
{
if( root == null )
{
return 0;
}

if( root == a || root == b )
{
return 1;
}

int iLeft = FindNCA(root->left, a, b, out);
if( iLeft == 2 )
{
return 2;
}

int iRight = FindNCA(root->right, a, b, out);
if( iRight == 2 )
{
return 2;
}

if( iLeft + iRight == 2 )
{
*out = root;
}
return iLeft + iRight;
}

void main()
{
Node* root = ...;
Node* a = ...;
Node* b = ...;
Node* out = null;
int i = FindNCA(root, a, b, &out);
if( i == 2 )
{
printf("Result pointer is %p", out);
}
else
{
printf("Not find pointer");
}
}


3.进制算法转换

编程实现将十进制的整数转化成任意进制的整数,用户输入

一个十进制数R和想要转化的进制数X,程序输出转换后的X进制的整数。

算法思想:将十进制数R与进制X取模,即R%X的值作为X进制整数的倒数第一位,

然后使R等于R/X,再取R%X的值作为X进制整数的倒数第二位......依次类推,直

到最后R/X=0为止。


#include <iostream>
#include <string>
using namespace std;
 
/*将一个整型数字转化成字符型数字,例如 8->'8',12->'c'*/
void numToChar(char &num)
{

 /*num是0到9之间的数字*/
 if(num<=9&&num>=0)
 {
  num+=48;
 }
 
 /*num是10到15之间的数字*/
 else
 {
  switch(num)
  {
  case 10:num='A';
  case 11:num='B';
  case 12:num='C';
  case 13:num='D';
  case 14:num='E';
  case 15:num='F';
  }
 }
}
 
/*进制转化函数——r表示需要被转化的十进制数,x表示进制(1<x<17)*/
void decimalTransmit(int r,int x)
{
 string result;//保存x进制数
 char temp;
 while(r>0)
 {
  temp=r%x;
        numToChar(temp);
  result+=temp;
  r=r/x;
 }
 
 /*输出转化后的x进制整数*/
 for(int i=result.size()-1;i>=0;i--)
  cout<<result[i];
 cout<<endl;
}
 
int main()
{
 int R,X;
 cout<<"请输入一个十进制数和要转化的进制(用空格作为间隔符):";
 cin>>R>>X;
 decimalTransmit(R,X);
 return 0;
}

还有一个mton的算法,很漂亮,但是遗憾的是只支持1~10进制的转换。

void m2n(int m, char* mNum, int n, char* nNum)
{
int i = 0;
char c, *p = nNum;

//这是一个考察地方,是否能用最少乘法次数。
while (*mNum != '\0')
i = i*m + *mNum++ - '0';

//辗转取余
while (i) {
*p++ = i % n + '0';
i /= n;
}
*p-- = '\0';

//逆置余数序列
while (p > nNum) {
c = *p;
*p-- = *nNum;
*nNum++ = c;
}
}


4.大小写转换

#define to_uppercase(ch) ((ch) - 'a' + 'A')
#define to_lowercase(ch) ((ch) - 'A' + 'a')

以后记不住,记住带入特殊值作检验:

‘a’->‘A’    ‘a’ - ‘a’ + ‘A’

‘A’->‘a’    ‘A’ - 'A'  + 'a'

貌似这么简答我都在考场上想了很久,足见状态之差

5.求两同长数组的中位数问题

中位数问题:设X[0:n-1]和Y[0:n-1]为两个数组,每个数组中含有N个 已经排好序的数。试设计一个O(logn)时间算法,找出X和Y的2N个数的中位数。

     解决问题的核心:找出将大问题分割成较小规模的相同问题的切割点,并递归定义大问题与子问题之间的关系。

     确定切割点:对于两个数组,我们可以从他们中分别选取出一个中位数,称为x,y,并将两个数组的左右边界称之为aLeft,aRight,bLeft,bRight。对比两个中位数,如果X数组中的中位数大于Y数组中的中位数,且X数组中的元素个数为偶数个,则X数组被切割为X[aLeft,x+1],Y被切割为Y[y,bRight],如果X数组的元素个数不为偶数个的话,则直接将X切割为X[aLeft,x]。如果X数组的中位数小于Y数组的中位数,取值情况刚好相反。
     递归关系:根据上面所述,对于原问题X[aLeft , aRight], Y[bLeft, bRight]。假设切割后的子问题为X[aLeft, x+1],Y[y,bRight]。则求解X[aLeft , aRight], Y[bLeft, bRight]问题的中位数,归结于求解子问题X[aLeft, x+1],Y[y,bRight]的中位数。
     递归结束条件:当切割后得到的子问题的两个数组的长度都为2位时,整个递归结束。
实际问题的输入:

第一行: n,为x和y数组的元素个数

第二行: x数组的n个数,用空格分隔

第三行: y数组的n个数,用空格分隔

算法的输出:

中位数两个,用空格分隔
#include <stdio.h>
#include <stdlib.h>
int main()
{
//两个数组
int len = 0;
//获取数组的长度
scanf("%d", &len);
//动态分配数组
int * a;
int * b;
a = (int *)malloc(sizeof(int) * len);
b = (int *)malloc(sizeof(int) * len);
//获取每个数组的元素
for(int i=0; i < len; i++)
{
scanf("%d", &a[i]);
}
for(int j=0; j < len; j++)
{
scanf("%d", &b[j]);
}
//两个数组的左右端点的坐标
int aLeft = 0;
int aRight = len - 1;
int bLeft = 0;
int bRight = len - 1;
//两数组中间坐标
int aMid = 0;
int bMid = 0;
//迭代循环
while(true)
{
//printf("a left is %d right is %d, and b left is %d right is %d.\n", aLeft, aRight, bLeft, bRight);
//  如果两个数组都只剩下两个元素,则中位数一定在其中
if( (aRight - aLeft) == 1 && (bRight - bLeft) == 1)
{
//  输出第一行的最大一个值
printf("%d ", (a[aLeft]>=b[bLeft])?a[aLeft]:b[bLeft]);
//  输出第一行的最小一个值
printf("%d\n", (a[aRight]<=b[bRight])?a[aRight]:b[bRight]);
//  结束循环
break;
}
else
{
//  求解各个数组的中值
aMid = (int)((aLeft + aRight)/2);
bMid = (int)((bLeft + bRight)/2);
//  如果A中值小于B中值
if(a[aMid] < b[bMid])
{
//  如果B中现存的数列是偶数个,右边值加一
if((bLeft + bRight + 1) % 2 == 0) {
aLeft = aMid;
bRight = bMid + 1;
}
else {
aLeft = aMid;
bRight = bMid;
}
}
//  如果B中值小于A中值
else
{
//  如果A中现存的数列是偶数个,右边值加一
if((aLeft + aRight + 1) % 2 == 0) {
aRight = aMid + 1;
bLeft = bMid;
}
else {
aRight = aMid;
bLeft = bMid;
}
}
}
}
return 0;
}


6.求数组的第k大的数字:


      太经典了,经常面试问到。具体参考《算法导论》

  
#include <cstdlib>
#include <iostream>
/**
*求一个数组中的第k大数
*基本思想:
*以最后一个元素x为轴,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:
*1.Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;
*2.Sa中元素的个数大于等于k,则返回Sa中的第k大数。
*时间复杂度近似为O(n)
*/
using namespace std;
void exchange(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
//快排的partition函数
int partition(int *a,int l,int r)
{
int i=l-1,j=l;
int x=a[r];
int temp;

for(j=l;j<r;j++)
{
if(a[j]>=x) //把比x大的数往前放
{
exchange(a[j],a[i+1]);
i++;
}
}
exchange(a[r],a[i+1]);
return i+1;
}
int k_element(int *a,int l,int r, int k)
{
if(l>=r)
return a[l];
int q=partition(a,l,r);
if(q==k-1)
return a[q];
else if(q>=k)
return k_element(a,l,q-1,k); //Sa中元素个数大于等于k
else
return k_element(a,q+1,r,k-(q+1)); //Sa中元素个数小于k
}
int main(int argc, char *argv[])
{
int a[100];
int length;
cin>>length;
for(int i=0;i<length;i++)
cin>>a[i];
cout<<k_element(a,0,length-1,4)<<endl;

system("PAUSE");
return EXIT_SUCCESS;
}

7. 最接近S的中位数的k个数

      问题描述:

      给定由n个互补相同的数组成的集合S以及正整数k<=n,试设计一个O(n)时间算法找出S中最接近S的中位数的k个数。

算法分析:

     首先,通过select算法得到这个数组的中位数,然后最这个集合中的每一个数都减去这个中位数然后取abs();最后在第二步得到的集合中,取第k小的数,再取小于第k小的数的k-1个数,他们原来的数,就是要求的k个数。PS:此算法有缺陷,要求保证绝对值数组中无重复,否则无法线性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息