《剑指offer》 31~35题

面试题31:栈的压入 弹出序列

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

//题目:栈的压入 弹出序列
/*
* 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。
* 假设压入栈的所有数字均不相等。
* 例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,
* 但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
* 示例:
* 输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
* 输出:true
* 解释:我们可以按以下顺序执行:
* push(1), push(2), push(3), push(4), pop() -> 4,
* push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
*/

/*思路:
* 如何判断栈的压入序列和弹出序列?
* 设置两个指针,初始时分别指向压入序列和弹出序列的第一个元素
* 从压栈序列开始遍历,首先将压栈序列的第一个元素压入栈中,压栈序列指针前进
* 随后开始比较,如果栈顶元素和弹出序列指针所指元素不同,就继续向将栈中插入压入序列,压栈序列指针前进
* 除非压栈序列所有元素均已被压入,或栈顶元素和弹出序列指针所指元素相同 进行下一步
* 若此时元素仍不同,则说明不匹配,返回false,若元素相同,则更新弹出序列指针
* 将匹配元素从栈中弹出,并继续循环,直到压入序列遍历完毕
* 最后检查栈是否为空,若为空 说明匹配成功,若非空,则匹配失败
*/

//leetcode:https://leetcode-cn.com/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof/
//牛客:https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
	if (pushed.size() == 0 || popped.size() == 0)
		return true;
	int n = popped.size();
	int indexOfPush = 0;
	int indexOfPop = 0;
	stack<int> data;
	while (indexOfPop<popped.size()) {
		while (data.empty() || popped[indexOfPop] != data.top()) {
			if (indexOfPush == pushed.size())
				break;
			data.push(pushed[indexOfPush++]);
		}
		if (data.top() != popped[indexOfPop])
			return false;
		data.pop();
		indexOfPop++;
	}
	return data.empty();
}

面试题32-1:从上到下打印二叉树

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

//题目:从上到下打印二叉树
//从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

/*思路:
* 若想从上到下打印二叉树,类似于广度优先遍历,需要借助队列来完成
* 从根节点开始,先把根节点压入队列中,并进入循环
* 只要队列非空,就执行以下操作:
* 首先从队列中取出一个节点,打印该节点,如果该节点左子节点非空,则将左子节点入队列;如果右子节点非空,则将右子节点入队列
* 退出循环时,即已完成所有节点元素的顺序打印
*/

//leetcode:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/
//牛客:https://www.nowcoder.com/practice/7fe2212963db4790b57431d9ed259701?tpId=13&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
vector<int> levelOrder(TreeNode* root) {
	if (root == nullptr)
		return {};
	vector<int> res;
	queue<TreeNode*> q;
	TreeNode* pNode = root;
	q.push(root);
	while (!q.empty()) {
		TreeNode* pLast = q.front();
		q.pop();
		res.push_back(pLast->val);
		if (pLast->left)
			q.push(pLast->left);
		if (pLast->right)
			q.push(pLast->right);
	}
	return res;
}

面试题32-2:按层打印二叉树

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

//题目:按层打印二叉树
//从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

/*思路:
* 和前一小题类似,唯一的区别在于,这次不是只打印在一个vector中,而是要按层分开,每次循环中要进行计数
* 从根节点开始,先把根节点压入队列中,并进入循环
* 只要队列非空,就执行以下操作:
* 首先记录当前栈中的元素个数n,创建vector,并开始进入子循环,将以下步骤进行n次:
* 首先从队列中取出一个节点,打印该节点到vector中,如果该节点左子节点非空,则将左子节点入队列;如果右子节点非空,则将右子节点入队列
* 子循环完毕,将该vector保存下来,继续循环
* 当队列为空时说明打印完毕,退出
*/

//leetcode:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof/
//牛客:https://www.nowcoder.com/practice/445c44d982d04483b04a54f298796288?tpId=13&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
vector<vector<int>> levelOrder(TreeNode* root) {
	if (root == nullptr)
		return {};
	vector<vector<int>> res;
	TreeNode *pNode = root;
	queue<TreeNode*> q;
	q.push(pNode);
	while (!q.empty()) {
		int n = q.size();
		vector<int> tmp;
		for (int i = 0; i<n; i++) {
			TreeNode *pLast = q.front();
			tmp.push_back(pLast->val);
			q.pop();
			if (pLast->left)
				q.push(pLast->left);
			if (pLast->right)
				q.push(pLast->right);
		}
		res.push_back(tmp);
	}
	return res;
}

