一、赋值运算符函数

题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数。

class CMyString {
public:
CMyString(char *pData = nullptr);
CMyString(const CMyString &str);
~CMyString();
private:
char *m_pData;
};

测试用例:

  • 把一个CMyString的实例赋值给另外一个实例。
  • 把一个CMyString的实例赋值给它自己。
  • 连续赋值。

没有考虑异常安全性的解法:

CMyString& CMyString::operator=(const CMyString &rhs)
{
if(this != &rhs)
{
delete []m_pData;
m_pData = nullptr;
m_pData = new char[strlen(rhs.m_pData) + 1];
strcpy(m_pData, rhs.m_pData);
}
return *this;
}

考虑异常安全性的解法:

CMyString& CMyString::operator=(const CMyString &rhs)
{
if(this != &rhs)
{
CMyString strTemp(rhs); // 创建一个临时实例strTemp
/* 交换strTemp.m_pData和实例自身的m_pData */
char *pTemp = m_pData;
m_pData = strTemp.m_pData;
strTemp.m_pData = pTemp;
} /* 自动调用strTemp的析构函数,释放strTemp.m_pData指向的新内存 */
return *this;
}

补:在新的代码中,我们在CMyString的构造函数里用new分配内存,故如果内存不足将抛出诸如bad_alloc等异常,但由于我们还没有修改实例自身的状态,故实例的状态还是有效的,这也就保证了异常安全性。

考点:

  • C++基础语法,如运算符函数、常量引用等。
  • 内存泄露。
  • 代码异常安全性。

二、实现Singleton(单例)模式

题目:设计一个类,我们只能生成该类的一个实例。

解读题意:只能生成一个实例的类是实现了Singleton模式的类型。

考点:

  • 单例模式
  • C#基础语法,如静态构造函数等。
  • 多线程编程。

三、数组中重复的数字

题目一:找出数组中重复的数字。在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。

测试用例:

  • 长度为n的数组里包含一个或多个重复的数字。
  • 数组中不包含重复的数字。
  • 无效输入测试用例,如输入空指针、长度为n的数组中包含0~n-1之外的数字。

时间复杂度和空间复杂度均为O(n)的解法:

int solve(int arr[], int len)
{
int ans = -1; // 没有重复的数字则返回-1
int *pArr = new int[len](); // 对动态数组进行值初始化
for(int i = 0; i != len; ++i)
{
++pArr[arr[i]];
}
for(int i = 0; i != len; ++i) {
if(pArr[i] > 1) {
ans = i;
break;
}
}
delete []pArr;
return ans;
}

补:书中的解法是时间复杂度为O(n),而空间复杂度为O(1)。

题目二:不修改数组找出重复的数字。在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。

分析:这一题和上面的面试题类似,但题目明确要求不能修改输入的数组。我们可以像上题一样,创建一个长度为n+1的辅助数组,但该方案需要O(n)的辅助空间。我们要设法尝试避免使用O(n)的辅助空间。但正如下面这个算法所示,即使它的空间复杂度为O(1),但其时间复杂度却为O(nlogn),而且它不能保证找出所有重复的数字。故我们选取什么算法应取决于面试官提出的功能要求或性能要求。

思路:由题意可以确定数组中一定有重复的数字,故可以把从1~n的数字从中间的数字m分为两部分,前面一半为1~m,后面一半为m+1~n。如果1~m的数字的数目超过m,那么这一半的区间里一定包含重复的数字;否则,另一半m+1~n的区间里一定包含重复的数字。然后,继续把包含重复数字的区间一分为二,直到找到一个重复的数字。

四、在二维数组中的查找

题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

测试用例:

  • 二维数组中包含查找的数字。
  • 二维数组中没有查找的数字。
  • 特殊输入测试。

解法:

bool solve(int *matrix, int rows, int cols, int x)
{
bool found = false;
if(rows > 0 && cols > 0)
{
int row = 0, col = cols - 1;
while(row < rows && col >= 0)
{
if(matrix[row * cols + col] == x)
{
found = true;
break;
}
else if(matrix[row * cols + col] > x)
--col;
else
++row;
}
}
return found;
}

分析:若从二维数组(矩形)的中间选取一个数字来比较,那么如果该数字与目标值不等的话,接下来的查找将没有固定的范围。而如果从右上角或左下角开始进行比较,则会逐渐缩小查找范围,最终得到结果。所以,该题考察的是应聘者能否通过具体的例子来找出其中的规律。

