《剑指offer》 26~30

面试题26:树的子结构

#include<iostream>
using namespace std;

//题目:树的子结构
//输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
//B是A的子结构, 即A中有出现和B相同的结构和节点值。

//思路:
/*
* 这类问题其实归根结底是递归问题,什么时候可以继续比较,什么时候返回错误
* 首先从根节点开始,比较A树和B树的根节点值是否一样,如果一样,则说明B有可能是A子树
* 而如果不一样,则说明B不是A的子树,但可以继续比较B和A根节点的左右子节点是否一致
* 如果当前比较的A树节点和B树节点一样,则继续比较其左右子节点是否一致
* 当然会有特殊的情况出现,比如当B树的节点为空时,说明此时其所有的节点都已经比较过且一致,否则是走不到这一步的
* 也即是说B是A的子树,而如果A树的节点为空,则说明未完全匹配上,A树就到了叶子节点,即匹配失败
* 代码由主函数isSubStructure函数和BSubOfA函数组成
* 其中isSubStructure中,首先对两树根节点进行判断,只要有一个为空,则返回错误,随后则分别调用BSubOfA和isSubStructure函数
* 分别以A树的根节点、根节点左右子节点为根,和B树相匹配
* 到了函数BSubOfA中,首先就需要判断当前A树的节点和B树的节点是否为空
* 若B树节点为空,说明匹配完毕,返回true;若A树节点为空,说明未匹配完成就到了A树叶子节点,匹配失败,返回false
* 随后比较两节点值,若不等,返回false;若相等,则继续比较两节点的左右子节点

* 这道题的重点就在于对问题的分解,将匹配的过程分成两块,其中主函数控制所比较的两个根节点的变化
* 子函数则只管进行比较,这样一来思路就会清晰一些
*/

//牛客:https://www.nowcoder.com/practice/6e196c44c7004d15b1610b9afca8bd88?tpId=13&&tqId=11170&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
//leetcode:https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

bool BSubOfA(TreeNode* root1, TreeNode* root2) {
	if (root2 == nullptr)
		return true;
	if (root1 == nullptr)
		return false;
	if (root1->val != root2->val)
		return false;
	return BSubOfA(root1->left, root2->left) && BSubOfA(root1->right, root2->right);
}

bool isSubStructure(TreeNode* A, TreeNode* B) {
	if (A == nullptr || B == nullptr)
		return false;
	return BSubOfA(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B);

}

面试题27:二叉树镜像

#include<iostream>
using namespace std;

//题目:二叉树镜像
//请完成一个函数,输入一个二叉树,该函数输出它的镜像。

//思路:
/*
* 这道题的思路很简单,从根节点开始,分别交换所有的左右子节点即可,直到节点为空止
* 没有太多需要注意的,按照以下流程递归做DFS即可
* 首先判断当前节点是否为空 若为空则直接返回空
* 否则交换该节点的左右子节点
* 再分别调用函数处理节点的左右子节点即可
* 最后返回根节点
*/

//leetcode:https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/
//牛客:https://www.nowcoder.com/practice/6e196c44c7004d15b1610b9afca8bd88?tpId=13&&tqId=11170&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

TreeNode* mirrorTree(TreeNode* root) {
	if (root == nullptr)
		return nullptr;
	TreeNode* pNode = root->left;
	root->left = root->right;
	root->right = pNode;
	mirrorTree(root->left);
	mirrorTree(root->right);
	return root;
}

面试题28:对称的二叉树

#include<iostream>
using namespace std;

//题目:对称的二叉树
//请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

//思路:
/*
* 这道题和27有点像 不过27是对二叉树自己进行镜像,本题是判断二叉树是不是镜像的
* 自己跟自己比总归不太方便,可以把这个二叉树复制一份,并翻转成其镜像
* 那么接下来的工作就变成了:判断两个二叉树是否相同,复制并翻转后,节点的左右子树颠倒,其左子节点就是翻转前的右子节点
* 右子节点就是翻转前的左子节点,这样理解代码也能轻松一些
* 主函数isSymmetric里的一个参数为什么变成子函数的两个参数也就说得通了
* 在子函数中则进行了具体的对比,首先分析节点为空的情况,两节点同时为空时,说明二叉树为空或者已经匹配完毕,返回true
* 当有一个为空时,则表明不匹配,返回false
* 随后比较节点值是否一样,不一样也返回false,节点值一样时,则继续
* 再分别判断节点1的左子节点与节点2的右子节点(因为翻转) 以及节点1的右子节点与节点2的左子节点(因为翻转)
*/

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
bool isSymCore(TreeNode *pRoot1, TreeNode *pRoot2) {
	if (pRoot1 == nullptr&&pRoot2 == nullptr)
		return true;
	if (pRoot1 == nullptr || pRoot2 == nullptr)
		return false;
	if (pRoot1->val != pRoot2->val)
		return false;
	return isSymCore(pRoot1->left, pRoot2->right) && isSymCore(pRoot1->right, pRoot2->left);
}

