您的位置:首页 > Web前端

23rd Feb: 数组(搜索专题)和二叉树(Path Sum专题)

2017-02-25 00:03 363 查看
162. Find Peak Element

A peak element is an element that is greater than its neighbors.

Given an input array where num[i] ≠ num[i+1], find a peak element and return its index.

The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.

You may imagine that num[-1] = num
= -∞.

For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2.

Thinking: 

The easiest way to solve this problem is to traversal this array. And judge if current element is the peek element. However, this may take O(n) time complexity.

Instead, we can choose a middle element. Then keep searching to the direction that the values is bigger than this value. The worst situation for this is traversal n/2 elements. 

public class Solution {
// judment helper
private boolean isPeek(int[]nums, int index) {
if (index == 0) {
if (nums[index + 1] < nums[index]) {
return true;
} else {
return false;
}
} else if (index == nums.length - 1) {
if (nums[index - 1] < nums[index]) {
return true;
} else {
return false;
}
} else {
if (nums[index - 1] < nums[index] && nums[index + 1] < nums[index]) {
return true;
} else {
return false;
}
}
}
// main logic
public int findPeakElement(int[] nums) {
if (nums.length < 2) {
return 0;
}

int mid = (nums.length - 1) / 2;

if (isPeek(nums, mid)) {
return mid;
}

if (nums[mid] < nums[mid + 1]) {
while (mid < nums.length - 1 && nums[mid] < nums[mid + 1]) {
mid = mid + 1;
}

if (isPeek(nums, mid)) {
return mid;
}
}

if (mid > 0 && nums[mid] < nums[mid - 1]) {
while (mid > 0 && nums[mid] < nums[mid - 1]) {
mid = mid - 1;
}

if (isPeek(nums,mid)) {
return mid;
}
}
// never reach
return -1;
}
}


Feedback: The reason why we can only use these two if branches is because there is not any two elements are equal. 

33. Search in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Thinking: Binary Search with condition judgement is the way to solve this problem. 

There are two situations here: 

1) Like above: 4 5 6 7 0 1 2, mid = 3, array[3] = 7. 

low < mid,证明到从low到mid这部分是升序的。所以如果target < array[mid] 且 大于array[0]的话,这个值就在这部分。而如果不在的话,low = mid + 1. 在那个混序的部分进行搜索。

2) 如果是混序部分,遇到了mid之前的是混序的,如6 7 0 1 2 3 4. 此时array[low] > array[mid],这时候可以推断array[mid + 1] 和 array[high]是升序的。所以可以检查剩下的部分。如果target不在里面,再搜另外的部分。

Feedback:这里如果搜到Mid刚好是就返回。所以不是mid的话,要在分类考虑:如果等于low或者mid + 1或者high这些特殊的值。

