[高阶数据结构]二叉树经典面试题

二叉树经典面试题::

目录

二叉树经典面试题::

1.根据二叉树创建字符串

2.二叉树的层序遍历

3.二叉树的层序遍历II

4.二叉树的最近公共祖先

5.二叉树与双向链表

6.从前序与中序序列构造二叉树

7.从中序与后序序列构造二叉树

8.非递归实现二叉树的前序遍历

9.非递归实现二叉树的中序遍历

10.非递归实现二叉树的后序遍历

11.前K个高频单词

12.在长度2N的数组中找出重复N次的元素

13.两个数组的交集I

14.两个数组的交集II

15.两句话中的不常见单词


1.根据二叉树创建字符串

cpp 复制代码
struct TreeNode 
{
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode() : val(0), left(nullptr), right(nullptr) {}
      TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
      TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right)             {}
};
 
class Solution 
{
public:
	string tree2str(TreeNode* root) 
	{
		if (root == nullptr)
		{
			return string();
		}
		string str;
		str += to_string(root->val);
		if (root->left != nullptr)
		{
			str += '(';
			str += tree2str(root->left);
			str += ')';
		}
		else if(root->left == nullptr && root->right != nullptr)
		{
			str += "()";
		}
		
		if (root->right != nullptr)
		{
			str += '(';
			str += tree2str(root->right);
			str += ')';
		}
		//右边为空则不处理
		return str;
	}
};

2.二叉树的层序遍历

cpp 复制代码
struct TreeNode 
{
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode() : val(0), left(nullptr), right(nullptr) {}
      TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
      TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution 
{
public:
	vector<vector<int>> levelOrder(TreeNode* root) 
	{
		queue<TreeNode*> q;
		vector<vector<int>> vv;
		int levelSize = 0;
		if (root != nullptr)
		{
			q.push(root);
			levelSize = 1;
		}
		while (!q.empty())
		{
			//一层一层出
			vector<int> levelv;
			while (levelSize--)
			{
				TreeNode* front = q.front();
				q.pop();
				levelv.push_back(front->val);
				if (front->left != nullptr)
				{
					q.push(front->left);
				}
				if (front->right != nullptr)
				{
					q.push(front->right);
				}
			}
			vv.push_back(levelv);
			//当前一层出完 下一层全部进入队列 那么size就是下一层的数据个数
			levelSize = q.size();
			
		}
		return vv;
	}
};

3.二叉树的层序遍历II

cpp 复制代码
struct TreeNode
{
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode() : val(0), left(nullptr), right(nullptr) {}
      TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
      TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution
{
public:
	vector<vector<int>> levelOrderBottom(TreeNode* root)
	{
		queue<TreeNode*> q;
		vector<vector<int>> vv;
		int levelSize = 0;
		if (root != nullptr)
		{
			q.push(root);
			levelSize = 1;
		}
		while (!q.empty())
		{
			vector<int> levelv;
			while (levelSize--)
			{
				TreeNode* front = q.front();
				q.pop();
				levelv.push_back(front->val);
				if (front->left != nullptr)
				{
					q.push(front->left);
				}
				if (front->right != nullptr)
				{
					q.push(front->right);
				}
			}
			vv.push_back(levelv);
			levelSize = q.size();

		}
		reverse(vv.begin(), vv.end());
		return vv;
	}
};

4.二叉树的最近公共祖先

cpp 复制代码
 struct TreeNode
 {
       int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 };

class Solution
{
public:
	bool GetPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
	{
		if (root == nullptr)
		{
			return false;
		}
		path.push(root);
		if (root == x)
		{
			return true;
		}
		if (GetPath(root->left, x, path))
		{
			return true;
		}
		if (GetPath(root->right, x, path))
		{
			return true;
		}
		path.pop();
		return false;
	}


	TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
	{
		stack<TreeNode*> pPath;
		stack<TreeNode*> qPath;
		GetPath(root, p, pPath);
		GetPath(root, q, qPath);

		//找路径相遇点
		while (pPath.size() != qPath.size())
		{
			if (pPath.size() > qPath.size())
			{
				pPath.pop();
			}
			else
			{
				qPath.pop();
			}
		}

		while (pPath.top() != qPath.top())
		{
			pPath.pop();
			qPath.pop();
		}
		return pPath.top();
	}

};

5.二叉树与双向链表

cpp 复制代码
struct TreeNode
{
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) 
    {}
};
class Solution 
{
public:
	void InOrderConvert(TreeNode* cur, TreeNode*& prev)
	{
		if (cur == nullptr)
		{
			return;
		}
		InOrderConvert(cur->left, prev);
		cur->left = prev;
		if (prev != nullptr)
		{
			prev->right = cur;
		}
		prev = cur;
		InOrderConvert(cur->right, prev);
	}
	TreeNode* Convert(TreeNode* pRootOfTree) 
	{
		TreeNode* prev = nullptr;
		InOrderConvert(pRootOfTree, prev);
		TreeNode* head = pRootOfTree;
		while (head != nullptr && head->left != nullptr)
		{
			head = head->left;
		}
		return head;
	}
};

6.从前序与中序序列构造二叉树

cpp 复制代码
struct TreeNode 
{
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode() : val(0), left(nullptr), right(nullptr) {}
      TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
      TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution
{
public:
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int inbegin, int inend) 
    {
        //中序区间不存在则是空树
        if (inbegin > inend)
        {
            return nullptr;
        }
        //划分左右区间
        int rooti = inbegin;
        while (rooti <= inend)
        {
            if (inorder[rooti] ==preorder[prei])
            {
                break;
            }
            ++rooti;
        }
        TreeNode* root = new TreeNode(preorder[prei]);
        ++prei;
        //[inbegin,rooti-1] rooti [rooti+1,inend]
        root->left = _buildTree(preorder, inorder, prei, inbegin, rooti - 1);
        root->right = _buildTree(preorder, inorder, prei, rooti + 1, inend);
        return root;

    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
    {
        int pi = 0;
        return _buildTree(preorder, inorder, pi, 0, inorder.size() - 1);
    }
};

7.从中序与后序序列构造二叉树

cpp 复制代码
struct TreeNode 
{
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode() : val(0), left(nullptr), right(nullptr) {}
      TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
      TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution 
{
public:
	TreeNode* _buildTree(vector<int>& inorder, int inbegin, int inend, vector<int>& postorder, int& pi)
	{
		if (inbegin > inend)
		{
			return nullptr;
		}
		TreeNode* root = new TreeNode(postorder[pi]);
		pi--;
		//将该子树的中序遍历序列划分为其左子树和右子树的中序遍历序列
		int rooti = inbegin;
		while (inorder[rooti] != root->val)
		{
			rooti++;
		}
		root->right = _buildTree(inorder, rooti + 1, inend, postorder, pi);
		root->left = _buildTree(inorder, inbegin, rooti - 1, postorder, pi);
		return root;
	}
	TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
	{
		int i = postorder.size() - 1; 
		return _buildTree(inorder, 0, inorder.size() - 1, postorder, i);
	}
};

8.非递归实现二叉树的前序遍历

cpp 复制代码
struct TreeNode 
{
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode() : val(0), left(nullptr), right(nullptr) {}
      TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
      TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 };
class Solution
{
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        vector<int> v;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (!st.empty() || cur != nullptr)
        {
            //准备开始访问一棵树
            //访问左路结点
            //左路结点的右子树
            while (cur != nullptr)
            {
                v.push_back(cur->val);
                st.push(cur);
                cur = cur->left;
            }
            //左路结点右子树
            TreeNode* top = st.top();
            st.pop();
            //循环子问题访问右子树
            cur = top->right;

        }
        return v;
    }
};

9.非递归实现二叉树的中序遍历

cpp 复制代码
 struct TreeNode
 {
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode() : val(0), left(nullptr), right(nullptr) {}
      TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
      TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 };
class Solution 
{
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
        vector<int> v;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur || !st.empty())
        {
            while (cur)
            {
                st.push(cur);
                cur = cur->left;
            }
            TreeNode* top = st.top();
            st.pop();

            v.push_back(top->val);
            
            //访问右子树
            cur = top->right;
        }
        return v;
    }
};

10.非递归实现二叉树的后序遍历

