九章算法笔记 3.二叉树与分治算法Binary Tree & Divide Conquer

大纲 cs3k.com

• 时间复杂度训练 II

• 二叉树的遍历算法 Traverse in Binary Tree

Preorder / Inorder / Postorder

• 二叉树的深度优先搜索 DFS in Binary Tree

1.遍历问题 Preorder / Inorder / Postorder
2.分治算法 Introduce Divide Conquer Algorithm
3.非递归 遍历法 分治法 Non-recursion vs Traverse vs Divide Conquer
4.二叉搜索树 Binary Search Tree : Insert / Remove / Find / Validate

时间复杂度训练 II

cs3k.com

通过O(n)的时间,把n的问题,变为了两个n/2的问题,复杂度是多少?

T(n) = 2Tn/2) + O(n)

= 2 * 2T(n/4) + O(n) + O(n)

=n + nlogn

FullSizeRender-12-03-17-23-26-5通过O(1)的时间,把n的问题,变成了两个n/2的问题,复杂度是多少?

T(n) = 2T(n/2) + O(1)

= 2 * 2T(n/4) + O(1) + O(1)

=n + (1 + 2 + 4 +…+ n)

≈n + 2n

≈O(n)

二叉树的时间复杂度呢?

也是T(n) = 2T(n/2) + O(1)

即使是不平衡的二叉树, T(n) = T(x) + T(n-x-1) + O(1), 最后也是拆成n个O(1)相加

比如:

FullSizeRender-12-03-17-23-26-4

所以, 二叉树基本都是O(n)的复杂度**

二叉树的遍历算法 Traverse in Binary Tree

Screenshot from 2017-03-12 20-11-35

前序遍历

cs3k.com

 

Screenshot from 2017-03-12 20-15-27

 

中序遍历

Screenshot from 2017-03-12 20-20-32

后序遍历

Screenshot from 2017-03-12 20-21-08

Divide Conquer Algorithm

cs3k.com

Traverse vs Divide Conquer

1.递归是实现方式,遍历和分治是可以用递归实现的算法思想

2.Result in parameter vs Result in return value

3.Top down vs Bottom up

4.Traverse的结果要改参数,返回参数. Divide conquer的结果直接return,是个更好的接口,因为给你什么参数最好不要改.

Screenshot from 2017-03-12 20-36-28

非递归做遍历和递归做遍历的区别?

非递归其实是模拟递归用的stack

为什么自己模拟的可以, 调用计算机的就不行呢?

因为我们new出来的stack在heap memory里面, 能用的空间≈ memory size

stack memory ≈ process memory是计算机分给每个程序的一个很小的独占的空间,所以递归的深度太深,就不够用了

前序遍历

TRAVERSE

是一个小人, 拿着一个记事本, 顺着二叉树走, 走过一个, 在本子上面记下来

FullSizeRender-12-03-17-23-26-3

Screenshot from 2017-03-12 20-15-40

DIVIDE & CONQUER

是女王接到这个任务, 找两个小弟A和B, 让A和B先去收集, A收集了[2, 4, 5], B收集了[3], 最后女王把A, B的结果汇总加上自己是1,得到答案[1, 2, 4, 5, 3]

FullSizeRender-12-03-17-23-26-2

Screenshot from 2017-03-12 21-24-24.png

递归三要素

1.递归的定义:接什么参,返什么值->求以root为根的preorder并返回

2.递归的拆解

3.出口

理解的顺序是123, 写程序的顺序是132

递归的出口是选NULL还是叶子节点?

1.有一个corner case是直接就传一个null的节点进来, 所以要选null

2.叶子节点比较复杂, 只要判断null的return之后结果ok, 就null

Maximum Depth of Binary Tree

cs3k.com

Given a binary tree, find its maximum depth.

The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

Screenshot from 2017-03-12 21-37-19.png

这道题divide conquer比较简单, 因为traverse要游走,边走还得边在记事本上记录最大深度, 需要全局变量.

Binary Tree Paths

Given a binary tree, return all root-to-leaf paths.

Screenshot from 2017-03-12 21-41-38.png

写完出口先无脑派下去

再把结果加上去

最后检查一遍corner case, 再单独处理下子节点有null的情况

Minimum Subtree

cs3k.com

Given a binary tree, find the subtree with minimum sum. Return the root of the subtree.

LintCode will print the subtree which root is your return node.

It’s guaranteed that there is only one subtree with minimum sum and the given binary tree is not an empty tree.