public class Solution {
public int search(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return -1;
}

if (nums.length == 1) {
if (nums[0] == target) {
return 0;
}
return -1;
}

int low = 0;
int high = nums.length - 1;
int mid = 0;

while (low + 1 < high) {
mid = low + (high - low) / 2;//prevent overflow

if (nums[mid] == target) {//directly return if we are lucky
return mid;
}

if (nums[low] < nums[mid]) {// left part is sorted
if (target >= nums[low] && target < nums[mid]) { //only when it is sorted, this can be used as criteria
high = mid - 1;
} else { //
low = mid + 1;
}
} else { // right part is sorted
if (target >= nums[mid + 1] && target <= nums[high]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
}

if (nums[low] == target) {
return low;
}

if (nums[high] == target) {
return high;
}

return -1;
}
}


81. Search in Rotated Sorted Array II

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

Write a function to determine if a given target is in the array.

The array may contain duplicates.

Thinking: The only different between this problem and the problem above is such situation: [3,1,2,3,3,3,3]. Even though array[low] == array[mid], it can be not sorted in the left part. Therefore, in order to prevent such case, we can use if (array[low] ==
array[mid]) { low++;} and if (array[high] == array[mid]) {high--;}

Code: 

public class Solution {
public boolean search(int[] nums, int target) {

if (nums == null || nums.length == 0) {
return false;
}

if (nums.length == 1) {
if (nums[0] == target) {
return true;
}
return false;
}

int low = 0;
int high = nums.length - 1;
int mid = 0;

while (low + 1 < high) {

mid = low + (high - low) / 2; //prevent overflow
int midVal = nums[mid];

if (nums[low] == midVal) {
low++;
continue;
}

if (midVal == nums[high]) {
high--;
continue;
}

if (nums[mid] == target) {//directly return if we are lucky
return true;
}

if (nums[low] < nums[mid]) {// left part is sorted
if (target >= nums[low] && target < nums[mid]) { //only when it is sorted, this can be used as criteria
high = mid - 1;
} else { //
low = mid + 1;
}
} else { // right part is sorted
if (target >= nums[mid + 1] && target <= nums[high]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
}

if (nums[low] == target || nums[high] == target) {
return true;
}

return false;
}
}

153. Find Minimum in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0
4000
1 2).

Find the minimum element.

You may assume no duplicate exists in the array.

Thinking: The basic idea is similar to the problem above. The basic return case is array[low] < array[high], we can return array[low] directly. Otherwise, we need to divide the search range into two parts. The worst case is the whole sorted array only rotate
for 1 step. It means only when array[low] == array[low], then recursive function will return. 

Therefore, the basic case here is array[low] <= array[high]. We can return the array[low] directly.

public class Solution {
private int traversal(int[] nums, int low, int high) {
if (nums[low] <= nums[high]) {
return nums[low];
}

int mid = low + (high - low) / 2;

int left = traversal(nums, low, mid);
int right= traversal(nums, mid + 1, high);

if (left < right) {
return left;
}
return right;
}

public int findMin(int[] nums) {
if (nums == null || nums.length == 0) {
return -1;
}

if (nums.length == 1) {
return nums[0];
}

int low = 0;
int high = nums.length - 1;

return traversal(nums, low, high);
}
}


Thinking: There is also a tricky thinking here. In this problem, we have only three cases. [Ref: https://discuss.leetcode.com/topic/14768/4ms-simple-c-code-with-explanation]
Case 1. The leftmost value is less than the rightmost value in the list: This means that the list is not rotated.

e.g> [1 2 3 4 5 6 7 ]

Case 2. The value in the middle of the list is greater than the leftmost and rightmost values in the list.

e.g> [ 4 5 6 7 0 1 2 3 ]

Case 3. The value in the middle of the list is less than the leftmost and rightmost values in the list.

e.g> [ 5 6 7 0 1 2 3 4 ]

As you see in the examples above, if we have case 1, we just return the leftmost value in the list. If we have case 2, we just move to the right side of the list. If we have case 3 we need to move to the left side of the list.

Code: 

public class Solution {
public int findMin(int[] nums) {
int low = 0;
int high = nums.length - 1;
int mid = 0;

while (low + 1 < high) {
mid = low + (high - low) / 2;

if (nums[low] < nums[high]) {
return nums[low];
}

if (nums[mid] > nums[low] && nums[mid] > nums[high]) {
low = mid;
}

if (nums[mid] < nums[low] && nums[mid] < nums[high]) {
high = mid;
}
}

if (nums[low] < nums[high]) {
return nums[low];
} else {
return nums[high];
}
}
}


Binary Tree专题(Path Sum专题)

112. Path Sum

Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.

For example:

Given the below binary tree and sum = 22,

              5

             / \

            4   8

           /   / \

          11  13  4

         /  \      \

        7    2      1

return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.

Thinking: This problem is similar to the path sum II. We need to remember that it must be the leaf node when it return true or false. The other logics are easy. (Consider extreme cases). 

public class Solution {
private boolean traversal(TreeNode root, int tmp, int sum) {
if (root == null) {
return false;
}

tmp = root.val + tmp;
if (root.left == null && root.right == null) {//must be the leaf
if (tmp == sum) {
return true;
} else {
return false;
}
}
// non leaf node
if (root.left != null && root.right != null) {
return traversal(root.left, tmp, sum) || traversal(root.right, tmp, sum);
} else if (root.left != null) {
return traversal(root.left, tmp, sum);
} else {
return traversal(root.right, tmp, sum);
}
}

public boolean hasPathSum(TreeNode root, int sum) {
int curSum = 0;
return traversal(root, curSum, sum);
}
}


Worst case: O(n). n is the number of nodes. 

257. Binary Tree Paths

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

For example, given the following binary tree:

   1

 /   \

2     3

 \

  5

All root-to-leaf paths are:

["1->2->5", "1->3"]

Thinking: Similar problem to DFS. Before calling the same function for left child and right child, we need to add “->” + “current node value“ to the string. However, we need to realize that we cannot add to the string directly. Because it will modify the string.
We need to use the new String to create a new string. Also, we need to consider the base case and once it reach the leaf, we need to add the current new string to the result list. 

Code: 

public class Solution {
private void traversal(TreeNode root, List<String> res, String tmp) {
if (root == null) {
return;
}

if (tmp.equals("")) {
tmp = new String(Integer.toString(root.val));
} else {
tmp = new String(tmp + "->" + Integer.toString(root.val));
}

if (root.left == null && root.right == null) {// leaf node
res.add(tmp);
return;
}

traversal(root.left, res, tmp);
traversal(root.right, res, tmp);
return;
}

public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<String>();
if (root == null) {
return res;
}
String tmp = "";
traversal(root, res, tmp);

return res;
}
}


145. Binary Tree Postorder Traversal

Given a binary tree, return the postorder traversal of its nodes' values.

For example:

Given binary tree {1,#,2,3},

   1

    \

     2

    /

   3

return [3,2,1].

Thinking: 注意二叉树的后序遍历的顺序是:右、左,中。模板是:利用两个linked list,一个用来存结果,一个用来模拟stack. 两个linked list都充分利用好addFirst()这个函数。结果集用这个来函数来构建先左子树、后右子树最后再根的遍历顺序。通过不停的往前面插应该排在它前面。所以,整个遍历的顺序应该反转过来:先根,根节点的右边所有的点直到叶子节点,每一步放进结果集的同时压栈,接着从栈取出上一个节点,把其变为其左孩子。然后重复上一顺序。

Code: 

public class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
LinkedList<Integer> result = new LinkedList<Integer>();
TreeNode tmp = root;

while (tmp != null || !(stack.isEmpty())) {
while (tmp != null) {
stack.addFirst(tmp);
result.addFirst(tmp.val);
tmp = tmp.right;
}

tmp = stack.removeFirst();
tmp = tmp.left;
}

return result;
}
}


Feedback: 类似的前序非递归和中序的非递归遍历。请看这篇博文:https://leetcode.com/problems/binary-tree-postorder-traversal/?tab=Solutions

236. Lowest Common Ancestor of a Binary Tree

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”

