面试小算法___1

来源:互联网 发布:软件开发测试流程 编辑:程序博客网 时间:2024/05/22 06:57


1.一个父类写了一个virtual 函数,如果子类覆盖它的函数不加virtual ,也能实现多态?

在子类的空间里,有没有父类的这个函数,或者父类的私有变量? (华为笔试题)

答案:只要基类在定义成员函数时已经声明了virtue关键字,在派生类实现的时候覆盖该函数时,virtue关键字可加可不加,不影响多态的实现。子类的空间里有父类的所有变量(static除外)


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

2   //判断两个整数是否互为质数

bool isPrimeNumber(int a, int b)
{
      int temp;
      while(b!=0)
      {
           temp=b;
           b=a%b;
           a=temp;
       }

       if(a==1)
            return true;
       else
            return false;

}


3.完成字符串拷贝可以使用 sprintf、strcpy 及 memcpy 函数,请问这些函数有什么区别

,你喜欢使用哪个,为什么?

答案:这些函数的区别在于实现功能以及操作对象不同。
1.strcpy
函数操作的对象是字符串,完成源字符串目的字符串拷贝功能。
2.snprintf
函数操作的对象不限于字符串:虽然目的对象是字符串,但是源对象可以是字符串、也可以是任意基本类型的数据。这个函数主要用来实现(字符串或基本数据类型)向字符串的转换功能。如果源对象是字符串,并且指定 %s 格式符,也可实现字符串拷贝功能。
3.memcpy
函数顾名思义就是内存拷贝,实现将一个内存块的内容复制到另一个内存块这一功能。内存块由其首地址以及长度确定。程序中出现的实体对象,不论是什么类型,其最终表现就是在内存中占据一席之地(一个内存区间或块)。因此,memcpy的操作对象不局限于某一类数据类型,或者说可适用于任意数据类型,只要能给出对象的起始地址和内存长度信息、并且对象具有可操作性即可。鉴于 memcpy函数等长拷贝的特点以及数据类型代表的物理意义,memcpy函数通常限于同种类型数据或对象之间的拷贝,其中当然也包括字符串拷贝以及基本数据类型的拷贝。

对于字符串拷贝来说,用上述三个函数都可以实现,但是其实现的效率和使用的方便程度不同:

·         strcpy 无疑是最合适的选择:效率高且调用方便

·         snprintf 要额外指定格式符并且进行格式转化,麻烦且效率不高

·         memcpy 虽然高效,但是需要额外提供拷贝的内存长度这一参数,易错且使用不便;并且如果长度指定过大的话(最优长度是源字符串长度 + 1),还会带来性能的下降。其实 strcpy 函数一般是在内部调用 memcpy函数或者用汇编直接实现的,以达到高效的目的。因此,使用 memcpy strcpy拷贝字符串在性能上应该没有什么大的差别。

对于非字符串类型的数据的复制来说,strcpy snprintf一般就无能为力了,可是对 memcpy却没有什么影响。但是,对于基本数据类型来说,尽管可以用 memcpy进行拷贝,由于有赋值运算符可以方便且高效地进行同种或兼容类型的数据之间的拷贝,所以这种情况下 memcpy几乎不被使用。memcpy的长处是用来实现(通常是内部实现居多)对结构或者数组的拷贝,其目的是或者高效,或者使用方便,甚或两者兼有。


4 .变量的声明和定义有什么区别?

5.写一个程序,把一个100以内的自然数分解因数。(自然数分解因数就是将一个自然数

分解为几个素数的乘积,提示,由于该数不是很大,所以可以将质数保存在数组中,以加快计

算速度)


6. Consider the following code:

#include <stdio.h>

#include <string.h>

int main(int argc, char *argv[]) {

int i = 1;

char buf[4];

strcpy(buf, "AAAA");

printf("%d/n", i);

return 0;

}

a) When compiled and executed on x86, why does this program usually not output what the programmer intended?     在x86上为什么不能得到预期结果

b) Name several ways in which the security problem that causes this program not to output what the programmer intended can be prevented WITHOUT changing the code.


参考:第一个问题:  
  32
位情况
:  
  x86
,栈方向向上生长.main的栈中,先分配i空间(4byte),然后分配4个字节的buf(地址在i的上面,i).strcpy越界,0buf开始的第4(0开始)个字节覆盖掉了.x86LSB排列顺序,所以真好覆盖了i的内个数字1.所以显示出数字
0.  
  16
位情况同样分析即可.  

2问?