五、替换空格

题目:请实现一个函数,把字符串中的每个空格替换成“%20”。例如,输入“We are happy!”,则输出“We%20are%20happy!”。

题意:假设面试官是让我们在原来的字符串上进行替换,并且保证输入的字符串后面有足够多的空余内存。

测试用例:

  • 输入的字符串中包含空格,空格可位于字符串的最前面、最后面或中间,甚至可以有连续多个空格。
  • 输入的字符串中没有空格。
  • 特殊输入测试,如字符串是一个空字符串,或字符串只有一个空格字符。

时间复杂度为O(n2)的解法:

从头到尾扫描字符串,每次碰到空格字符的时候进行替换。而由于是把1个字符替换成3个字符,所以我们必须要把空格后面所有的字符都后移2字节,否则就有两个字符被覆盖了。

假设字符串的长度为n,对每个空格字符,需要移动后面O(n)个字符,因此对于含有O(n)个空格字符的字符串而言,总的时间效率是O(n2)。

时间复杂度为O(n)的解法:

先遍历一次字符串,这样就能统计出字符串中空格的总数,并可以由此计算出替换之后的字符串的总长度。我们从字符串的后面开始复制和替换。

六、从尾到头打印链表

题目:输入一个链表的头节点,从尾到头反过来打印出每个节点的值。

测试用例:

  • 功能测试,如输入的链表有多个节点,或输入的链表只有一个节点。
  • 特殊输入测试,如输入的链表头节点指针为nullptr。

栈+循环的解法:

struct ListNode {
int value;
ListNode *next;
}; void print_list(ListNode *pHead)
{
stack<int> sck;
ListNode *pNode = pHead;
while(pNode) {
sck.push(pNode->value);
pNode = pNode->next;
}
while(!sck.empty()) {
cout << sck.top() << endl;
sck.pop();
}
}

栈+递归的解法:

struct ListNode {
int value;
ListNode *next;
}; void print_list_recursively(ListNode *pHead)
{
if(pHead != nullptr) {
if(pHead->next != nullptr) {
print_list_recursively(pHead->next);
}
cout << pHead->value << cout;
}
}

补充:既然想到了用栈来实现该函数,并且递归在本质上就是一个栈结构,于是很自然地又想到了用递归来实现。

注意:当链表非常长的时候,就会导致函数调用的层级很深,从而有可能导致函数调用栈溢出。

七、重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树并输出它的头节点。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如,输入前序遍历序列{1, 2, 4, 7, 3, 5, 6, 8}和中序遍历序列{4, 7, 2, 1, 5, 3, 8, 6},则重建如下图所示的二叉树并输出它的头节点。

测试用例:

  • 普通二叉树。
  • 特殊二叉树,如只有一个节点的二叉树、所有节点都没有右子节点的二叉树、所有节点都没有左子节点的二叉树。
  • 特殊输入测试,如二叉树的根节点指针为nullptr、输入的前序遍历序列和中序遍历序列不匹配。

考点:

  • 对二叉树的前序遍历和中序遍历的理解程度。
  • 分析复杂问题的能力,把构建二叉树的大问题分解成构建左、右子树的两个小问题,而小问题和大问题在本质上是一致的,故可以用递归的方式解决。
struct BinaryTreeNode {
int m_nValue;
BinaryTreeNode *m_pLeft;
BinaryTreeNode *m_pRight;
}; BinaryTreeNode* Construct(int* preorder, int* inorder, int length)
{
if(preorder == nullptr || inorder == nullptr || length <= 0)
return nullptr; return ConstructCore(preorder, preorder + length - 1, inorder, inorder + length - 1);
} BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder)
{
// 前序遍历序列的第一个数字是根结点的值
int rootValue = startPreorder[0];
BinaryTreeNode* root = new BinaryTreeNode();
root->m_nValue = rootValue;
root->m_pLeft = root->m_pRight = nullptr; if(startPreorder == endPreorder)
{
if(startInorder == endInorder && *startPreorder == *startInorder)
return root;
else
throw std::exception("Invalid input.");
} // 在中序遍历中找到根结点的值
int* rootInorder = startInorder;
while(rootInorder <= endInorder && *rootInorder != rootValue)
++ rootInorder; if(rootInorder == endInorder && *rootInorder != rootValue)
throw std::exception("Invalid input."); int leftLength = rootInorder - startInorder;
int* leftPreorderEnd = startPreorder + leftLength;
if(leftLength > 0)
{
// 构建左子树
root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd,
startInorder, rootInorder - 1);
}
if(leftLength < endPreorder - startPreorder)
{
// 构建右子树
root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder,
rootInorder + 1, endInorder);
} return root;
}

