赞
踩
常见的时间复杂度和对应的算法题型如下:
根据题意,时间复杂度为 O(log(n)) ,则可以简单得出:时间复杂度为 O(log(n)) 的解决方案一般是二分查找算法。
这道题就是二分查找基本题的变形,区别就是if判断语句不一样,不多说
class Solution { public int peakIndexInMountainArray(int[] arr) { int left = 0; int right = arr.length - 1; while(left < right){ int mid = left + ((right - left) >> 1); if(mid <= 0 || mid >= arr.length - 1){ return -1; } if(arr[mid] > arr[mid+1] && arr[mid] > arr[mid-1]){ return mid; } if(arr[mid] > arr[mid - 1]){ left = mid+1; }else{ right = mid; } } return -1; } }
不过可以再简单一点:至于哪个更快,要看数据
class Solution { public int peakIndexInMountainArray(int[] arr) { int left = 0; int right = arr.length - 1; while (left < right) { int mid = left + ((right - left) >> 1); if (arr[mid] < arr[mid + 1]) { left = mid + 1; } else { right = mid; } } return left; } }
这道题其实也算是上面那道题的变式,需要思考的就是边界值:
题意:
可以得出:mid>right的时候,最大值一定在mid的右边
class Solution { public int findMin(int[] nums) { int left = 0; int right = nums.length - 1; if(nums.length == 1) return nums[0]; if(nums[left] < nums[right]){ return nums[left]; } else if(nums[right] < nums[right-1]){ return nums[right]; } while(left < right){ int mid = left + ((right - left) >> 1); if(mid == 0 || mid == nums.length - 1){ } if(nums[mid] < nums[mid+1] && nums[mid] < nums[mid-1]){ return nums[mid]; } if (nums[mid] > nums[right]) { left = mid + 1; } else { right = mid; } } return nums[left]; } }
不过我写的不够好,可以再优化一下:
下面这样看起来更清楚,而且效率会更高一点
public int findMin(int[] nums) { int left = 0; int right = nums.length - 1; while (left < right) { int mid = left + ((right - left) >> 1); // 检查是否已经有序,如果是则直接返回最左元素 if (nums[left] < nums[right]) { return nums[left]; } else if(nums[right] < nums[right-1]){ return nums[right]; } // 判断mid处的元素与右边界的关系,确定搜索方向 if (nums[mid] > nums[right]) { left = mid + 1; } else { right = mid; } } // 循环结束时,left和right相等,即找到了最小值 return nums[left]; }
leetcode 剑指 Offer 53 - II. 0~n-1中缺失的数字
这道题其实就是最简单二分查找的略微变形:
代码如下:
class Solution { public int missingNumber(int[] nums) { int left = 0; int right = nums.length - 1; while(left <= right){ int mid = left+((right-left) >> 1); if(nums[mid]>mid){ right = mid - 1; }else{ left = mid + 1; } } return left; } }
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
没什么好讲的,翻译过来就是[1,n]的数组进行二分查找
当我们在搜索平方根时,如果mid的平方等于x,即
mid * mid == x,那么我们可以确定mid就是x的平方根。这是因为我们在范围[1, x]内进行二分查找,每次取中间值mid并计算
mid * mid与x的大小关系。当mid * mid等于x时,说明我们找到了x的平方根。
class Solution { public int mySqrt(int x) { if (x == 0) { return 0; } int left = 1; int right = x; while (left <= right) { int mid = left + ((right - left) >> 1); if (x / mid == mid) { return mid; } else if (x / mid > mid) { left = mid + 1; } else { right = mid - 1; } } return right; } }
二叉搜索树是一个很简单的概念,但是想说清楚却不太容易。简单来说就是如果一棵二叉树是搜索树,则按照中序遍历其序列正好是一个递增序列。比较规范的定义是:
若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
它的左、右子树也分别为二叉排序树。下面这两棵树一个中序序列是{3,6,9,10,14,16,19},一个是{3,6,9,10},因此都是搜索树:

搜索树的题目虽然也是用递归,但是与前后序有很大区别,主要是因为搜索树是有序的,就可以根据条件决定某些递归就不必执行了,这也称为“剪枝”。
要注意是二叉搜索树,那么就是它的特点来写:
如果根节点为空 root == null 或者根节点的值等于搜索值 val == root.val,返回根节点。
如果 val < root.val,进入根节点的左子树查找 searchBST(root.left, val)。
如果 val > root.val,进入根节点的右子树查找 searchBST(root.right, val)。
public TreeNode searchBST(TreeNode root, int val) {
if (root == null || val == root.val) return root;
return val < root.val ? searchBST(root.left, val) : searchBST(root.right
}
如果采用迭代方式,也不复杂:
如果根节点不空 root != null 且根节点不是目的节点 val != root.val:
如果 val < root.val,进入根节点的左子树查找 root = root.left。
如果 val > root.val,进入根节点的右子树查找 root = root.right。
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
while(root != null && root.val != val ){
if(root.val > val){
root = root.left;
}else{
root = root.right;
}
}
return root;
}
}
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
根据题目给出的性质,我们可以进一步知道二叉搜索树「中序遍历」得到的值构成的序列一定是升序的,在中序遍历的时候实时检查当前节点的值是否大于前一个中序遍历到的节点的值即可。
class Solution { long prev = Long.MIN_VALUE; public boolean isValidBST(TreeNode root) { if (root == null) { return true; } // 验证左子树 if (!isValidBST(root.left)) { return false; } // 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST if (root.val <= prev) { return false; } prev = root.val; // 更新prev为当前节点的值 // 验证右子树 return isValidBST(root.right); } }
递归的方式最难实现的就是在哪里写条件,有点难理解,用迭代就比较容易理解,就是比较长
class Solution { public boolean isValidBST(TreeNode root) { if (root == null) { return true; } Stack<TreeNode> stack = new Stack<>(); TreeNode current = root; long prev = Long.MIN_VALUE; while (!stack.isEmpty() || current != null) { // 将当前节点及其所有左子节点入栈 while (current != null) { stack.push(current); current = current.left; } // 弹出栈顶节点,并判断是否大于前一个节点 current = stack.pop(); if (current.val <= prev) { return false; } prev = current.val; // 处理右子节点 current = current.right; } return true; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。