7  注意指针的地址大小,我们看的书上都是从高位开始描述,但是在实际的栈里,分配是从低位开始的。

     由低位自增向高位

    

int main(void)
{
    int i = 1;
    printf("%d\n", i);

    char* pInt = (char*)&i;
    pInt[0] = '\0';
    printf("%d\n", i);
}
1
0


int main(void)
{
    int i = 1;
    printf("%d\n", i);

    char* pInt = (char*)&i;
    pInt[3] = '\0';
    printf("%d\n", i);
}
1
1


8  char **p, a[16][8];  问:p=a是否会导致程序在以后出现问题?为什么?

参考: 这个不会导致出现问题,但是要注意p的使用,如a[1][2]等价的为 *(*(p+1)+2)而不是*(p+11),

会的,这样会出现编译错误  正确的是:char a[5][5]; char (*p)[5]; p=a;

9 应用程序在运行时的内存包括代码区和数据区,其中数据区又包括哪些部分?

参考:对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。

动态数据区一般就是堆栈。栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的

全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。

程序通过堆栈的基地址和偏移量来访问本地变量。


10. 100位以上的超大整数的加法(主要考虑数据结构和加法的实现)


11 Int (*(*F)(int,int))(int)
(1). float(**def)[10];  def是一个二级指针,它指向的是一个一维数组的指针,数组的元素都是float
(2). double*(*gh)[10];  gh是一个指针,它指向一个一维数组,数组元素都是double*
(3). double(*f[10])();  f是一个数组,f有十个元素,元素都是函数的指针,指向的函数类型是没有参数且返回double的函数
(4). int*((*b)[10]);    就跟"int* (*b)[10]"是一样的,b是一维数组的指针
(5). Long (* fun)(int)  函数指针
(6).Int (*(*F)(int,int))(int)  F是一个函数的指针,指向的函数的类型是有两个int参数并且返回一个函数指针的函数, 返回的函数指针指向有一个int参数且返回int的函数



11  中缀表达式 A-(B+C/D)*E的后缀形式是什么?   ABCD/+E*

人工实现转换  这里我给出一个中缀表达式:a+b*c-(d+e)   第一步:按照运算符的优先级对所有的运算单位加括号:式子变成了:((a+(b*c))-(d+e))   第二步:转换前缀与后缀表达式   前缀:把运算符号移动到对应的括号前面   则变成了:-( +(a *(bc)) +(de))   把括号去掉:-+a*bc+de 前缀式子出现   后缀:把运算符号移动到对应的括号后面   则变成了:((a(bc)* )+ (de)+ )-   把括号去掉:abc*+de+- 后缀式子出现


12

class X

{

public:

X();

virtual ~X();

void myMemberFunc();

static void myStaticFunc();

virtual void myVirtualFunc();

private:

int i;

char * pstr;

char a;

}

sizeof(X) = ?


sizeof(X) = 16  因为定义了虚函数,所以有隐藏的 vptr 指针

4 + 4 + 4(1) + 4 = 16.


13 

写一算法检测单向链表中是否存在环(whether there is a loop in a link list),

