原题链接:
- 第一题:顺时针打印矩阵;
- 第二题:包含min函数的栈;
- 第三题:栈的压入、弹出序列;
- 第四题:从上往下打印二叉树;
- 第五题:二叉搜索树的后序遍历序列;
- 第六题:二叉树中和为某一值的路径;
第一题:顺时针打印矩阵
题目:
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
解析:
这题没什么好说的,具体看代码,考察代码的实现。
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> ret;
for (int x = 0, y = 0, up = 0, down = matrix.size() - 1, left = 0, right = matrix[0].size() - 1, f = 0;
left <= right && up <= down; ++y, ++x, ++left) {
for (; left <= right && up <= down && y <= right; ret.push_back(matrix[x][y++]));
for (++x, --y, ++up; left <= right && up <= down && x <= down; ret.push_back(matrix[x++][y]));
for (--y, --x, --right; left <= right && up <= down && y >= left; ret.push_back(matrix[x][y--]));
for (--x, ++y, --down; left <= right && up <= down && x >= up; ret.push_back(matrix[x--][y]));
}
return ret;
}
};
第二题:包含min函数的栈
题目:
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
解析:
这题要用可持久化的数据结构,也就是说当从栈中弹出元素时,要在 O(1) 的时间复杂度内得到最小值。那么只能空间换时间了;
设置一个数据栈,一个辅助栈,辅助栈中存储的是每一步的最小值,当前状态的最小值就是辅助栈的栈顶元素,即
- 把元素压入数据栈时,比较辅助栈栈顶元素和压入的元素的大小,如果压入的元素比栈顶元素小,那么就把压入的元素压入辅助栈;如果压入的元素比栈顶元素大,那么就把栈顶元素再次压入辅助栈;
- 当从数据栈中弹出元素时,同时也要从辅助栈中弹出元素。
这样就完成了可持久化。
class Solution {
public:
stack<int> minStack, st;
void push(int value) {
st.push(value);
minStack.push(minStack.empty() || minStack.top() > value ? value : minStack.top());
}
void pop() {
st.pop();
minStack.pop();
}
int top() {
return st.top();
}
int min() {
return minStack.top();
}
};
第三题:栈的压入、弹出序列
题目:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列
1,2,3,4,5
是某栈的压入顺序,序列4,5,3,2,1
是该压栈序列对应的一个弹出序列,但4,3,5,1,2
就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
解析:
利用栈的先入后出这个性质,我们来模拟元素进栈就可以解决这个问题,具体做法如下:
- 根据进栈序列依次进栈;
- 进栈的过程中,判断栈顶元素时候和出栈序列相同,如果相同,那么就出栈,重复这个过程,直到不相同为止;
- 当所有元素都入栈后,判断栈是否为空,如果为空,那么出栈序列就是合法的。
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> st;
for (int i = 0, j = 0; i < (int)pushV.size(); i++)
for (st.push(pushV[i]); !st.empty() && j < (int)popV.size() && st.top() == popV[j]; st.pop(), ++j);
return st.empty();
}
};
第四题:从上往下打印二叉树
题目:
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
解析:
其实就是二叉树的层序遍历,利用一个队列就可以实现了,具体点说就是 BFS 。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> ret;
queue<struct TreeNode *> que;
que.push(root);
while (!que.empty()) {
auto now = que.front();
que.pop();
if (now == nullptr)
continue;
ret.push_back(now->val);
que.push(now->left);
que.push(now->right);
}
return ret;
}
};
第五题:二叉搜索树的后序遍历序列
题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
解析:
先说递归算法,利用排序二叉树的性质:左子树所有结点的值都小于根节点,右子树所有结点的值都大于根节点。
由于后序遍历序列的结果为 LeftRightRoot ,意思是前半部分是左子树后序遍历的结果,中间部分为右子树后序遍历的结果,最后一个元素是根结点;那么我们就递归判断序列是否满足这个性质就行了。
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
return sequence.size() == 0 ? false : judge(sequence, 0, sequence.size());
}
bool judge(vector<int> &sequence, int l, int r)
{
if (l == r)
return true;
int mid = l, last = 0;
for (; mid < r - 1 && sequence[mid] < sequence[r - 1]; ++mid);
for (last = mid; last < r - 1 && sequence[last] > sequence[r - 1]; ++last);
return last == r - 1 && judge(sequence, l, mid) && judge(sequence, mid, r - 1);
}
};
非递归做法也很好理解,还是利用上面说的二叉排序树和二叉排序树后序遍历序列的性质,只不过不再递归判断了,先看下图:
上图就是二叉排序树的后序遍历序列,首先要明确一点,从右往左每一个结点都是某个子树的根,如果我们从右往左(从左往右也行)判定每一个子树都是二叉排序树,整个树就是二叉排序树。
根据后序遍历序列的性质,
Right < Root, Left > Root
,这个时候往左移动一个结点,根节点变成了L'
,我们只需要判断L'
时候满足上述条件就可以了,但是中间这个点在代码中不好确定,不过没关系,我们利用右子树的结点均大于左子树的结点这条性质,中间这个点取左端点,这时候我们只是多判断了一些没必要的结点,但是代码就好实现多了。
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
if (sequence.size() == 0)
return false;
int last = sequence.size() - 1;
for (int i = 0; last != 0; last--, i = 0) {
for (; sequence[i] < sequence[last]; ++i);
for (; sequence[i] > sequence[last]; ++i);
if (i != last)
return false;
}
return true;
}
};
第六题:二叉树中和为某一值的路径
题目:
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
解析:
也有两种做法,递归的和非递归的,先说递归的。
DFS ,好像没什么可以说的,如果你不会 DFS ,说了也不好理解,如果你会,那么不用我说,你自己也能做出来这个题。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector<vector<int> > ret;
vector<int> path;
dfs(ret, root, expectNumber, path, 0, 0);
return ret;
}
void dfs(vector<vector<int> > &ret, TreeNode* root,int expectNumber, vector<int> &path, int len, int sum)
{
if (root == nullptr)
return ;
if (len == path.size())
path.push_back(root->val);
else
path[len] = root->val;
if (sum + root->val == expectNumber && root->left == nullptr && root->right == nullptr) {
vector<int> arr(len + 1);
for (int i = 0; i < len + 1; arr[i] = path[i], ++i);
ret.push_back(arr);
return ;
}
dfs(ret, root->left, expectNumber, path, len + 1, sum + root->val);
dfs(ret, root->right, expectNumber, path, len + 1, sum + root->val);
}
};
非递归的做法就是利用二叉树的后序遍历的非递归算法来做的,由于二叉树的后序遍历的非递归算法要借助辅助栈,当访问一个结点时,栈中的元素依次就是从根到达该结点的路径。二叉树后序遍历的非递归实现请看这里。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector<vector<int> > ret;
vector<pair<TreeNode*, int> > st;
TreeNode *p = root;
for (int sum = 0; p || !st.empty(); ) {
if (p) {
sum += p->val;
st.emplace_back(p, 1);
p = p->left;
} else {
auto now = st.back();
st.pop_back();
if (now.second == 1) {
st.emplace_back(now.first, 2);
p = now.first->right;
} else if (sum == expectNumber && now.first->left == nullptr && now.first->right == nullptr) {
vector<int> arr(st.size() + 1);
for (int i = 0; i < (int)st.size(); arr[i] = st[i].first->val, ++i);
ret.push_back((arr[st.size()] = now.first->val, arr));
sum -= now.first->val;
} else
sum -= now.first->val;
}
}
return ret;
}
};