小结:其实面试不会让写这么一道题,但容易考到的是先序遍历的递归/循环实现方法,对于中序遍历、后序遍历也是如此。

八、二叉树的下一个节点

题目:给定一棵二叉树和其中的一个节点,如何找出中序遍历序列的下一个节点?树中的节点除了有两个分别指向左、右子节点的指针,还有一个指向父节点的指针。

分析:此题需要应聘者在掌握中序遍历的基础上,通过具体的例子来找出其中的规律,并由此设计出可行的算法。

解法:

BinaryTreeNode* find_next(BinaryTreeNode *pNode)
{
if(pNode == nullptr)
return nullptr;
BinaryTreeNode *pNext = nullptr;
if(pNode->m_pRight != nullptr) {
BinaryTreeNode *pTemp = pNode->m_pRight;
while(pTemp->m_pLeft != nullptr)
pTemp = pTemp->m_pLeft;
pNext = pTemp;
}
else if(pNode->m_pParent != nullptr) {
BinaryTreeNode *pCurrent = pNode;
BinaryTreeNode *pParent = pNode->m_pParent;
while(pParent != nullptr && pCurrent == pParent->m_pRight) {
pCurrent = pParent;
pParent = pParent->m_pParent;
}
pNext = pParent;
}
return pNext;
}

  

九、用两个栈实现队列

题目:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入节点和在队列头部删除节点的功能。

测试用例:

  • 往空的队列里添加、删除元素。
  • 往非空的队列里添加、删除元素。
  • 连续删除元素直至队列为空。

解法:

stack<int> sck1;
stack<int> sck2; void appendTail(int val)
{
sck1.push(val);
} int deleteHead()
{
if(sck2.empty()) {
while(!sck1.empty()) {
int temp = sck1.top();
sck1.pop();
sck2.push(temp);
}
}
if(sck2.empty())
throw new exception("queue is empty");
int ret = sck2.top();
sck2.pop();
return ret;
}

分析:这道题的意图是要求我们操作两个“后进先出”的栈sck1和sck2以实现一个“先进先出”的队列。当添加元素时,我们总是向栈sck1内插入;而删除元素时,则总是从栈sck2内删除。

拓展:用两个队列实现一个栈。分析思路见书p71。

十、斐波拉契数列

题目一:求斐波那契数列的第n项。写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。

递归解法:

int fibonacci(int n)
{
if(n == 0) return 0;
if(n == 1) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}

分析:许多教科书在讲解递归算法时,总是以此段代码作为示例,但该解法有很严重的效率问题。一句话阐明,该递归代码分解的两个子问题中存在大量重复的计算。

循环解法:

int fibonacci(int n)
{
if(n == 0) return 0;
if(n == 1) return 1;
int x1 = 0;
int x2 = 1;
int ans = 0;
for(int i = 2; i <= n; ++i) {
ans = x1 + x2;
x1 = x2;
x2 = ans;
}
return ans;
}

分析:上述递归代码之所以慢,是因为存在大量重复的计算,因此,我们只要想办法避免重复计算就行了。该解法是从下往上计算,首先根据f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3)……以此类推就可以算出第n项了。

题目二:青蛙跳台阶问题。一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个n级的台阶总共有多少种跳法。

分析:把n级台阶时的跳法看成n的函数,记为f(n),这就不难看出该问题就是斐波那契数列了。

  

  