要求算法复杂度(Algorithm's complexity是O(n)) 并只使用常数空间(space is O(c)).

注意,你只知道一个指向单向链表头的指针。链表的长度是不定的,而且环出现的地方也

是不定的,环有可能在头,有可能在中间。而且要求是检测,不能破坏环的结构.(MOTO)

答:用两个指针来遍历这个单向链表,第一个指针p1,每次走一步;第二个指针p2,每次走两步; p2指针追上 p1的时候,就表明链表当中有环路了。

int testLinkRing(Link *head)
{
  Link *t1=head,*t2=head;
  while( t1->next && t2->next)
   {
       t1 = t1->;next;
            if (NULL == (t2 = t2->next->next))
               return 0;   //
无环

            if (t1 == t2)
               return 1;
     }
  return 0;
}

如果要定位环路在链表当中的开始点

发现p2p1重合,确定了单向链表有环路了。接下来,让p2回到链表的头部,重新走,P1也继续走,每次步长都走1,那么当p1p2再次相遇的时候,就是环路的入口了。


14 

(1) 解二次方程:a*x*x+b*x+c

无符号整数乘法,乘数为32bit,结果为64bit

int Quadratic( double a,double b,double c,double& x1,double& x2);

返回值:解的个数

(2) 最大公约数

DWORD Divisor( DWORD dwFirst, DWORD dwSecond );

返回值:最大公约数

(3) 根据蒙特卡洛算法计算圆周率

double PI( DOWRD dwCount/*测试次数*/ );

返回值:PI

(4) 提示:32bit整数分解为16bit相乘

void Multiply( DWORD dwFirst, DWORD dwSecond, DWORD& dwHigh, DWORD& dwLower );


15  改错并说明原因

file: 1.c

int a[10]={0};

file: 2.c

int main ()

{ extern int *a;

  printf ("%d/n", a[0]);

  return 0;


16 写一个函数,将其中的/t都转换成4个空格。
#include <iostream>
#define LEN 100
using namespace std;
void TabtoSpace(char * str,int len)

{
    int i=0,j;
    while(str[i]!='\0')

    {
    if (str[i]=='\t')
    {
        j=len+3;
        while (j>i+3)
        {
            str[j]=str[j-3];
            j--;
        }
        len+=3;
        str[i]='*';
        str[i+1]='*';
        str[i+2]='*';
        str[i+3]='*';
    }
        i++;
    }
}

void main(){
    char p[LEN]="ab\tdc\tdfasld\t";
    int plen=strlen(p);
    TabtoSpace(p,plen);
    cout<<p<<endl;
    system("pause");
}


17  Windows程序的入口是哪里?写出Windows消息机制的流程

入口在.main()/WinMain()

Windows应用程序消息处理机制:

A.操作系统接收应用程序的窗口消息,将消息投递到该应用程序的消息队列中

B.应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息,取出消息后,应用程序可以对消息进行一些预处理。

C.应用程序调用DispatchMessage,将消息回传给操作系统。

D.系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消

息进行处理。


18 如何定义和实现一个类的成员函数为回调函数?


 class Test
 {
   public:

       static void callBackFun(void){}; //因为callBackFun默认有一个const Test* 的指针
 };

 typedef void (*FPtr)(void);

 void Fun(FPtr ptr)
 {
     ptr();
 }

 void main(void)
 {
     Fun(Test::callBackFun);
 }


19  某个程序在一个嵌入式系统(200M的CPU,50M的SDRAM)中已经最化了,换到另一个系统(

300M的CPU,50M的SDRAM)中运行,还需要优化吗?


20  8086是多少尉的系统?在数据总线上是怎么实现的


21 多态。overload 和 override的区别。

overload是重载,重载是一种参数多态机制,即代码通过参数的类型或个数不同而实现的多态机制。是一种静态的绑定机制(在编译时已经知道具体执行的是哪个代码段)。  
 override是覆盖。覆盖是一种动态绑定的多态机制。即在父类和子类中同名元素(如成员函数)有不同的实现代码。执行的是哪个代码是根据运行时实际情况而定的。


22 重载Overload特点

1 方法名必须相同 

2  参数列表必须不相同

3 返回值类型可以不相同

注意:override存在于继继承的关系类中。

覆盖Override特点(三相同)有的时候虚函数是要用到返回值的.


23  “new”in c++ is a:

A. library function like malloc in c B. key word  C. operator  D. none of the above malloc是库函数,不在编译器控制范围之内;new是运算符,在编译器控制范围之内。   调用malloc时,从堆中申请内存;调用new时,从堆中申请内存并为内存调用构造函数。

24 非C++内建型别 A 和 B,在哪几种情况下B能隐式转化为A?[C++中等]

a. class B : public A { ……} // B公有继承自A,可以是间接继承的

b. class B { operator A( ); } // B实现了隐式转化为A的转化

c. class A { A( const B& ); } // A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数


25

void testsize(char str[])
{
    std::cout<<"testsize "<<sizeof(str)<<std::endl;
}

void main(void)
{
   char str[] = "Ch";
   std::cout<<"str[]  "<<sizeof(str)<<std::endl;

   testsize(str);
}

str[]<包括\0>

testsize 4;

函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。


26

20 以下代码有什么问题?[C难]

void char2Hex( char c ) // 将字符以16进制表示{

char ch = c/0x10 + '0'; if( ch > '9' ) ch += ('A'-'9'-1);

char cl = c%0x10 + '0'; if( cl > '9' ) cl += ('A'-'9'-1);

cout << ch << cl << ' ';}

char str[] = "I love 中国";

for( size_t i=0; i<strlen(str); ++i )

    char2Hex( str[i] );

cout << endl;


21  

以下代码有什么问题?[C++易]

cout << (true?1:"1") << endl;

答:三元表达式“?:”问号后面的两个操作数必须为同一类型。


22

以下代码能够编译通过吗,为什么?[C++易]

unsigned int const size1 = 2;

char str1[ size1 ];

unsigned int temp = 0;

cin >> temp;

unsigned int const size2 = temp;

char str2[ size2 ];

答:str2定义出错,size2非编译器期间常量,而数组定义要求长度必须为编译期常量


23

struct CLS
{
    int m_i;
    CLS(int i):m_i(i) {}
    CLS()
    {
        CLS(0);
    }
};

void main(void)
{
    CLS obj;
    std::cout <<obj.m_i<<std::endl;
}

答:不能。在默认构造函数内部再调用带参的构造函数属用户行为而非编译器行为,亦即仅执行函数调用,而不会执行其后的初始化表达式。只有在生成对象时,初始化表达式才会随相应的构造函数一起调用。


24  C++中的空类,默认产生哪些类成员函数?[C++易]

答:

class Empty

{

public:

    Empty();                          // 缺省构造函数

    Empty( const Empty& );            // 拷贝构造函数

    ~Empty();                         // 析构函数

    Empty& operator=( const Empty& ); // 赋值运算符

    Empty* operator&();               // 取址运算符

    const Empty* operator&() const;   // 取址运算符 const

};


25
0                     //同上
0
1

0x0012FF74,0x0012FF70,0x0012FF6C,0x0012FF68  //地址的输出
0x0012FF64,0x0012FF60
0                    //等式的判断
0
1

附录:
(int&)a:将a的引用强制转换为整型,意思是a所在的内存,本来定义的时候为float类型,并初始为1.0f,
 但现在我要按int类型解释这段内存(也就是说a所在的内存地址中的数据本来是按float型存储表示的,你非要按int型来解释不可)。
 1.0f   在内存中的存储为
 0   011   1111   1   000   0000   0000   0000   0000   0000.
 把他按整型数解释为2^29+2^28+2^27+2^26+2^25+2^24+2^23=1065353216

 (int&)a 相当于
 *(int*)&a
 *(int*)(&a)
 *((int*)&a)


26

vector array;

array.push_back( 1 );

array.push_back( 2 );

array.push_back( 3 );

for( vector::size_type i=array.size()-1; i>=0; --i ) // 反向遍历array数组

{

    cout << array[i] << endl;

}

答:首先数组定义有误,应加上类型参数:vector<int> array。其次vector::size_type被定义为unsigned int,即无符号数,这样做为循环变量的i0时再减1就会变成最大的整数,导致循环失去控制

 

27  写一个函数,完成内存之间的拷贝。[考虑问题是否全面]

答:

void* mymemcpy( void *dest, const void *src, size_t count )

{

    char* pdest = static_cast<char*>( dest );

    const char* psrc = static_cast<const char*>( src );

    if( pdest>psrc && pdest<psrc+cout ) 能考虑到这种情况就行了

    {

        for( size_t i=count-1; i!=-1; --i )

                pdest[i] = psrc[i];

    }

    else

    {

        for( size_t i=0; i<count; ++i )

            pdest[i] = psrc[i];

    }

    return dest;

}

int main( void )

{

    char str[] = "0123456789";

    mymemcpy( str+1, str+0, 9 );

    cout << str << endl;

 

    system( "Pause" );

    return 0;

}


28   C++程序下列说法正确的有:

A,对调用的虚函数和模板类都进行迟后编译.

B,基类与子类中函数如果要构成虚函数,除了要求在基类中用virtual 声名,而且必须名

字相同且参数类型相同返回类型相同

C,重载的类成员函数都必须要:或者返回类型不同,或者参数数目不同,或者参数序列的类

型不同.

D,静态成员函数和内联函数不能是虚函数,友员函数和构造函数也不能是虚函数,但是析

构函数可以是虚函数.

答:A


29   巧排数字,将1,2,...,19,20这20个数字排成一排,使得相邻的两个数字之和为一个素数,且
首尾两数字之和也为一个素数。编程打印出所有的排法。 

我的总体思想就是根据所有20个数的和建20X20的表,表中和为非素数的项置零,整个问题的求解就归结为递归遍历所有表项,在20列中查找出所有非零值的项数的排列组合,初始将表中所有非零项入栈,并将21,21这个值压入栈中做为每一项的分隔符,栈的大小可估算为:初始表项+(每列表项+1)X20.

      只打2开头的一种排列..打出了10339131种排列,大约用时3分钟,有耐心的兄弟试下,初步估计,打印出全集要跑4-5小时左右.(表中有一百项),针对我的算法还可以进一步优化,但空间不大,有时间再想一想,目前主要感觉在递归比较上浪费了好多时间.


30  总结一下,用回溯的方法解决8皇后问题的步骤为:

1)从第一列开始,为皇后找到安全位置,然后跳到下一列

