《Cracking the coding interview》之题目与思路的故事

本文探讨了多种字符串处理及二叉树相关算法问题,包括判断字符串中字符是否唯一、去除重复字符、判断变位词等。同时,还讨论了二叉树平衡性的判断、寻找最短路径、构建最小高度二叉树等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 1.1实现一个算法来判断一个字符串中的字符是否唯一(即没有重复).不能使用额外的数据结构。 (即只使用基本的数据结构)

首先,你可以问面试官,构成字符串的字符集有多大?是ASCII字符,还是只是26个字母? 还是有更大的字符集,对于不同的情况,我们可能会有不同的解决方案。

  1. 如果我们假设字符集是ASCII字符,那么我们可以开一个大小为256的bool数组来表征每个字 符的出现。数组初始化为false,遍历一遍字符串中的字符,当bool数组对应位置的值为真, 表明该字符在之前已经出现过,即可得出该字符串中有重复字符。否则将该位置的bool数组 值置为true。

  2. 该算法的时间复杂度为O(n)。我们还可以通过位运算来减少空间的使用量。 用每一位表征相应位置字符的出现。对于ASCII字符,我们需要256位,即一个长度为8的int 数组a即可。

  • 1.3 设计算法并写出代码移除字符串中重复的字符,不能使用额外的缓存空间。注意: 可以使用额外的一个或两个变量,但不允许额外再开一个数组拷贝。
#include<iostream>
#include<bitset>
#include<cstring>
using namespace  std;
//假设全是'a'~'z'的字母
void fun(char s[])
{
    int len = strlen(s);
    std::bitset<32>  bitmap(0);
    int p = 0 ;
    for(int i= 0 ;i< len ;i++)
    {
        if( bitmap.test(s[i]-'a') ==  false )
        {
            s[p++] = s[i];
        }
        bitmap.set(s[i]-'a');

    }
    s[p] =0;
    cout << s << endl;
}
  • 1.3 写一个函数判断两个字符串是否是变位词。(变位词(anagrams)指的是组成两个单词的字符相同,但位置不同的单词。比如说, abbcd和abcdb就是一对变位词)

  • 1.4
    由于组成变位词的字符是一模一样的, 因此我们可以先统计每个字符串中各个字符出现的次数, 然后看这两个字符串中各字符出现次数是否一样。如果是,则它们是一对变位词。 这需要开一个辅助数组来保存各字符的出现次数。我们可以开一个大小是256的整数数组, 遍历第一个字符串时,将相应字符出现的次数加1;遍历第二个字符串时, 将相应字符出现的次数减1。最后如果数组中256个数都为0,说明两个字符串是一对变位词

  • 4.1 实现一个函数检查一棵树是否平衡。对于这个问题而言, 平衡指的是这棵树任意两个叶子结点到根结点的距离之差不大于1。
    对于这道题,要审清题意。它并不是让你判断一棵树是否为平衡二叉树。 平衡二叉树的定义为:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1, 并且左右两个子树都是一棵平衡二叉树。 而本题的平衡指的是这棵树任意两个叶子结点到根结点的距离之差不大于1。 这两个概念是不一样的。例如下图,它是一棵平衡二叉树,但不满足本题的平衡条件。 (叶子结点f和l到根结点的距离之差等于2,不满足题目条件)

那么如何判断一棵树是不是平衡二叉树呐?

对于本题,只需要求出离根结点最近和最远的叶子结点, 然后看它们到根结点的距离之差是否大于1即可

leetcode链接:https://leetcode-cn.com/problems/balanced-binary-tree/

  vector<int>  depth_vec{1000,0};
   int num  = 0;
   int d = 0;
   void getDepthVec(TreeNode *head)
   {
       if( !head)  return ;
       d++;
       getDepthVec(head->left);
       if(!head->left && !head->right)
           depth_vec[num++] = d;
       getDepthVec(head->right);
       d--;
   }
   bool isBalanced(TreeNode* root) {
       getDepthVec(root);
       int min =  depth_vec[0];
       int  max  = min;
       for(auto i:depth_vec)
       {
           if( i < min )  min = i;
           if( i > max )  max = i;
       }
       if(max - min > 1)
           return false;
       else
           return true;
   }