如果能for一下多好, 就可以用大擂台来算了, 所以我们用打擂台加分治

Screenshot from 2017-03-12 22-01-55.png

 

也可以使用divide conquer

Balanced Binary Tree

Given a binary tree, determine if it is height-balanced.

For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

这道题根节点和儿子节点的关系理解:

cs3k.com

1.左子树是平衡的

2.右子数是平衡的

3.左右高度差≤1

divide and conquer要return两个值, 要建个class或者 struct, c++可以用pair

用全局变量的是traverse算法

Screenshot from 2017-03-12 22-08-34Screenshot from 2017-03-12 22-08-52

Subtree with Maximum Average

Given a binary tree, find the subtree with maximum average. Return the root of the subtree.

LintCode will print the subtree which root is your return node.

It’s guaranteed that there is only one subtree with maximum average.

最好把sum和size都记下来

sumA/sizeA   >  sumB/sizeB比较可以化作

sumA * sizeB   >  sumB * sizeA

如果用除法的话, size为0无意义, 而且精度有流失

用乘法的话, 容易溢出, 所以最好用long

Screenshot from 2017-03-12 22-16-43Screenshot from 2017-03-12 22-15-55

 Flattern Binary Tree to Linked List

Flatten a binary tree to a fake “linked list” in pre-order traversal.

Here we use the right pointer in TreeNode as the next pointer in ListNode.

Don’t forget to mark the left child of each node to null. Or you will get Time Limit Exceeded or Memory Limit Exceeded.

 

思路:

先把左右子树flattern了, 再把root的右接左子树, 左子树的最后接右子树
FullSizeRender-12-03-17-23-26-1
Screenshot from 2017-03-12 22-24-21
 

Screenshot from 2017-03-12 22-24-36

Screenshot from 2017-03-12 22-24-49

Lowest Common Ancestor

Given the root and two nodes in a Binary Tree. Find the lowest common ancestor(LCA) of the two nodes.

The lowest common ancestor is the node with largest depth which is the ancestor of both nodes.

Assume two nodes are exist in tree.

cs3k.com

Screenshot from 2017-03-12 22-26-55.png

LCA II

Given the root and two nodes in a Binary Tree. Find the lowest common ancestor(LCA) of the two nodes.

The lowest common ancestor is the node with largest depth which is the ancestor of both nodes.

The node has an extra attribute parent which point to the father of itself. The root’s parent is null.

Screenshot from 2017-03-12 22-29-40

5出发 5 7 4

6出发 6 7 4

最近的是7

自己也可以是自己的祖先, 倒着for循环一下

LCA III

cs3k.com

Given the root and two nodes in a Binary Tree. Find the lowest common ancestor(LCA) of the two nodes.
The lowest common ancestor is the node with largest depth which is the ancestor of both nodes.
Return null if LCA does not exist.

node A or node B may not exist in tree.

需要搞个result type

Screenshot from 2017-03-12 22-37-31Screenshot from 2017-03-12 22-37-50

Binary Tree Longest Consecutive Sequence

cs3k.com

Given a binary tree, find the length of the longest consecutive sequence path.

The path refers to any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The longest consecutive path need to be from parent to child (cannot be the reverse).

两个问题:
1.从root走, 最长序列多少? 并且和root能不能接上
2.找到全局的最优序列
Screenshot from 2017-03-12 22-41-04.png

Binary Tree Path Sum

Given a binary tree, find all paths that sum of the nodes in the path equals to a given number target.

A valid path is from root node to any of the leaf nodes.

traverse

边走变加

divide conquer

Screenshot from 2017-03-12 22-43-56

从1出来找和为5的path, 所以我们就要在2和4的位置上找和为(5-1)=4的path

Screenshot from 2017-03-12 22-47-06.png

稍后其它答案?

Binary Tree Path Sum II

cs3k.com

Your are given a binary tree in which each node contains a value. Design an algorithm to get all paths which sum to a given value. The path does not need to start or end at the root or a leaf, but it must go in a straight line down.

不要求在根出发,不要求在叶子节点结束,只要不拐弯就行

Screenshot from 2017-03-12 23-03-17.png

分为三个问题:

1.path在左

2.path在右

3.path以root开始

难的是一定以1开始, 和为6, 又是一个分治, 分为:

一定以2出发和一定以3出发的

Screenshot from 2017-03-12 23-07-52.png

Binary Tree Path Sum III