2)如果在第n列出现死胡同,如果该列为第一列,棋局失败,否则后退到上一列,在进行回溯

3)如果在第8列上找到了安全位置,则棋局成功。

8个皇后都找到了安全位置代表棋局的成功,用一个长度为8的整数数组queenList代表成功摆放的8个皇后,数组索引代表棋盘的col向量,而数组的值为棋盘的row向


31  不使用变量实现c语言的strlen函数

int myStrlen(const char *str)   /* 不用中间变量,用递归实现,很容易看懂 */
{
    if ( (str == NULL) || (*str == '\0') ) {
        return 0;
    }
    else {
        return myStrlen(str+1)+1;
    }
}

int myStrlen1(const char *str)  /* 不用中间变量,也是用递归实现,写得更简洁而已 */
{
    assert(str != NULL);
    return *str ? (myStrlen1(++str) + 1) : 0;
}

函数运行过程中不占用内存基本不可能,除非都使用了寄存器。“不使用中间变量”只是说程序员不能显示的申请内存而已,即不能有局部变量或者动态内存申请。


32 不使用变量实现反转字符串


33  一个hash函数,输入随机,现发生冲突,如数据集中在某几条中,问怎样处理hash函数保证高效的访问,怎样实现?


34  C语言中函数参数的入栈顺序
#include <stdio.h>
void foo(int x, int y, int z)
{
        printf("x = %d at [%X]/n", x, &x);
        printf("y = %d at [%X]/n", y, &y);
        printf("z = %d at [%X]/n", z, &z);
}