《剑指Offer》题一~题十的更多相关文章

  1. 浅谈《剑指offer》原题:不使用条件、循环语句求1+2+……+n

    转载自:浅谈<剑指offer>原题:求1+2+--+n 如侵犯您的版权,请联系:windeal12@qq.com <剑指offer>上的一道原题,求1+2+--+n,要求不能使 ...

  2. 《剑指offer》算法题第十二天

    今天是<剑指offer>算法题系列的最后一天了,但是这个系列并没有包括书上的所有题目,因为正如第一天所说,这些代码是在牛客网上写并且测试的,但是牛客网上并没有涵盖书上所有的题目. 今日题目 ...

  3. 《剑指offer》刷题目录

    <剑指offer>刷题目录 面试题03. 数组中重复的数字 面试题04. 二维数组中的查找 面试题05. 替换空格 面试题06. 从尾到头打印链表 面试题07. 重建二叉树 面试题09. ...

  4. 《剑指offer》算法题第十天

    今日题目: 数组中的逆序对 两个链表的第一个公共节点 数字在排序数组中出现的次数 二叉搜索树的第k大节点 字符流中第一个不重复的字符 1. 数组中的逆序对 题目描述: 在数组中的两个数字,如果前面一个 ...

  5. 剑指offer部分编程题

    一 斐波那契数列 题目描述: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项. n<=39 问题分析: 可以肯定的是这一题通过递归的方式是肯定能做出来,但是这样会有 ...

  6. 《剑指Offer》附加题_用两个队列实现一个栈_C++版

    在<剑指Offer>中,在栈和队列习题中,作者留下来一道题目供读者自己实现,即"用两个队列实现一个栈". 在计算机数据结构中,栈的特点是后进先出,即最后被压入(push ...

  7. 剑指offer 面试5题

    面试5题: 题目:请实现一个函数,将一个字符串中的空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 方法一: # -*- co ...

  8. 剑指offer 面试8题

    面试8题: 题目:二叉树的下一个节点 题目描述:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 解题思路:详见剑 ...

  9. 剑指offer 面试10题

    面试10题: 题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项.n<=39 n=0时,f(n)=0 n=1时,f(n)=1 n>1时,f(n)=f(n-1 ...

  10. 剑指offer 面试11题

    面试11题: 题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4 ...

随机推荐

  1. 竞赛题解 - [CF 1080D]Olya and magical square

    Olya and magical square - 竞赛题解 借鉴了一下神犇tly的博客QwQ(还是打一下广告) 终于弄懂了 Codeforces 传送门 『题目』(直接上翻译了) 给一个边长为 \( ...

  2. linux系统基础之---RAID(基于centos7.4 1708)

  3. h5和css3构建响应式网站

    响应式页面组成 灵活图像,媒体:资源尺寸使用百分比定义 流式布局,所有width属性使用百分比设定,水平属性通常使用相对单位(em,百分数,rem等) 媒体查询,根据媒体特征进行设计调整 创建可伸缩图 ...

  4. 万恶的a标签

    相信很多人碰见过这些问题吧  给某个a标签套的元素中添加点击事件 在外面就能获取到但是点击事件不生效把  或者在页面中点击一个a标签元素发现页面返回了最顶端 然后就开始郁闷了 哈哈 其实这些看似神奇的 ...

  5. PHP Mysql字符集utf8mb4支持Emoji表情

    项目开发中经常会遇到用户在评论或者发表文章的时候会打一些表情在里面,如果我们在开发中不去做一些处理的话,表情会出不来的,甚至是报错,下面简单介绍处理方式.原文地址:小时刻个人博客:http://sma ...

  6. 【NXP开发板应用—智能插排】2.初步解析example之GPI

    [前言] 首先感谢深圳市米尔科技有限公司举办的这次活动并予以本人参加这次活动的机会,以往接触过嵌入式,但那都是皮毛,最多刷个系统之类的,可以说对于嵌入式系统开发这件事情是相当非常陌生的,这次活动为我提 ...

  7. 最小生成树——Kruscal(克鲁斯卡尔算法)

    一.核心思想 ​ 将输入的数据由小到大进行排序,再使用并查集算法(传送门)将每个点连接起来,同时求和. ​ 个人认为这个算法比较偏向暴力,有些题可能会超时. 二.例题 洛谷-P3366 题目地址:ht ...

  8. 解决WinScp连接被拒绝的问题

    尝试以下方法: 1) 开启|关闭防火墙(这里需要关闭) sudo ufw enable|disable 2) 开启远程服务 在终端界面输入:service sshd start.结果显示:ssh:un ...

  9. 北京Uber优步司机奖励政策(3月29日

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  10. 北京Uber优步司机奖励政策(1月24日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...