Give a binary tree, and a target number, find all path that the sum of nodes equal to target, the path could be start and end at any node in the tree.

可以拐弯, 最多可以拐几个弯?

一个!

以最多拐一个弯为突破点:

不拐的理解为在第一个点拐弯

所以以每个点为拐点的路径,就traverse了所有

FullSizeRender-12-03-17-23-26

其中x是什么?  for一下

Screenshot from 2017-03-12 23-07-22.png

Binary Search Tree

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node’s key.
  • The right subtree of a node contains only nodes with keys greater than the node’s key.
  • Both the left and right subtrees must also be binary search trees.
  • A single node tree is a BST

验证二叉查找树:

1.traverse  挨个和上一个比比打小

Screenshot from 2017-03-12 23-16-02

2.divide conquer和左最大以及右最小比较

Screenshot from 2017-03-12 23-15-55
 

quick sort

cs3k.com

public class Solution {
    /**
     * @param A an integer array
     * @return void
     */
    public void sortIntegers2(int[] A) { quickSort(A, 0, A.length - 1); } private void quickSort(int[] A, int start, int end) { if (start >= end) { return; } int left = start, right = end; // key point 1: pivot is the value, not the index int pivot = A[(start + end) / 2]; // key point 2: every time you compare left & right, it should be // left <= right not left < right while (left <= right) { // key point 3: A[left] < pivot not A[left] <= pivot while (left <= right && A[left] < pivot) { left++; } // key point 3: A[right] > pivot not A[right] >= pivot while (left <= right && A[right] > pivot) { right--; } if (left <= right) { int temp = A[left]; A[left] = A[right]; A[right] = temp; left++; right--; } } quickSort(A, start, right); quickSort(A, left, end); } } 

错误一:

        while (left < right) { // key point 3: A[left] < pivot not A[left] <= pivot while (left < right && A[left] < pivot) { left++; } // key point 3: A[right] > pivot not A[right] >= pivot while (left < right && A[right] > pivot) { right--; } if (left < right) { int temp = A[left]; A[left] = A[right]; A[right] = temp; left++; right--; } } 

走一遍:

3          2          1          4          5

l                                               r                    pivot = 1

l                      r

————————————————————————————————————

1          2          3          4          5

l r                                                        pivot = 1

————————————————————————————————————

左       1          2

r           l                                                         pivot = 1

[1 2]分成左右 左[1]  右还是[1 2], 规模没有缩小, 会无限循环

错误二:

   while (left <= right) { // key point 3: A[left] < pivot not A[left] <= pivot while (left <= right && A[left] <= pivot) { left++; } // key point 3: A[right] > pivot not A[right] >= pivot while (left <= right && A[right] >= pivot) { right--; } if (left <= right) { int temp = A[left]; A[left] = A[right]; A[right] = temp; left++; right--; } } 
 

这样, 对于数据[1 1 1 1 1]还是不行

merge sort

cs3k.com

public class Solution {
    /**
     * @param A an integer array
     * @return void
     */
    public void sortIntegers2(int[] A) { // use a shared temp array, the extra memory is O(n) at least int[] temp = new int[A.length]; mergeSort(A, 0, A.length - 1, temp); } private void mergeSort(int[] A, int start, int end, int[] temp) { if (start >= end) { return; } int left = start, right = end; int mid = (start + end) / 2; mergeSort(A, start, mid, temp); mergeSort(A, mid+1, end, temp); merge(A, start, mid, end, temp); } private void merge(int[] A, int start, int mid, int end, int[] temp) { int left = start; int right = mid+1; int index = start; // merge two sorted subarrays in A to temp array while (left <= mid && right <= end) { if (A[left] < A[right]) { temp[index++] = A[left++]; } else { temp[index++] = A[right++]; } } while (left <= mid) { temp[index++] = A[left++]; } while (right <= end) { temp[index++] = A[right++]; } // copy temp back to A for (index = start; index <= end; index++) { A[index] = temp[index]; } } }

quick VS merge

cs3k.com

time                                        space                稳定性                其它

quick   O(nlogn)平均,慢会O(n^2)          O(1)                 不稳定             先整体后局部

merge    一直O(nlogn)                             O(n)                  稳定               先局部后整体

merge sort因为要重新开一个数组,开空间再删空间这个操作影响了速度, 所以quick sort是比merge sort快的

猜你喜欢

转载自www.cnblogs.com/jiuzhangsuanfa/p/9895650.html