int main(int argc, char *argv[])
{
        foo(100, 200, 300);
        return 0;
}

运行结果:
x = 100 at [BFE28760]
y = 200 at [BFE28764]
z = 300 at [BFE28768]

C程序栈底为高地址,栈顶为低地址,因此上面的实例可以说明函数参数入栈顺序的确是从右至左的。可到底为什么呢?查了一直些文献得知,参数入栈顺序是和具体编译器实现相关的。比如,Pascal语言中参数就是从左到右入栈的,有些语言中还可以通过修饰符进行指定,如Visual C++。即然两种方式都可以,为什么C语言要选择从右至左呢?

进一步发现,Pascal语言不支持可变长参数,而C语言支持这种特色,正是这个原因使得C语言函数参数入栈顺序为从右至左。具体原因为:C方式参数入栈顺序(从右至左)的好处就是可以动态变化参数个数。通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底。除非知道参数个数,否则是无法通过栈指针的相对位移求得最左边的参数。这样就变成了左边参数的个数不确定,正好和动态参数个数的方向相反。

因此,C语言函数参数采用自右向左的入栈顺序,主要原因是为了支持可变长参数形式。换句话说,如果不支持这个特色,C语言完全和Pascal一样,采用自左向右的参数入栈方式。


35  x^4+a*x^3+x^2+c*x+d最少需要作几次乘法

((((x+a)*x)+1)*x+c)*x+d最少需要三次乘法即可,这是一种有效的加速表达式求解的方法。

36 

让你在100000000个浮点数中找出最大的10000个,要求时间复杂度优。

//本算法使用快排,O(n*lg(n)) 

//最低可以找到线性算法,使用预先区域统计划分!类试于构造Quad Trees! 写起来代码会长些!


37 问哪个袋子里有金子?
 A袋子上的标签是这样写的:B袋子上的话是对的,金子在A袋子。
 B袋子上的标签是这样写的:A袋子上的话是错的,金子在A袋子。


38

CPU在上电后,进入操作系统的main()之前必须做什么工作?

整个系统对开发环境以及各种变量的初始化,包括了变量空间的分配,cpu内部寄存器的初始化,总线的初始化等等,总之,只有等系统初始化完成以后,我们的c语言的main才能被识别和执行下来


39

1 需求分析2 设计(逻辑设计和物理设计)3 编码4 调试5 运行407.makefile文件的作用是什么?

Makefile 的作用是根据配置的情况,构造出需要编译的源文件列表,然后分别编译,

并把目标代码链接到一起,最终形成 Linux 内核二进制文件。

41  什么是进程(Process)和线程(Thread)?有何区别?





























 


























原创粉丝点击