cpp 复制代码
struct TreeNode
{
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode() : val(0), left(nullptr), right(nullptr) {}
      TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
      TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution
{
public:
    vector<int> postorderTraversal(TreeNode* root)
    {
        vector<int> v;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* prev = nullptr;
        while (cur || !st.empty())
        {
            while (cur)
            {
                st.push(cur);
                cur = cur->left;
            }

            TreeNode* top = st.top();
            //1.top的右子树为空或者右子树已经访问过了(上一个访问的结点是右子树的根)
            //那么说明右子树不用访问或者访问过了,可以访问根top
            //2.右子树不为空且没有访问,则迭代子问题访问
            if (top->right == nullptr || top->right == prev)
            {
                st.pop();
                v.push_back(top->val);
                prev = top;
            }
            else
            {
                cur = top->right;
            }
        }
        return v;
    }
};

11.前K个高频单词

cpp 复制代码
class Solution 
{
public:
    struct Compare
    {
        bool operator()(const pair<int, string>& l, const pair<int, string>& r)
        {
            return l.first > r.first;
        }
    };
public:
    vector<string> topKFrequent(vector<string>& words, int k)
    {
        map<string, int> countMap;
        for (auto& str : words)
        {
            countMap[str]++;
        }
        vector<pair<int, string>> v;
        for (auto& kv : countMap)
        {
            v.push_back(make_pair(kv.second, kv.first));
            cout << kv.first << ":" << kv.second << endl;
        }
        stable_sort(v.begin(), v.end(), Compare());

        vector<string> ret;
        for (int i = 0; i < k; i++)
        {
            ret.push_back(v[i].second);
        }
        return ret;
    }
};

12.在长度2N的数组中找出重复N次的元素

cpp 复制代码
class Solution 
{
public:
	int repeatedNTimes(vector<int>& A) 
	{
		size_t N = A.size() / 2;
		// 用unordered_map统计每个元素出现的次数
		unordered_map<int, int> m;
		for (auto e : A)
		{
			m[e]++;
		}

		// 找出出现次数为N的元素
		for (auto& e : m)
		{
			if (e.second == N)
			{
				return e.first;
			}
		}
		return -1;
	}
};

13.两个数组的交集I

cpp 复制代码
class Solution
{
public:
	vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {

		// 用unordered_set对nums1中的元素去重
		unordered_set<int> s1;
		for (auto e : nums1)
		{
			s1.insert(e);
		}

		// 用unordered_set对nums2中的元素去重
		unordered_set<int> s2;
		for (auto e : nums2)
		{
			s2.insert(e);
		}
		// 遍历s1,如果s1中某个元素在s2中出现过,即为交集
		vector<int> vRet;
		for (auto e : s1)
		{
			if (s2.find(e) != s2.end())
			{
				vRet.push_back(e);
			}
		}
		return vRet;
	}
};

14.两个数组的交集II

cpp 复制代码
//哈希表
class Solution 
{
public:
	vector<int> intersect(vector<int>& nums1, vector<int>& nums2) 
	{
		//为了降低空间复杂度,应该先将元素个数较少的数组放入哈希表
		if (nums1.size() > nums2.size())
		{
			return intersect(nums2, nums1);
		}
		//1、将nums1中的每个数字以及对应出现的次数放入哈希表中
		unordered_map<int, int> um;
		for (auto e : nums1)
		{
			um[e]++;
		}
		//2、遍历nums2,找出nums1和nums2的交集
		vector<int> vRet;
		for (auto e : nums2)
		{
			if (um.count(e)) //该元素在哈希表中
			{
				vRet.push_back(e); //该元素属于交集
				um[e]--; //减少该元素在哈希表中出现的次数
				if (um[e] == 0) //该元素的次数已经变为0了
				{
					um.erase(e); //将该元素从哈希表中删除
				}
			}
		}
		return vRet;
	}
};

15.两句话中的不常见单词

cpp 复制代码
class Solution 
{
public:
	vector<string> uncommonFromSentences(string s1, string s2) 
	{
		unordered_map<string, int> um;
		//1、将两个字符串拼接起来,中间加一个" "字符串
		string str = s1 + " " + s2;

		//2、将拼接后字符串当中的单词放入哈希表
		size_t start = 0; //字头
		size_t pos = str.find(' '); //字尾
		while (pos != string::npos) //直到找到字符串结尾
		{
			string word = str.substr(start, pos - start); //将单词提取出来
			um[word]++; //将单词放入哈希表
			start = pos + 1; //更新字头
			pos = str.find(' ', start); //更新字尾
		}
		//将最后一个单词放入哈希表
		string word = str.substr(start);
		um[word]++;

		//3、找出哈希表中只出现过一次的单词,即不常见单词
		vector<string> vRet;
		for (auto e : um)
		{
			if (e.second == 1) //只出现过一次的单词
			{
				vRet.push_back(e.first);
			}
		}
		return vRet;
	}
};
相关推荐
绝无仅有4 小时前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有4 小时前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
AAA梅狸猫5 小时前
Looper.loop() 循环机制
面试
AAA梅狸猫5 小时前
Handler基本概念
面试
Gorway5 小时前
解析残差网络 (ResNet)
算法
Wect5 小时前
浏览器缓存机制
前端·面试·浏览器
拖拉斯旋风5 小时前
LeetCode 经典算法题解析:优先队列与广度优先搜索的巧妙应用
算法
Wect5 小时前
LeetCode 207. 课程表:两种解法(BFS+DFS)详细解析
前端·算法·typescript
掘金安东尼6 小时前
Fun with TypeScript Generics:玩转 TS 泛型
前端·javascript·面试
掘金安东尼6 小时前
Next.js 企业级落地
前端·javascript·面试