题目21:求下面函数的返回值,输入x=9999;(微软)
int func(x)
{
int countx = 0;
while(x)
{
countx ++;
x = x&(x-1);
}
return countx;
}
解答:
其实这个程序的意思就是求9999的二进制中有多少个1(别问我为什么知道的,可以查看剑指offer面试题10),知道了这个就很容易求解这个题目了!
9×1024中含有1的个数为2;512中含有1的个数为1;256中含有1的个数为1;15中含有1的个数为4;故共有1的个数为8,结果为8。1000 - 1 = 0111,正好是原数取反。这就是原理。用程序中的这种方法来求1的个数是很效率很高的。不必去一个一个地移位。循环次数最少。
题目22:单向链表的反转是一个经常被问到的一个面试题,也是一个非常基础的问题。比如一个链表是这样的: 1->2->3->4->5 通过反转后成为5->4->3->2->1。
解答:
最容易想到的方法遍历一遍链表,利用一个辅助指针,存储遍历过程中当前指针指向的下一个元素,然后将当前节点元素的指针反转后,利用已经存储的指针往后面继续遍历。源代码如下:
struct linka
{
int data;
linka* next;
};
void reverse(linka*& head)
{
if(head ==NULL)
return;
linka *pre, *cur, *ne;
pre=head;
cur=head->next;
while(cur)
{
ne = cur->next;
cur->next = pre;
pre = cur;
cur = ne;
}
head->next = NULL;
head = pre;
}
还有一种利用递归的方法。这种方法的基本思想是在反转当前节点之前先调用递归函数反转后续节点。源代码如下。不过这个方法有一个缺点,就是在反转后的最后一个结点会形成一个环,所以必须将函数的返回的节点的next域置为NULL。因为要改变head指针,所以我用了引用。算法的源代码如下:
linka* reverse(linka* p,linka*& head)
{
if(p == NULL || p->next == NULL)
{
head=p;
return p;
}
else
{
linka* tmp = reverse(p->next,head);
tmp->next = p;
return p;
}
}
题目23:There are two int variables: a and b, don’t use “if”, “? :”, “switch”or other judgement statements, find out the biggest one of the two numbers.
解答:
( ( a + b ) + abs( a - b ) ) / 2
题目24:如何判断一个单链表是有环的?(注意不能用标志位,最多只能用两个额外指针)
struct node { char val; node* next;}
bool check(const node* head) {} //return false : 无环;true: 有环
解答:
一种O(n)的办法就是搞两个指针,一个每次递增一步,一个每次递增两步,如果有环的话两者必然重合,反之亦然。具体代码如下:
bool check(const node* head)
{
if(head==NULL) return false;
node *low=head, *fast=head->next;
while(fast!=NULL && fast->next!=NULL)
{
low=low->next;
fast=fast->next->next;
if(low==fast) return true;
}
return false;
}
题目25:链表题:一个链表的结点结构:
struct Node
{
int data ;
Node *next ;
};
typedef struct Node Node ;
(1)已知链表的头结点head,写一个函数把这个链表逆序 (Intel公司)
(2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序。(保留所有结点,即便大小相同)
(3)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序,这次要求用递归方法进行。 (Autodesk公司)
解答:
链表是数据结构中比较重要的一块,很多公司都喜欢在这一块出题目。具体的解答代码如下:
(1)链表逆序代码:
Node * ReverseList(Node *head) //链表逆序
{
if ( head == NULL || head->next == NULL )
return head;
Node *p1 = head ;
Node *p2 = p1->next ;
Node *p3 = p2->next ;
p1->next = NULL ;
while ( p3 != NULL )
{
p2->next = p1 ;
p1 = p2 ;
p2 = p3 ;
p3 = p3->next ;
}
p2->next = p1 ;
head = p2 ;
return head ;
}
(2)合并有序链表代码:
Node * Merge(Node *head1 , Node *head2)
{
if ( head1 == NULL)
return head2 ;
if ( head2 == NULL)
return head1 ;
Node *head = NULL ;
Node *p1 = NULL;
Node *p2 = NULL;
if ( head1->data < head2->data )
{
head = head1 ;
p1 = head1->next;
p2 = head2 ;
}
else
{
head = head2 ;
p2 = head2->next ;
p1 = head1 ;
}
Node *pcurrent = head ;
while ( p1 != NULL && p2 != NULL)
{
if ( p1->data <= p2->data )
{
pcurrent->next = p1 ;
pcurrent = p1 ;
p1 = p1->next ;
}
else
{
pcurrent->next = p2 ;
pcurrent = p2 ;
p2 = p2->next ;
}
}
if ( p1 != NULL )
pcurrent->next = p1 ;
if ( p2 != NULL )
pcurrent->next = p2 ;
return head ;
}
(3)递归代码:
Node * MergeRecursive(Node *head1 , Node *head2)
{
if ( head1 == NULL )
return head2 ;
if ( head2 == NULL)
return head1 ;
Node *head = NULL ;
if ( head1->data < head2->data )
{
head = head1 ;
head->next = MergeRecursive(head1->next,head2);
}
else
{
head = head2 ;
head->next = MergeRecursive(head1,head2->next);
}
return head ;
}
题目26:面向对象的三个基本特征,并简单叙述之?
解答:
1. 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private,protected,public)
2. 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。
3. 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
题目27:类成员函数的重载、覆盖(重写)之间的区别?
解答:
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
b.覆盖(重写)是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
题目28:下列代码正确吗?如果错误请指出来:
char *p = “world”;
p[0] = 'X’;
解答:
*p是指向的是常量字符串,常量字符串是存放在文字常量区的,里面的内容是不能被修改的,而题目中p[0]='X',明显修改了字符串的第一个字符,所以出现了未定义行为错误!
题目29:C++的空类默认会产生哪些类成员函数?
解答:
class Empty
{
public:
Empty();//缺省构造函数
Empty(const Empty &);//拷贝构造函数
~Empty();//析构函数
Empty &operator=(const Empty &);//赋值运算符
Empty *operator&();//取址运算符
const Empty *operator& () const;//取址运算符const
}
题目30:C++中的malloc/free和new/delete之间的联系与区别?
解答:
联系:都是在堆(heap)上进行动态的内存操作。
区别:1、malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符。
2、new能够自动分配空间的大小,用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。
3、对于用户自定义的对象而言,使用malloc/free无法满足动态管理对象和内存的要求。