链表
从尾到头打印链表
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入{1,2,3}的链表如下图:
返回一个数组为[3,2,1]
输入:{1,2,3}
返回值:[3,2,1]
function printListFromTailToHead(head)
{
let res = [];
while(head){
res.unshift(head.val)
head = head.next
}
return res
}
反转链表
输入: {1,2,3}
返回值:{3,2,1}
function ReverseList(head)
{
let temp = new ListNode()
let next = null;
while(head){
next = head.next;
head.next = temp.next;
temp.next = head;
head = next
}
return temp.next
}
合并两个排序的链表
function Merge(pHead1, pHead2)
{
if(pHead1 === null) return pHead2;
if(pHead2 === null) return pHead1;
if(pHead1.val < pHead2.val){
pHead1.next = Merge(pHead1.next,pHead2)
return pHead1
}else{
pHead2.next = Merge(pHead1,pHead2.next)
return pHead2
}
}
两个链表的第一个公共结点
例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:
输入:{1,2,3},{4,5},{6,7}
输出:{6}
function FindFirstCommonNode(pHead1, pHead2)
{
if(!pHead1 || !pHead2) return null;
let p1 = pHead1;
let p2 = pHead2;
while(p1 != p2){
p1 = p1 === null ? pHead2 : p1.next;
p2 = p2 === null ? pHead1 : p2.next;
}
return p1
}
链表中倒数最后k个结点
例如输入{1,2,3,4,5},2时,对应的链表结构如下图所示:
其中蓝色部分为该链表的最后2个结点,所以返回倒数第2个结点(也即结点值为4的结点)即可,系统会打印后面所有的节点来比较。
输入:{1,2,3,4,5},2
返回值:{4,5}
注意:1.快指针走k步的时候注意判断fast是否为null
function FindKthToTail( pHead , k ) {
let fast = pHead;
let slow = pHead;
for(let i = 0; i < k; i++){
if(fast === null) return null;
fast = fast.next;
}
while(fast != null){
fast = fast.next;
slow = slow.next;
}
return slow
}
复杂链表的复制 hard
输入:{1,2,3,4,5,3,5,#,2,#}
返回值:{1,2,3,4,5,3,5,#,2,#}
删除链表中重复的结点(重复的也删除,感觉不会有人这样考)
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5 处理后为 1->2->5
输入:{1,2,3,3,4,4,5}
返回值:{1,2,5}
function deleteDuplication(pHead) {
root.next = pHead;
let r = root;
while (r.next != null && r.next.next != null) {
if (r.next.val == r.next.next.val) {
var temp = r.next.val;
while (r.next != null && r.next.val == temp) r.next = r.next.next;
} else {
r = r.next;
}
}
return root.next;
}
删除排序链表中的重复元素(保留一个重复的)
输入: head = [1,1,2]
输出: [1,2]
var deleteDuplicates = function(head) {
let cur = head;
while(cur && cur.next) {
if(cur.val == cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
};
删除链表的节点
输入: {2,5,1,9},5
输出: {2,1,9}
function deleteNode( head , val ) {
if(head === null ){return head}
head.next = deleteNode(head.next, val)
return head.val === val ? head.next : head
}
反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
输入: head = [1,2,3,4,5], left = 2, right = 4
输出: [1,4,3,2,5]
var reverseBetween = function(head, left, right) {
let temp = new ListNode();
temp.next = head;
let pre = temp;
for(let i = 0; i < left - 1; i++){
pre = pre.next;
}
let cur = pre.next;
for(let i = 0; i < right - left; i++){
const next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next = next;
}
return temp.next;
};
24. 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
输入: head = [1,2,3,4]
输出: [2,1,4,3]
var swapPairs = function(head) {
let temp = new ListNode();
temp.next = head;
let cur = temp;
while(cur.next != null && cur.next.next!=null){
let n1 = cur.next;
let n2 = cur.next.next;
n1.next = n2.next;
n2.next = n1;
cur.next = n2;
cur = n1;
}
return temp.next
};
二叉树
二叉树的深度
function TreeDepth(pRoot)
{
if(!pRoot) return 0;
let left = TreeDepth(pRoot.left);
let right = TreeDepth(pRoot.right);
return left>right?left+1:right+1
}
按之字形顺序打印二叉树(记住层序遍历)
给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
先求二叉树的层序遍历,再对数组下标对2求余数,余数不为0 就把数组reverse();返回arr。
function Print(pRoot) {
const arr = [];
das(pRoot, arr, 0);
for (i = 0; i < arr.length; i++) {
if (i % 2) {
arr[i].reverse();
}
}
return arr;
}
function das(root, arr, level) {
// 层序遍历
if (!root) return;
if (!arr[level]) {
arr[level] = [];
}
arr[level].push(root.val);
das(root.left, arr, level + 1);
das(root.right, arr, level + 1);
}
二叉树前中后层序遍历
//前序遍历:
var preorderTraversal = function(root, res = []) {
if (!root) return res;
res.push(root.val);
preorderTraversal(root.left, res)
preorderTraversal(root.right, res)
return res;
};
//中序遍历:
var inorderTraversal = function(root, res = []) {
if (!root) return res;
inorderTraversal(root.left, res);
res.push(root.val);
inorderTraversal(root.right, res);
return res;
};
//后序遍历:
var postorderTraversal = function(root, res = []) {
if (!root) return res;
postorderTraversal(root.left, res);
postorderTraversal(root.right, res);
res.push(root.val);
return res;
};
// 层序遍历
function das(root, arr, level) {
if (!root) return;
if (!arr[level]) {
arr[level] = [];
}
arr[level].push(root.val);
das(root.left, arr, level + 1);
das(root.right, arr, level + 1);
}
98. 验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树
输入: root = [2,1,3]
输出: true
思路:利用二叉搜索树的性质,每个节点都大于它左子树所有节点,小于右子树上所有节点,并且每个节点左右子树不为空,那它的左右子树也是二叉搜索树。我们可以递归验证每个节点的左右子树。
function helper(root,lower,higher){
if(root === null) return true;
if(root.val <= lower || root.val >= higher){
return false;
}
return helper(root.left,lower,root.val) && helper(root.right,root.val,higher)
}
var isValidBST = function(root) {
return helper(root,-Infinity,Infinity)
};
235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
分为三种情况,1.root节点大于p并且大于q,说明p和q都在root的左子树, 2.root节点小于p并且小于q,说明p和q都在root的右子树,3.其他情况比如root等于p或q,说明root就是公共祖先,前两种情况直接递归左右子树,第3种情况直接返回root
var lowestCommonAncestor = function(root, p, q) {
if(root === null) return root
if(root.val > p.val && root.val > q.val){
let left = lowestCommonAncestor(root.left , p, q)
return left !== null && left
}
if(root.val < p.val && root.val < q.val){
let right = lowestCommonAncestor(root.right, p, q)
return right !== null && right
}
return root
};
236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
分四种情况,1.root是null或者root等于p或q,说明root是p,q的公共祖先,2.递归左右子树,如果左右子树递归函数返回的都不为空,则root就是p,q的公共祖先,3.左子树递归函数返回的值为空,则p,q都在右子树,4.右子树递归函数返回的值为空,则p,q都在左子树
function travel(root,p,q){
// 确定递归终止条件
if(root === null || root === p || root === q) return root
// 确定递归单层逻辑
let left = travel(root.left, p, q)
let right = travel(root.right, p, q)
//如果在某一个节点的左右子树都能找到p和q说明这个节点就是公共祖先
if(left !== null && right !== null) return root
if(left === null) return right //如果左子树没找到就说明p,q都在右子树
if(right === null) return left //如果右子树没找到就说明p,q都在左子树
}
var lowestCommonAncestor = function(root, p, q) {
return travel(root,p,q)
};
112. 路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
递归左右子树,不断让sum减去当前节点的值。左右子树有一个返回true就找到了一条这样的路径
var hasPathSum = function(root, targetSum) {
if(root === null) return false
// 遇到叶子结点
if(root.left === null && root.right === null){
return targetSum - root.val === 0
}
// 遍历左右子树
return hasPathSum(root.left, targetSum-root.val) || hasPathSum(root.right, targetSum-root.val)
};
257. 二叉树的所有路径 (easy)
给你一个二叉树的根节点 `root` ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
输入: root = [1,2,3,null,5]
输出: ["1->2->5","1->3"]
递归左右子树,直到叶子节点,递归的过程中不断透传path,递归的每一层连接上当前节点的值
var binaryTreePaths = function(root) {
let arr = []
function dfs(root, path){
if(root){
path += root.val.toString()
// 如果是叶子结点就把结果放进去
if(root.left === null && root.right === null){
arr.push(path)
}else{
path += '->'
dfs(root.left,path)
dfs(root.right,path)
}
}
}
dfs(root,'')
return arr
};