面试题32-3:z字形打印二叉树

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

//题目:z字形打印二叉树
//从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

/*思路:
* 与前面不同的是,本小题要求z字形打印,即并不是每层都是从左到右打印
* 奇数层为从左到右,偶数层则为从右到左,所以重点就在于如何能够保证奇偶层不同的打印效果
* 从右到左 和 从右到左 需要“两个翻转”,其一为当其父节点将两个兄弟节点加入时对奇偶层的不同处理
* 其二是借助栈来实现反序
* 举个例子 根节点值为1,左右子节点值分别为2和3,左子节点2的两个子节点值为4和5,右子节点3的两个子节点值为6和7
* 第一层只有一个节点1,先将节点1放入栈1中,开始循环
* 打印1的同时,将2和3(先左后右)送入了栈2中,第一次子循环完毕,vector中只有1一个元素 [1]
* 第二次子循环,从栈2中取出3和2(和入栈顺序相反),并打印到vector中,vector中有两个元素 [3,2]
* 同时分别将节点3和节点2的左右子节点放到栈1中,这里要注意,对于每个节点,是先右后左放入节点的子节点
* 因为只有这样才能保证顺序 栈2从栈底到栈顶分别为[7,6,5,4]
* 第三次子循环,从栈1中依次弹出[4,5,6,7],这些节点的子节点都为空,所以不会再有元素进入栈中
* 第三次子循环完毕,vector中为[4,5,6,7]
* 此后栈1和栈2皆为空,打印完毕

* 相比于前面两小题,这一题的难点就在于如何实现从左到右和从右到左的变化 要通过栈的后进先出
* 以及对奇偶层不同的策略来实现,对于奇数层,先右子树后左子树,对于偶数层,先左子树后右子树
*/

//leetcode:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof/
//牛客:https://www.nowcoder.com/practice/91b69814117f4e8097390d107d2efbe0?tpId=13&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking

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

vector<vector<int>> levelOrder(TreeNode* root) {
	if (root == nullptr)
		return {};
	TreeNode *pNode = root;
	vector<vector<int>> res;
	stack<TreeNode*> q1;
	stack<TreeNode*> q2;
	q1.push(pNode);
	while (!q1.empty() || !q2.empty()) {
		vector<int> tmp;
		if (q1.empty()) {
			int n = q2.size();
			for (int i = 0; i<n; i++) {
				TreeNode *pLast = q2.top();
				q2.pop();
				tmp.push_back(pLast->val);
				if (pLast->right)
					q1.push(pLast->right);
				if (pLast->left)
					q1.push(pLast->left);
			}
		}
		else {
			int n = q1.size();
			for (int i = 0; i<n; i++) {
				TreeNode *pLast = q1.top();
				q1.pop();
				tmp.push_back(pLast->val);
				if (pLast->left)
					q2.push(pLast->left);
				if (pLast->right)
					q2.push(pLast->right);
			}
		}
		res.push_back(tmp);
	}
	return res;
}

面试题33:二叉搜索树的后序遍历

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

//题目:二叉搜索树的后序遍历
//输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。
//如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

/*思路:
* 二叉搜索树的特点就是 当前节点的值大于所有左子节点,且小于所有右子节点
* 先取由右边界处的元素,即当前数组的最后一个元素值为根节点的值
* 随后从左边界开始,找到第一个比根节点值大的数,并记录此处的下标cutPos,如果遍历结果正确,该数开始就应该是根节点的右子节点
* 即从此处到右边界-1的位置,应该都大于根节点值,以此为衡量准则,进行判断,如果最终没有到达右边界
* 则说明不满足,否则,就继续一分为二进行判断,原问题转换为两个规模更小的问题
* 最终结果即为判断这两个小问题得到的结果相与
*/

//leetcode:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/
//牛客:https://www.nowcoder.com/practice/a861533d45854474ac791d90e447bafd?tpId=13&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking

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

bool verifyCore(vector<int>& postorder, int l, int r) {
	int rootVal = postorder[r];
	int idx = l;
	while (idx<r&&postorder[idx]<rootVal)
		idx++;
	int cutPos = idx;
	while (idx<r&&postorder[idx]>rootVal)
		idx++;
	if (idx != r)
		return false;
	bool res = true;
	if (cutPos>l)
		res &= verifyCore(postorder, l, cutPos - 1);
	if (cutPos<r)
		res &= verifyCore(postorder, cutPos, r - 1);
	return res;
}

