二叉树的递归和层序遍历
层序遍历
function levelOrder(root: TreeNode | null): number[][] {
let helperQueue: TreeNode[] = [];
let ret: number[][] = [];
let curNode:TreeNode;
let tempArr:number[] = [];
if(root !== null) helperQueue.push(root);
while(helperQueue.length > 0){
for(let i = 0,len = helperQueue.length; i < len; i++){
curNode = helperQueue.shift()!;
tempArr.push(curNode.val);
if(curNode.left !== null){
helperQueue.push(curNode.left);
}
if(curNode.right !== null){
helperQueue.push(curNode.right);
}
}
ret.push(tempArr);
tempArr = [];
}
return ret;
};
翻转二叉树
递归
①出口:root为空是出口,此时终止;
②单层循环的逻辑:每次都借temp进行节点的交换。
前序遍历是先交换整个左右子树 后交换具体节点;
后序遍历是先交换子树的具体节点,再交换整颗子树;
后序遍历比较特殊,交换左子树的具体节点后,再交换左右子树,这样最后交换的就是原来的右子树,现在的左子树的具体节点。
代码重复性比较高,按思路拖一下就都ok了:
//递归-前序遍历
function invertTree(root: TreeNode | null): TreeNode | null {
if(root === null) return root;
let temp: TreeNode | null = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
};
//*递归中序遍历
function invertTree(root: TreeNode | null): TreeNode | null {
if(root === null) return root;
invertTree(root.left);
let temp: TreeNode| null = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
return root;
};
//递归-后序遍历
function invertTree(root: TreeNode | null): TreeNode | null {
if(root === null) return root;
invertTree(root.left);
invertTree(root.right);
let temp: TreeNode| null = root.left;
root.left = root.right;
root.right = temp;
return root;
};
迭代
可以使用栈或者队列来进行;
使用栈时,注意出栈顺序,根据出栈顺序的不同来区分前中后序遍历;
栈模拟中的中序遍历和后序遍历都需要添加null作为标记;
使用队列时,进行层序遍历的通用写法,最后一层的节点仍然要做交换,但没有左右节点继续压入队列了,队列为空时进行返回。
//栈模拟前序遍历
//根据栈先进后出的特性,不用使用null作为标记
function invertTree(root: TreeNode | null): TreeNode | null {
let tempStack: TreeNode[] = [];
let curNode: TreeNode | null = null;
let tempNode: TreeNode | null = null;
if(root !== null) tempStack.push(root);
while(tempStack.length > 0){
curNode = tempStack.pop();
//这里是先交换还是先入栈并不影响功能实现
tempNode = curNode.left;
curNode.left = curNode.right;
curNode.right = tempNode;
if(curNode.right) tempStack.push(curNode.right);
if(curNode.left) tempStack.push(curNode.left);
}
return root;
};
// 栈模拟二叉树中序遍历
function invertTree(root: TreeNode | null): TreeNode | null {
let tempStack: TreeNode[] = [];
let curNode: TreeNode | null = null;
let tempNode: TreeNode | null = null;
if (root !== null) tempStack.push(root);
while (tempStack.length > 0) {
curNode = tempStack.pop();
if (curNode !== null) {
if (curNode.right) tempStack.push(curNode.right);
tempStack.push(curNode);
tempStack.push(null);
//这里的 null作为标记使用
if (curNode.left) tempStack.push(curNode.left);
}
else {
//读取到null标记时进行交换
curNode = tempStack.pop();
tempNode = curNode.left;
curNode.left = curNode.right;
curNode.right = tempNode;
}
}
return root;
};
// 栈模拟二叉树后序遍历
// 就是中序遍历稍微交换一下位置
function invertTree(root: TreeNode | null): TreeNode | null {
let tempStack: TreeNode[] = [];
let curNode: TreeNode | null = null;
let tempNode: TreeNode | null = null;
if (root !== null) tempStack.push(root);
while (tempStack.length > 0) {
curNode = tempStack.pop();
if (curNode !== null) {
if (curNode.right) tempStack.push(curNode.right);
if (curNode.left) tempStack.push(curNode.left);
tempStack.push(curNode);
tempStack.push(null);
}
else {
curNode = tempStack.pop();
tempNode = curNode.left;
curNode.left = curNode.right;
curNode.right = tempNode;
}
}
return root;
};
//队列模拟层序遍历
//层序遍历加上题目要求的操作代码
function invertTree(root: TreeNode | null): TreeNode | null {
let tempQueue: TreeNode[] = [];
let tempNode: TreeNode | null;
let curNode: TreeNode | null;
if(root !== null) tempQueue.push(root);
while(tempQueue.length > 0){
for(let i = 0, len = tempQueue.length; i < len; i++){
curNode = tempQueue.shift();
tempNode = curNode.left;
curNode.left = curNode.right;
curNode.right = tempNode;
if(curNode.left) tempQueue.push(curNode.left);
if(curNode.right) tempQueue.push(curNode.right);
//最后一层的节点仍然要做交换,但没有左右节点继续压入队列了
}
}
return root;
};
对称二叉树
递归
终止条件
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
- 左右都不为空,比较节点数值,不相同就return false
此时左右节点不为空,且数值也不相同的情况我们也处理了。
执行的功能性代码
比较外层outside是否相等left->left === right->right
比较内层inside是否相等left->right === right->left
返回比较结果outside && inside
function isSymmetric(root: TreeNode | null): boolean {
// 递归写法
function compare(left: TreeNode | null, right: TreeNode | null): boolean {
if(left === null && right !== null) return false;
else if(left !== null && right === null) return false;
else if(left === null && right === null) return true;
else if(left.val !== right.val) return false;
let outside = compare(left.left, right.right);
let inside = compare(left.right, right.left);
return outside && inside;
}
if(root === null) return true;
return compare(root.left, root.right);
};
层序
这题栈和队列区别不大,因为出栈(出队列)时只需要保证同时出了两个数就可以,而出数顺序对结果无影响(我们只需要比较两数是不是相等)。
function isSymmetric(root: TreeNode | null): boolean {
// 队列模拟-迭代写法
let tempQueue: TreeNode[] = [];
let tempNode1: TreeNode | null;
let tempNode2: TreeNode | null;
if (root) {
tempQueue.push(root.left);
tempQueue.push(root.right);
}
while (tempQueue.length > 0) {
tempNode1 = tempQueue.shift();
tempNode2 = tempQueue.shift();
if (tempNode1 === null && tempNode2 === null) continue;
if (tempNode1 === null || tempNode2 === null) return false;
if (tempNode1.val !== tempNode2.val) return false;
tempQueue.push(tempNode1.left);
tempQueue.push(tempNode2.right);
tempQueue.push(tempNode1.right);
tempQueue.push(tempNode2.left);
}
return true;
};