bool isSymmetric(TreeNode* root) {
	return isSymCore(root, root);
}

面试题29:顺时针打印矩阵元素

#include<iostream>
#include<vector>
using namespace std;

//题目:顺时针打印矩阵
/*
* 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
* 输入:matrix = [[1, 2, 3, 4],
                  [5, 6, 7, 8],
				  [9,10,11,12]]
* 输出:[1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]
*/

//思路:
/*
* 首先要看到,矩阵的打印是一层一层来完成的,如例,第一层为[1,2,3,4,8,12,11,10,9,5],第二层则为[6,7]
* 所以要考虑“分层”的方式来打印
* 那么具体需要分几层呢?分层数应该是max{rows,cols}/2,对于例子中的3*4矩阵,分层为2
* 每层的打印分四步来进行:
* 第1步:水平方向向右,第1层水平方向下标为从0到cols-1,第i层水平方向下标为i-1到cols-i
* 第2步:垂直方向向下,需要注意的是,当前层的右上角已经在上一步打印过
*        第1层第2步垂直方向下标为1到rows-1,第i层第2步垂直方向下标为i到rows-i
* 第3步:水平方向向左,需要注意,当前层右下角已经在上一步打印过
         第1层第3步水平方向下标为cols-2到0,第i层第3步水平方向下标为cols-i-1到i-1
* 第4步:垂直方向向上,需要注意,当前层的左上和左下角已经分别在第1步和第3步打印过
         第1层第4步垂直方向下标为rows-2到1,第i层第4步垂直方向下标为rows-i-1到i
* 具体的代码如下 当然第2、3、4步在打印前需要先判断,因为并不是每层的打印都需要四步
* 也可能只需要1步、2步或3步
*/

vector<int> spiralOrder(vector<vector<int>>& matrix) {
	vector<int> res;
	if (matrix.size() == 0 || matrix[0].size() == 0)
		return {};
	int rows = matrix.size();
	int cols = matrix[0].size();
	int start = 0;
	while (cols>start * 2 && rows>start * 2) {
		int endX = cols - 1 - start;
		int endY = rows - 1 - start;
		for (int i = start; i <= endX; i++)
			res.push_back(matrix[start][i]);
		if (start<endY) {
			for (int i = start + 1; i <= endY; i++)
				res.push_back(matrix[i][endX]);
		}
		if (start<endY&&start<endX) {
			for (int i = endX - 1; i >= start; i--)
				res.push_back(matrix[endY][i]);
		}
		if (start + 1<endY&&start<endX) {
			for (int i = endY - 1; i>start; i--)
				res.push_back(matrix[i][start]);
		}
		start++;
	}
	return res;
}

面试题30:包含min函数的栈

#include<iostream>
#include<stack>
using namespace std;

//题目:包含min的栈
/*
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,
调用 min、push 及 pop 的时间复杂度都是 O(1)。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.min();   --> 返回 -2.
*/

//思路:
/*
* 要想实现min函数,就要额外构造一个辅助栈,该栈中元素从栈底到栈顶是非严格递减的
* 辅助栈的每个元素都对应着以原栈中相同位置为栈顶时的栈中最小元素
* 当使用push将新元素加入时,需要在辅助栈中也加入相应元素
* 若新元素比原栈最小值,即s_min栈顶元素小时,直接在s_min栈中插入新元素
* 否则,需要再次插入原栈最小值,即插入s_min栈顶元素
*/

class MinStack {
public:
	stack<int> s_data;
	stack<int> s_min;
	/** initialize your data structure here. */
	MinStack() {}
	void push(int x) {
		s_data.push(x);
		if (!s_min.empty() && s_min.top()<x) {
			s_min.push(s_min.top());
		}
		else
			s_min.push(x);
	}
	void pop() {
		s_data.pop();
		s_min.pop();
	}
	int top() {
		return s_data.top();
	}
	int min() {
		return s_min.top();
	}
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值