08 2012 档案
摘要:1 #include <iostream> 2 3 using namespace std; 4 5 class A 6 { 7 protected: 8 int m_data; 9 10 public:11 A(int data = 0)12 {13 m_data = data;14 }15 int GetData()16 {17 return doGetData();18 }19 20 virtual int doGetData()21 {22 return m_d...
阅读全文
摘要:题目:输入一棵二叉树的根结点,判断该树是不是平衡二叉树。如果某二叉树中任意结点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。例如下图中的二叉树就是一棵平衡二叉树: 1 bool IsBalanced(BinaryTreeNode* pRoot) 2 { 3 if(pRoot == NULL) 4 return true; 5 6 int left = TreeDepth(pRoot->m_pLeft); 7 int right = TreeDepth(pRoot->m_pRight); 8 int diff = left - rig...
阅读全文
摘要:写在前面的话:由于与C++语法相关的面试题,通常用很短的篇幅就能解释清楚,不适合写博客,因此本博客一直没有关注C++的语法题。近期发现篇幅短的C++题目刚好合适微博,于是开始在微博http://weibo.com/zhedahht和http://t.163.com/zhedahht上写C++的系列面试题。感兴趣的读者可以关注我的微博,或者直接围观面试题每日一题系列。同时,我也将不定期整理一些经典的C++面试题,发表到本博客上。题目(一):我们可以用static修饰一个类的成员函数,也可以用const修饰类的成员函数(写在函数的最后表示不能修改成员变量,不是指写在前面表示返回值为常量)。请问:能
阅读全文
摘要:题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。例如:如果输入如下矩阵:12345678910111213141516则依次打印出数字1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10。分析:第一次看到这个题目的时候,觉得这个题目很简单,完全不需要用到数据结构或者算法的知识,因此没有兴趣做这道题。后来听到包括Autodesk、EMC在内的多家公司在面试或者笔试里采用过这道题,于是想这么多家公司用它来检验一个程序员的编程功底总是有原因的,于是决定自己写一遍试一下。真正写一遍才发现,要完整写出这道题的代码,还真不是件容易的
阅读全文
摘要:题目:设计一个类,我们只能生成该类的一个实例。分析:只能生成一个实例的类是实现了Singleton模式的类型。由于设计模式在面向对象程序设计中起着举足轻重的作用,在面试过程中很多公司都喜欢问一些与设计模式相关的问题。在常用的模式中,Singleton是唯一一个能够用短短几十行代码完整实现的模式。因此,写一个Singleton的类型是一个很常见的面试题。事实上,要让一个类型是能创建一个实例不是一件很难的事情。我们可以把该类型的构造函数设为private,这样该类型的用户就不能创建该类型的实例了。然后我们在给类型中创建一个静态实例。当用户需要该类型的实例时,我们就返回这个实例。基于这个思路,我们可
阅读全文
摘要:题目:实现函数double Power(double base, int exponent),求base的exponent次方。不需要考虑溢出。分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后30秒写出如下的代码:1 double Power(double base, int exponent)2 {3 double result = 1.0;4 for(int i = 1; i <= exponent; ++i)5 result *= base;6 7 return result;8 }上述代码至少有一个问题:由于输入...
阅读全文
摘要:题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,打印出S的所有可能的值出现的概率。分析:玩过麻将的都知道,骰子一共6个面,每个面上都有一个点数,对应的数字是1到6之间的一个数字。所以,n个骰子的点数和的最小值为n,最大值为6n。因此,一个直观的思路就是定义一个长度为6n-n的数组,和为S的点数出现的次数保存到数组第S-n个元素里。另外,我们还知道n个骰子的所有点数的排列数6^n。一旦我们统计出每一点数出现的次数之后,因此只要把每一点数出现的次数除以n^6,就得到了对应的概率。该思路的关键就是统计每一点数出现的次数。要求出n个骰子的点数和,我们可以先把n个骰子分为两堆:第一堆只
阅读全文
摘要:题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。分析:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素,时间复杂度显然是O(N)。但这个思路没有利用输入数组的特性,我们应该能找到更好的解法。我们注意到旋转之后的数组实际上可以划分为两个排序的子数组,而且前面的子数组的元素都大于或者等于后面子数组的元素。我们还可以注意到最小的元素刚好是这两个子数组的分界线。我们试着用二元查找法的思路在寻找这个最小的元
阅读全文
摘要:大一下学期遇到的问题,现在想起来把他搞明白………………--------------------------------------------------------------------------------------------------------------------------------这个是<stdio.h>中定义的scanf:_CRTIMP int __cdecl __MINGW_NOTHROW scanf (const char*, ...);这是scanf函数的一般形式(百度百科):scanf(格式控制,地址表列)int scanf(char *for
阅读全文
摘要:题目:从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。分析:这题目很有意思,是一个典型的寓教于乐的题目。我们需要把扑克牌的背景抽象成计算机语言。不难想象,我们可以把5张牌看成由5个数字组成的数组。大小王是特殊的数字,我们不妨把它们都当成0,这样和其他扑克牌代表的数字就不重复了。接下来我们来分析怎样判断5个数字是不是连续的。最直观的是,我们把数组排序。但值得注意的是,由于0可以当成任意数字,我们可以用0去补满数组中的空缺。也就是排序之后的数组不是连续的,即相邻的两个数字相隔若干个数字,但如果我
阅读全文
摘要:题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。例如输入数组{32,321},则输出这两个能排成的最小数字32132。请给出解决问题的算法,并证明该算法。分析:这是09年6月份百度新鲜出炉的一道面试题,从这道题我们可以看出百度对应聘者在算法方面有很高的要求。这道题其实是希望我们能找到一个排序规则,根据这个规则排出来的数组能排成一个最小的数字。要确定排序规则,就得比较两个数字,也就是给出两个数字m和n,我们需要确定一个规则m和n哪个更大,而不是仅仅只是比较这两个数字的数值哪个更大。根据题目的要求,两个数字m和n排成的数字mn和nm,如果mn<nm,那么
阅读全文
摘要:题目:输入数字n,按顺序输出从1最大的n位10进制数。比如输入3,则输出1、2、3一直到最大的3位数即999。分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。应聘者在解决这个问题的时候,最容易想到的方法是先求出最大的n位数是什么,然后用一个循环从1开始逐个输出。很快,我们就能写出如下代码: 1 // Print numbers from 1 to the maximum number with n digits, in order 2 void Print1ToMaxOfNDigits_1(int n) 3 { 4 // calculate 10^n 5 i...
阅读全文
摘要:题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第1500个丑数。分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。所谓一个数m是另一个数n的因子,是指n能被m整除,也就是n % m == 0。根据丑数的定义,丑数只能被2、3和5整除。也就是说如果一个数如果它能被2整除,我们把它连续除以2;如果能被3整除,就连续除以3;如果能被5整除,就除以连续5。如果最后我们得到的是1,那么这个数就是丑数,否则不是。基于前面的分析,我们可以写出如下的函数来判断
阅读全文
摘要:题目:输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”。分析:这是一道微软面试题。在微软的常见面试题中,与字符串相关的题目占了很大的一部分,因为写程序操作字符串能很好的反映我们的编程基本功。要编程完成这道题要求的功能可能并不难。毕竟,这道题的基本思路就是在第一个字符串中拿到一个字符,在第二个字符串中查找一下,看它是不是在第二个字符串中。如果在的话,就从第一个字符串中删除。但如何能够把效率优化到让人满意的程度,却也不是一件容易的事情。也就是说,如何在第一个字符
阅读全文
摘要:题目:两个单向链表,找出它们的第一个公共结点。链表的结点定义为:struct ListNode{ int m_nKey; ListNode* m_pNext;};分析:这是一道微软的面试题。微软非常喜欢与链表相关的题目,因此在微软的面试题中,链表出现的概率相当高。如果两个单向链表有公共的结点,也就是说两个链表从某一结点开始,它们的m_pNext都指向同一个结点。但由于是单向链表的结点,每个结点只有一个m_pNext,因此从第一个公共结点开始,之后它们所有结点都是重合的,不可能再出现分叉。所以,两个有公共结点而部分重合的链表,拓扑形状看起来像一个Y,而不可...
阅读全文
摘要:题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。分析:这是一道很新颖的关于位运算的面试题。首先我们考虑这个问题的一个简单版本:一个数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:任何一个数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现依次的数字,因为那些出现两次的数字全部在异或中抵消掉了。有了上面简单问题
阅读全文
摘要:题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:struct ListNode{ int m_nKey; ListNode* m_pNext;};函数的声明如下:void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);分析:这是一道广为流传的Google面试题,能有效考察我们的编程基本功,还能考察我们的反应速度,更重要的是,还能考察我们对时间复杂度的理解。在链表中删除一个结点,最常规的做法是从链表的头结点开始,顺序查找要删除的结点,找到之后再删除。由于需要顺...
阅读全文
摘要:题目:用C++设计一个不能被继承的类。分析:这是Adobe公司2007年校园招聘的最新笔试题。这道题除了考察应聘者的C++基本功底外,还能考察反应能力,是一道很好的题目。在Java中定义了关键字final,被final修饰的类不能被继承。但在C++中没有final这个关键字,要实现这个要求还是需要花费一些精力。首先想到的是在C++中,子类的构造函数会自动调用父类的构造函数。同样,子类的析构函数也会自动调用父类的析构函数。要想一个类不能被继承,我们只要把它的构造函数和析构函数都定义为私有函数。那么当一个类试图从它那继承的时候,必然会由于试图调用构造函数、析构函数而导致编译错误。可是这个类的构造函
阅读全文
摘要:题目:输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下:struct ListNode{ int m_nKey; ListNode* m_pNext;};分析:这是一道很有意思的面试题。该题以及它的变体经常出现在各大公司的面试、笔试题中。看到这道题后,第一反应是从头到尾输出比较简单。于是很自然地想到把链表中链接结点的指针反转过来,改变链表的方向。然后就可以从头到尾输出了。反转链表的算法详见本人面试题精选系列的第19题,在此不再细述。但该方法需要额外的操作,应该还有更好的方法。接下来的想法是从头到尾遍历链表,每经过一个结点的时候,把该结点放到...
阅读全文
摘要:问题:给出如下CMyString的声明,要求为该类型添加赋值运算符函数。class CMyString{public: CMyString(char* pData = NULL); CMyString(const CMyString& str); ~CMyString(void); private: char* m_pData;};当面试官要求应聘者定义一个复制运算符函数时,他会关注如下几点:·是否把返回值的类型声明为该类型的引用,并在函数结束前返回实例自身(即*this)的引用?只有返回一个引用,才可以允许连续赋值。否则如果函数的返回值是void,假设有三个...
阅读全文
摘要:题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。要求时间复杂度为O(n)。分析:如果不考虑时间复杂度,最简单的思路应该是从头扫描这个数组,每碰到一个偶数时,拿出这个数字,并把位于这个数字后面的所有数字往前挪动一位。挪完之后在数组的末尾有一个空位,这时把该偶数放入这个空位。由于碰到一个偶数,需要移动O(n)个数字,因此总的时间复杂度是O(n2)。要求的是把奇数放在数组的前半部分,偶数放在数组的后半部分,因此所有的奇数应该位于偶数的前面。也就是说我们在扫描这个数组的时候,如果发现有偶数出现在奇数的前面,我们可以交换他们的顺序,交换之后就符合
阅读全文
摘要:题目:输入一棵二元树的根结点,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。例如:输入二元树:10/\6 14/ / \41216输出该树的深度3。二元树的结点定义如下:struct SBinaryTreeNode // a node of the binary tree{ int m_nValue; // value of node SBinaryTreeNode *m_pLeft; // left child of node SBinaryTreeNode *m_pRi...
阅读全文
摘要:题目:输入一个正数n,输出所有和为n连续正数序列。例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。分析:这是网易的一道面试题。这道题和本面试题系列的第10题有些类似。我们用两个数small和big分别表示序列的最小值和最大值。首先把small初始化为1,big初始化为2。如果从small到big的序列的和大于n的话,我们向右移动small,相当于从序列中去掉较小的数字。如果从small到big的序列的和小于n的话,我们向右移动big,相当于向序列中添加big的下一个数字。一直到small等于(1+n)/2,因为序列至少要有两个数字。基
阅读全文
摘要:题目:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。求总共有多少总跳法,并分析算法的时间复杂度。分析:这道题最近经常出现,包括MicroStrategy等比较重视算法的公司都曾先后选用过个这道题作为面试题或者笔试题。首先我们考虑最简单的情况。如果只有1级台阶,那显然只有一种跳法。如果有2级台阶,那就有两种跳的方法了:一种是分两次跳,每次跳1级;另外一种就是一次跳2级。现在我们再来讨论一般情况。我们把n级台阶时的跳法看成是n的函数,记为f(n)。当n>2时,第一次跳的时候就有两种不同的选择:一是第一次只跳1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);另外
阅读全文
摘要:题目:输入一个整数,求该整数的二进制表达中有多少个1。例如输入10,由于其二进制表示为1010,有两个1,因此输出2。分析:这是一道很基本的考查位运算的面试题。包括微软在内的很多公司都曾采用过这道题。一个很基本的想法是,我们先判断整数的最右边一位是不是1。接着把整数右移一位,原来处于右边第二位的数字现在被移到第一位了,再判断是不是1。这样每次移动一位,直到这个整数变成0为止。现在的问题变成怎样判断一个整数的最右边一位是不是1了。很简单,如果它和整数1作与运算。由于1除了最右边一位以外,其他所有位都为0。因此如果与运算的结果为1,表示整数的最右边一位是1,否则是0。得到的代码如下: 1 ////
阅读全文
摘要:题目描述:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数,要求对长度为n的字符串操作的时间复杂度为O(n),空间复杂度为O(1)。编程之美上有这样一个类似的问题,咱们先来看一下:设计一个算法,把一个含有N个元素的数组循环右移K位,要求时间复杂度为O(N),且只允许使用两个附加变量。分析:我们先试验简单的办法,可以每次将数组中的元素右移一位,循环K次。abcd1234→4abcd123→34abcd12→234abcd1→1234abcd。 1 RightShift(int* arr, int
阅读全文