⾸先要明确的是,数据结构是⼯具,算法是通过合适的⼯具解决特定问题的 ⽅法。也就是说,学习算法之前,最起码得了解那些常⽤的数据结构,了解 它们的特性和缺陷。
那么该如何在 LeetCode 刷题呢?之前的⽂章算法学习之路写过⼀些,什么 按标签刷,坚持下去云云。现在距那篇⽂章已经过去将近⼀年了,我不说那 些不痛不痒的话,直接说具体的建议:
先刷⼆叉树,先刷⼆叉树,先刷⼆叉树!
这是我这刷题⼀年的亲⾝体会,下图是去年⼗⽉份的提交截图:
为什么要先刷⼆叉树呢,因为⼆叉 树是最容易培养框架思维的,⽽且⼤部分算法技巧,本质上都是树的遍历问题。
刷⼆叉树看到题⽬没思路?根据很多读者的问题,其实⼤家不是没思路,只 是没有理解我们说的「框架」是什么。不要⼩看这⼏⾏破代码,⼏乎所有⼆ 叉树的题⽬都是⼀套这个框架就出来了。
void traverse(TreeNode root) {
// 前序遍历
traverse(root.left)
// 中序遍历
traverse(root.right)
// 后序遍历
}
⽐如说我随便拿⼏道题的解法出来,不⽤管具体的代码逻辑,只要看看框架 在其中是如何发挥作⽤的就⾏。
LeetCode 124 题,难度 Hard,让你求⼆叉树中最⼤路径和,主要代码如 下:
int ans = INT_MIN;
int oneSideMax(TreeNode* root) {
if (root == nullptr) return 0; int left = max(0, oneSideMax(root->left));
int right = max(0, oneSideMax(root->right));
ans = max(ans, left + right + root->val); return max(left, right) + root->val;
}
你看,这就是个后序遍历嘛。
LeetCode 105 题,难度 Medium,让你根据前序遍历和中序遍历的结果还原 ⼀棵⼆叉树,很经典的问题吧,主要代码如下:
TreeNode buildTree(int[] preorder, int preStart, int preEnd,
int[] inorder, int inStart, int inEnd, Map<Integer, Integer> inMa p) {
if(preStart > preEnd || inStart > inEnd)
return null; TreeNode root = new TreeNode(preorder[preStart]);
int inRoot = inMap.get(root.val);
int numsLeft = inRoot - inStart; root.left = buildTree(preorder, preStart + 1, preStart + numsLeft , inorder, inStart, inRoot - 1, inMap); root.right = buildTree(preorder, preStart + numsLeft + 1, preEnd, inorder, inRoot + 1, inEnd, inMap);
return root; }
不要看这个函数的参数很多,只是为了控制数组索引⽽已,本质上该算法也 就是⼀个前序遍历。 LeetCode 99 题,难度 Hard,恢复⼀棵 BST,主要代码如下:
void traverse(TreeNode* node) {
if (!node) return;
traverse(node->left);
if (node->val < prev->val) { s = (s == NULL) ? prev : s; t = node; }prev = node; traverse(node->right); }
这不就是个中序遍历嘛,对于⼀棵 BST 中序遍历意味着什么,应该不需要 解释了吧。 你看,Hard 难度的题⽬不过如此,⽽且还这么有规律可循,只要把框架写 出来,然后往相应的位置加东⻄就⾏了,这不就是思路吗。
更多java数据结构算法视频课程及学习资料请点击:java数据结构算法视频教程(左神算法、直通BAT校招算法面试题、图解算法等课程均有)
对于⼀个理解⼆叉树的⼈来说,刷⼀道⼆叉树的题⽬花不了多⻓时间。那么 如果你对刷题⽆从下⼿或者有畏惧⼼理,不妨从⼆叉树下⼿,前 10 道也许 有点难受;结合框架再做 20 道,也许你就有点⾃⼰的理解了;刷完整个专 题,再去做什么回溯动规分治专题,你就会发现只要涉及递归的问题,都是 树的问题。