bool verifyPostorder(vector<int>& postorder) {
	if (postorder.size() == 0)
		return true;
	return verifyCore(postorder, 0, postorder.size() - 1);
}

面试题34:二叉树中和为某一值的路径

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

//题目:二叉树中和为某一值的路径
//输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。
//从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。

/*思路:
* 该问题的实质是深度优先遍历,也就是说我只要把所有的路径都走一遍,就能得到最终的结果
* 但是一个一个试肯定是不现实的,所以可以考虑采用递归的方式来实现:
* 首先设定好用于存储数值的结构,然后开始遍历
* 从根节点开始,每到一个点,先用预设值rest减去该点的值,随后问题变成表示从该点出发到叶子节点路径总和能否为 rest-当前节点值 的问题
* 首先需要将节点的值加入到临时数组tmp中,注意,这个临时数组的“出出进进”,替我们完成了路径上各节点值的拾取
* 随后判断:如果此时节点正好为叶子节点(左右子节点为空)且此时rest正好为零,则此时的tmp中存在一条符合条件的路径
* 将其保存进最终结果
* 否则,若其左右子节点非空,进入递归
* 最后,需要把加入tmp的该节点从tmp中弹出,这表示“这条路不合适,不从这走了”
* 那么需要把减掉数值的rest再加回来吗?答案是不需要,因为rest是值传递,其值的变化只体现在当前的函数中
*/

//leetcode:https://leetcode-cn.com/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof/
//牛客:https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca?tpId=13&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

vector<vector<int>> res;
vector<int> tmp;
vector<vector<int>> pathSum(TreeNode* root, int sum) {
	if (root == nullptr)
		return {};
	getPath(root, sum);
	return res;
}
void getPath(TreeNode* pNode, int rest) {
	rest -= pNode->val;
	tmp.push_back(pNode->val);
	if (rest == 0 && pNode->left == nullptr&&pNode->right == nullptr)
		res.push_back(tmp);
	if (pNode->left)
		getPath(pNode->left, rest);
	if (pNode->right)
		getPath(pNode->right, rest);
	tmp.pop_back();
}

面试题35:复杂链表的复制

#include<iostream>
using namespace std;

//题目:复杂链表的复制
//请实现 copyRandomList 函数,复制一个复杂链表。
//在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

/*思路:
* 该题代码写出来很多,但其实很简单,难点就在于对random指针的复制,这个问题无法直接来解决
* 所以,将整个复制分成三部分:
* 第一部分,先忽略random指针的指向,将原链表复制一份,比如原来链表为1->2->3,复制后变为1->1->2->2->3->3
* 第二部分,处理random指针的指向,第一步完成后,原链表扩展,但复制节点的random并没有指向,只有原节点有
  按照以下方法来复制原节点的random指针:
  原节点的next指针(指向复制而来、与该节点一样的新节点)的random指针指向  原节点random指针的next节点
* 第三部分则是将这个大链表一分为二,就能得到一个新的复制而来的节点

* 该题的整个复制步骤还是比较繁琐的,注意千万不要出错
*/

//leetcode:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/
//牛客:https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba?tpId=13&&tqId=11178&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

class Node {
public:
	int val;
	Node* next;
	Node* random;
	Node(int _val):val(_val), next(nullptr), random(nullptr) {} 
};

//第一步,复制节点
void copyNode(Node* head) {
	Node *pNode = head;
	while (pNode != nullptr) {
		Node *pNext = pNode->next;
		pNode->next = new Node(pNode->val);
		pNode->next->next = pNext;
		pNode = pNext;
	}
}

//第二步,复制random指针
void copyRandomPtr(Node *head) {
	Node *pNode = head;
	while (pNode != nullptr) {
		Node *pNext = pNode->next;
		if (pNode->random != nullptr)
			pNext->random = pNode->random->next;
		pNode = pNext->next;
	}
}

//第三步,拆分链表
Node* divideList(Node *head) {
	Node *newHead = nullptr;
	Node *pNewNode = nullptr;
	Node *pNode = head;
	if (pNode != nullptr) {
		newHead = pNode->next;
		pNewNode = newHead;
		pNode->next = pNewNode->next;
		pNode = pNode->next;
	}
	while (pNewNode != nullptr&&pNode != nullptr) {
		pNewNode->next = pNode->next;
		pNewNode = pNewNode->next;
		pNode->next = pNewNode->next;
		pNode = pNode->next;
	}
	return newHead;
}
Node* copyRandomList(Node *head) {
	copyNode(head);
	copyRandomPtr(head);
	return divideList(head);
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值