        _______3______

       /              \

    ___5__          ___1__

   /      \        /      \

   6      _2       0       8

         /  \

         7   4

For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. Another example is LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition.

Thinking: There are several situations here.

1) If two nodes are in the same subtree of root (different level). For example, 5 and 2. Then as we go through the right sub tree, we will return null. Then, we can see both two nodes are in the same left subtree. Then if we go through left subtree. We will
reach 5 first. We even don’t bother to visit its children, because another node must be its child.

2) Both nodes are in the same level, it means different subtree. Then, when we visit its parent, we will visit both of them and both of them will return itself. Once we get the situation that both of them aren’t null, we can judge that its parent is the answer. 

public class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) {
return root;
}

TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);

// two different siutations
if (left != null && right != null) {
return root;
} else if (left != null || right != null){
return left != null ? left : right;
} else {
return null;
}
}
}

235. Lowest Common Ancestor of a Binary Search Tree

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”

        _______6______

       /              \

    ___2__          ___8__

   /      \        /      \

   0      _4       7       9

         /  \

         3   5

For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

Thinking: We can take advantage of the feature of BST. If the current root value is between the p and q value, then current value is the answer. If both of them are smaller than current root, go to the left subtree. Otherwise, we go to the right subtree.However,
there is one thing that need to be noticed, it is that we cannot simply use the condition: root.val > p.val && root.val < q.val because it is not guaranteed that p.val < q.val. Therefore, we can simply multiply the difference between root value and their values. 


public class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}

if ((root.val - p.val) * (root.val - q.val) < 0) {
return root;
} else if (root.val == p.val || root.val == q.val) {
return root;
} else {
if (root.val < p.val) {
return lowestCommonAncestor(root.right, p, q);
} else {
return lowestCommonAncestor(root.left, p, q);
}
}
}
}


There is also an iterative version: 

public class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}

while (((root.val - p.val) * (root.val - q.val) > 0)) {
TreeNode bigger = p.val > q.val ? p : q;
TreeNode smaller = p.val < q.val ? p : q;

if (root.val < smaller.val) {
root = root.right;
}

if (root.val > bigger.val) {
root = root.left;
}
}

return root;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  二叉树 path 搜索