剑指offer - 举例让抽象具体化
1.包含 min 函数的栈
问题描述:
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数(时间复杂度应为 O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用 pop()或者 min()或者 top()方法。
const dataStack = []; // 栈
const minStack = []; // 存储栈中最小的元素
function push(node) {
// write code here
dataStack.push(node);
const length = minStack.length;
if (!length) {
minStack.push(node);
} else if (node <= minStack[length - 1]) {
//每当push一个node,判断这个node:如果不大于minStack中最后一个值,
//就存入minStack,等号是为了防止push栈中相同的值
minStack.push(node);
}
}
function pop() {
// write code here
if (dataStack[dataStack.length - 1] === minStack[minStack.length - 1]) {
minStack.pop();
}
return dataStack.pop();
}
function top() {
// write code here
//top()只是返回栈顶元素,不删除这个元素
return dataStack[dataStack.length - 1];
}
function min() {
// write code here
return minStack[minStack.length - 1];
}
2.栈的压入、弹出序列
问题描述:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
解题思路:
需要一个辅助栈,来模拟出入栈的过程。算法流程如下:
- 取压入队列的首元素,将其压入辅助栈
- 检查辅助栈顶元素是否和弹出队列的首元素相等:
- 若相等,则辅助栈弹出栈顶元素,弹出队列取出队首元素,重复检查
- 若不相等,回到第一步
- 最后,检查辅助栈和弹出队列是否均为空。
时间复杂度是 O(N^2),空间复杂度是 O(N)。
function IsPopOrder(pushV, popV) {
// write code here
var stack = [];
for (let i = 0; i < pushV.length; i++) {
var ele = pushV[i];
if (ele !== popV[0]) {
stack.push(ele);
} else {
popV.shift();
while (popV.length && stack[stack.length - 1] === popV[0]) {
popV.shift();
stack.pop();
}
}
}
return popV.length === 0 && stack.length === 0;
}
3.从上往下打印二叉树
从上往下打印出二叉树的每个节点,同层节点从左至右打印。空二叉树返回 false。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function PrintFromTopToBottom(root) {
// write code here
const nodes = []; //存放节点
const values = []; //存放节点的val
if (root === null) {
return false;
}
nodes.push(root);
while (nodes && nodes.length > 0) {
var node = nodes.shift();
values.push(node.val);
if (node.left) {
nodes.push(node.left);
}
if (node.right) {
nodes.push(node.right);
}
}
return values;
}
4.二叉搜索树的后序遍历序列
问题描述:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出 true,否则输出 false。假设输入的数组的任意两个数字都互不相同。
/*
在二叉搜索树中:
1.若任意结点的左子树不空,则左子树上所有结点的值均不大于它的根结点的值。
2. 若任意结点的右子树不空,则右子树上所有结点的值均不小于它的根结点的值。
3.任意结点的左、右子树也分别为二叉搜索树。
*/
function VerifySquenceOfBST(sequence) {
// write code here
if (!sequence || !sequence.length) {
return false;
}
return __VerifySquenceOfBST(sequence);
}
function __VerifySquenceOfBST(sequence) {
const len = sequence.length;
if (len < 2) return true;
const root = sequence[len - 1];
let i = 0;
for (; i < len - 1 && sequence[i] < root; i++) {} //left的数量是i,0 -> i-1
for (let j = i; j < len - 1; j++) {
if (sequence[j] < root) {
return false;
}
}
return (
__VerifySquenceOfBST(sequence.slice(0, i)) &&
__VerifySquenceOfBST(sequence.slice(i, len - 1))
);
}
5.二叉树中和为某一值的路径
问题描述:
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的 list 中,数组长度大的数组靠前)
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function FindPath(root, expectNumber) {
// write code here
var result = [];
if (root === null) {
return result;
}
dfsFind(root, expectNumber, [], 0, result);
return result;
}
function dfsFind(root, expectNumber, path, sum, result) {
sum += root.val;
path.push(root.val);
if (sum === expectNumber && root.left === null && root.right === null) {
result.push(path.slice(0)); //不知道为什么不能直接push(path),想不通
}
if (root.left !== null) {
dfsFind(root.left, expectNumber, path, sum, result);
}
if (root.right !== null) {
dfsFind(root.right, expectNumber, path, sum, result);
}
path.pop();
}