4.2 给定一个有向图,设计算法判断两结点间是否存在路径。

DFS / BFS

4.3 给定一个有序数组(递增),写程序构建一棵具有最小高度的二叉树。

想要使构建出来的二叉树高度最小,那么对于任意结点, 它的左子树和右子树的结点数量应该相当。比如,当我们将一个数放在根结点, 那么理想情况是,我们把数组中剩下的数对半分,一半放在根结点的左子树, 另一半放在根结点的右子树。我们可以定义不同的规则来决定这些数怎样对半分, 不过最简单的方法就是取得有序数组中中间那个数,然后把小于它的放在它的左子树, 大于它的放在它的右子树。不断地递归操作即可构造这样一棵最小高度二叉树。

void create_minimal_tree(Node* &head, Node *parent, int a[], int start, int end){
    if(start <= end){
        int mid = (start + end)>>1;
        node[cnt].key = a[mid];
        node[cnt].parent = parent;
        head = &node[cnt++];
        create_minimal_tree(head->lchild, head, a, start, mid-1);
        create_minimal_tree(head->rchild, head, a, mid+1, end);
    }
}

4.4 给定一棵二叉查找树,设计算法,将每一层的所有结点构建为一个链表(也就是说, 如果树有D层,那么你将构建出D个链表)
BFS

vector<list<Node*> > find_level_linklists(Node *head){
        if( ! head) return vector<list<Node*> >() ;

		std::queue<Node *>  que;
		vector<list<Node*> > res(3);
		int index = 0 ;
        Node *ptr = nullptr ;

		que.push(head);

		while(!que.empty())
		{
			int size  = que.size();
			for(int i=0 ;i< size ;i++)
			{
                ptr = que.front();
				res[index].push_back(ptr);
				que.pop();
                if(ptr->lchild)
                    que.push(ptr->lchild);
                if(ptr->rchild)
                    que.push(ptr->rchild);
			}
			index++;
		}
    return res;
}

4.6 写程序在一棵二叉树中找到两个结点的第一个共同祖先。不允许存储额外的结点。注意: 这里不特指二叉查找树。

其实也很简单,首先根结点一定为任意两个结点的共同祖先, 从根结点不断往下找,直到找到最后一个这两结点的共同祖先,即为题目所求。代码如下:

bool father(Node* n1, Node* n2){
    if(n1 == NULL) return false;
    else if(n1 == n2) return true;
    else return father(n1->lchild, n2) || father(n1->rchild, n2);
}
void first_ancestor2(Node* head, Node* n1, Node* n2, Node* &ans){
    if(head==NULL || n1==NULL || n2==NULL) return;
    if(head && father(head, n1) && father(head, n2)){
        ans = head;
        first_ancestor2(head->lchild, n1, n2, ans);
        first_ancestor2(head->rchild, n1, n2, ans);
    }
}

4.7 有两棵很大的二叉树:T1有上百万个结点,T2有上百个结点。写程序判断T2是否为T1的子树。

class Solution {
public:
	//子树
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
		if(!pRoot1  || !pRoot2)
			return false ;

		bool res ;
		if(pRoot1->val == pRoot2->val)
		{
			res = samTree(pRoot1->left,pRoot2->left) && samTree(pRoot1->right,pRoot2->right);
			if(res)  return res ; //如果直接检测成功就直接返回即可,如果不是,那就递归检测!!!
		}
		return HasSubtree(pRoot1->left,pRoot2) || HasSubtree(pRoot1->right,pRoot2);
    }
	bool  samTree(TreeNode* p1, TreeNode* p2)
	{
		if(!p2)
			return true;
		if(!p1)
			return false;
		if(p1->val !=  p2->val )
			return false;
		return samTree(p1->left,p2->left) && samTree(p1->right,p2->right);
	}
};

参考:http://hawstein.com/2013/03/14/ctci-solutions-contents/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值