几道经典笔试题目

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:此算法有缺陷,要求保证绝对值数组中无重复,否则无法线性。


你可能感兴趣的:(算法,String,面试,null,System,Exchange)