7.剑指Offer --- 两个面试案例

第7章 两个面试案例 
	介绍项目经历的时候,参照star模型,着重介绍自己完成的工作(包括基于什么平台,用来哪些技术,实现了哪些算法等),以及最终对项目组的贡献。

	技术面试环节,从编程语言,数据结构和算法等考察基础知识是否全面,很有可能要求面试者实现一两个函数。如果碰到简单的也不能掉以轻心,一定要从基本
功能,边界条件和错误处理等方面确保代码的鲁棒性和完整性。如果碰到难的题目,可以尝试画图让抽象的问题变得形象化,也可以尝试举例子去分析隐藏的规律,还可以
试图把大的问题分解成两个或者多个小问题再递归的解决。很多面试题不止一种解法,可以从时间复杂度和空间复杂度两个方面选择最优的解法。
	
	除了技术能力,还会关注应聘者的沟通和学习能力,并有可能考察知识迁移能力,抽象建模能力和发散思维能力。

	面试结束前几分钟,提问环节。可以从当前招聘的项目及其团队情况提几个问题。不建议问薪资,问结果。

面试题49:把字符串换成整数
	思路分析:当扫描字符串的第一个字符“1“时,由于是第一位,故得到数字1;继续向后扫描到第二个字符”2“,之前已经得到数字1,在其后添加一个数字2,
得到数字12,相当于前面的数字扩大了10倍然后加上刚扫描到的数字2,即:1×10+2=12。同理,扫描到第三个字符”3“,即可得到最终整数123为所求。
故而,其基本思路就是:从左至右扫描字符串,把之前得到的数字乘以10,再加上当前字符表示的数字。

enum Status { kValid = 0, kInvalid };

int g_nStatus = kValid;

int StrToInt(const char* str)
{
	g_nStatus = kInvalid;
	long long num = 0;

	if (str != null && *str != '\0') {
		bool minus = false;

		if (*str == '+') {
			str++;
		} else if (*str == '-') {
			str++;
			minus = true;
		}

		if (*str != '\0') {
			num = StrToIntCore(str, minus);
		}
	}

	return (int)num;
}

long long StrToIntCore(const char* digit, bool minus)
{
	long long num = 0;

	while (*digit != '\0') {

		if (*digit >= '0' && *digit <= '9') {
			int flag = minus ? -1 : 1;
			num = num * 10 + flag * (*digit - '0');

			//正数的最大值0x7FFFFFFF,负数的最大值0x80000000,判断整数是否上溢和下溢
			if ((!minus && num > 0x7FFFFFFF) || (minus && num < (signed int)0x80000000)) {
				num = 0;
				break;
			} 
			digit++;
		} else {
			num = 0;
			break;
		}
	}

	if (*digit == '\0') {
		g_nStatus = kvalid;
	}

	return num;
}


面试题50:树中两个结点的最低公共祖先
	题目一:如果这棵树是二叉搜索树

	分析:
		二叉搜索树是排序过的。如果当前结点的值比两个结点的值都大,那么最低的公共父结点一定是在当前结点的左子树中,于是下一步遍历当前结点的左子树;
	如果当前结点的值比两个结点都小,那么最低公共结点一定在当前结点的右子树中,于是下一步遍历当前结点的右子结点。这样在树中从上到下找到的第一个在
	两个输入结点的值之间的结点,就是最低的公共祖先。

	题目二:如果这棵树不是二叉搜索树,甚至二叉树都不是,只是普通的树,那怎么办?

	分析:
		如果树中的每个结点(除了根结点以外)都有一个指向父结点的指针,这个问题可以转换为求两个链表的第一个公共结点。比如输入两个结点分为为F和H,
	那么F在链表 F=>D=>B=>A上,而H在链表H=>E=>B=>A上,B刚好是它们最低的公共祖先。

	题目三:现在这棵树是普通的树,甚至树中结点没有指向父结点的指针。

	分析:
		所谓两个结点的公共祖先,指的是这两个结点都出现在某个结点的子树中。我们可以从根结点开始遍历一棵树,每遍历到一个结点时,判断两个输入的结点
	是不是在它的子树中。如果在子树中,则分别遍历它的所有子节点,并判断两个输入结点是不是在它们的子树中。这样从上到下找到的第一个结点,它自己的子树
	中同时包含两个输入的结点而它的子节点却没有,那么该结点就是最低的公共祖先。

		还是假设输入结点F和H。我们先判断A的子树中是否同时包含F和H,得到的结果是true。接着再先后判断A结点的两个子节点B和C的子树是不是同时包含F和
	H,结果B的结果是true,而C的结果是false。接下来我们再判断B的两个节点D和E,发现这两个结点的结果都是false。于是B是最后一个公共祖先。

		缺点:
		  这种思路会对同一个结点重复遍历很多次。

		改进:
			使用辅助空间。用两个链表分别保存从根结点到输入的两个结点的路径,把问题转换为两个链表的最后公共结点。

			首先我们得到一条从根结点到树中某一结点的路径,这就要求在遍历的时候,有一个辅助的内存来保存路径。比如我们用前序遍历的方法来得到从根结点
		到H的路径A=>B=>D。再从根结点到F的必经路径,A=>B=>D。最后求这两个路径的最后公共结点,也就是B。时间复杂度是O(n)。

//获取从根结点 pRoot 到 pNode 的路径,保存在 path中。
bool GetNodePath(TreeNode* pRoot, TreeNode* pNode, list<TreeNode*>&path)
{
	if (pRoot == pNode) 
		return true;

	path.push_back(pRoot);

	bool found = false;

	vector<TreeNode*>::iterator i = pRoot->m_vChildren.begin();
	while (!found && i < pRoot->m_vChildren.end()) {
		found = GetNodePath(*i, pNode, path);
		i++;
	}

	if (!found) 
		path.pop_back();

	return found;
}

//用来得到两个路径path1,path2 的最后一个公共节点
TreeNode* GetLastCommonNode(const list<TreeNode*>& path1, const list<TreeNode*>& path2)
{
	list<TreeNode*>::const_iterator iterator1 = path1.begin();
	list<TreeNode*>::const_iterator iterator2 = path2.begin();

	TreeNode* pLast = null;

	while (iterator1 != path1.end() && iterator2 != path2.end()) {
		if (*iterator1 == *iterator2) {
			pLast = *iterator1;
		}

		iterator1++;
		iterator2++;
	} 

	return pLast;
}

TreeNode* GetLastCommonParent(TreeNode* pRoot, TreeNode* pNode1, TreeNode* pNode2)
{
	if (pRoot == null | pNode1 == null || pNode2 == null)
		return null;

	list<TreeNode*> path1;
	GetNodePath(pRoot, pNode1, path1);

	list<TreeNode*> path2;
	GetNodePath(pRoot, pNode2, path2);

	return GetLastCommonNode(path1, path2);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值