1.5道经典 二分搜索与二叉查找( 排序)树 树的相关题目
预备知识:二分查找基础知识
例1:插入位置(easy) (二分查找)
例2:区间查找(medium) (二分查找)
例3:旋转数组查找(medium) (二分查找)
预备知识:二叉查找(排序)树基础知识
例4:二叉查找树编码与解码(medium)
例5:逆序数(hard) (二叉查找树应用)
例1:插入位置(easy) (二分查找)
- 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
public class Solution {
public int searchInsert(int[] nums, int target) {
int index=-1;
int begin=0;
int end=nums.length-1;
while(index==-1){//一定可以找到位置插入
int mid=(begin+end)/2;
if(nums[mid]==target){
index=mid;
}else if(nums[mid]>target){
if(mid==0||target>nums[mid-1]){
index=mid;
}
end=mid-1;
}else{//nums[mid]<target
if(mid==nums.length-1||target<nums[mid+1]){
index=mid+1;
}
begin=mid+1;
}
}
return index;
}
}
例2:区间查找(medium) (二分查找)
- 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
public class Solution {
public int[] searchRange(int[] nums, int target) {
int [] result=new int[2];
result[0]=SearchLeft(nums,target);
result[1]=SearchRight(nums,target);
return result;
}
public int SearchLeft(int[] nums,int target){
int begin=0;
int end=nums.length-1;
while(begin<=end){
int mid=(begin+end)/2;
if(nums[mid]==target){
if(mid==0||nums[mid-1]<target){
return mid;
}else {
end=mid-1;//寻找左端点,缩小范围
}
}else if(nums[mid]>target){
end=mid-1;
}else{
begin=mid+1;
}
}
return -1;
}
public int SearchRight(int[] nums,int target){
int begin=0;
int end=nums.length-1;
while(begin<=end){
int mid=(begin+end)/2;
if(nums[mid]==target){
if(mid==nums.length-1||nums[mid+1]>target){
return mid;
}else {
begin=mid+1;
}
}else if(nums[mid]>target){
end=mid-1;
}else{
begin=mid+1;
}
}
return -1;
}
}
例3:旋转数组查找(medium) (二分查找)
- 搜索旋转排序数组
给你一个整数数组 nums ,和一个整数 target 。
该整数数组原本是按升序排列,但输入时在预先未知的某个点上进行了旋转。(例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
提示:
1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
nums 肯定会在某个点上旋转
-10^4 <= target <= 10^4
public class Solution {
public int search(int[] nums, int target) {
int begin=0;
int end=nums.length-1;
while(begin<=end){
int mid=(begin+end)/2;
if(target==nums[mid]){
return mid;
}else if(target<nums[mid]){
if(nums[begin]<nums[mid]){
if(target>=nums[begin]){
end=mid-1;
}else {
begin=mid+1;
}
}else if(nums[begin]>nums[mid]){
end=mid-1;
}else if(nums[begin]==nums[mid]){
begin=mid+1;
}
}else if(target>nums[mid]){
if(nums[begin]>nums[mid]){
if(target>=nums[begin]){
end=mid-1;
}else {
begin=mid+1;
}
}else if(nums[begin]<nums[mid]){
begin=mid+1;
}else if(nums[begin]==nums[mid]){
begin=mid+1;
}
}
}
return -1;
}
}
例4:二叉查找树编码与解码(medium)
- 序列化和反序列化二叉搜索树
序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
示例 1:
输入:root = [2,1,3]
输出:[2,1,3]
示例 2:
输入:root = []
输出:[]
提示:
树中节点数范围是 [0, 104]
0 <= Node.val <= 104
题目数据 保证 输入的树是一棵二叉搜索树。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder result=new StringBuilder();
if(root==null){
return "";
}else{
BTS_pre(root,result);
return result.toString();
}
}
public void BTS_pre(TreeNode root,StringBuilder result){
if(root==null){
return;
}else{
result.append(Integer.toString(root.val));
//result+=Integer.toString(root.val);//编码为字符串
result.append("#");//结束符号
BTS_pre(root.left,result);
BTS_pre(root.right,result);
}
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data==""){
return null;
}
String array[]=data.split("#");
TreeNode result=new TreeNode(Integer.parseInt(array[0]));
for(int i=1;i<array.length;i++){
int insert=Integer.parseInt(array[i]);
BTS_pre_build(result,insert);
}
return result;
}
public void BTS_pre_build(TreeNode result,int insert){
if(insert<result.val){
if(result.left!=null){
BTS_pre_build(result.left,insert);
}else{
result.left=new TreeNode(insert);
}
}else{
if(result.right!=null){
BTS_pre_build(result.right,insert);
}else{
result.right=new TreeNode(insert);
}
}
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// String tree = ser.serialize(root);
// TreeNode ans = deser.deserialize(tree);
// return ans;
例5:逆序数(hard) (二叉查找树应用)
- 计算右侧小于当前元素的个数
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
提示:
0 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
class BTSNode{
int count;//左子树的元素个数
int val;
BTSNode left;
BTSNode right;
BTSNode(int val){
this.val=val;
this.left=null;
this.right=null;
this.count=0;
}
}
public class Solution {
static int count_small=0;//统计count的变量
public List<Integer> countSmaller(int[] nums) {
List<Integer> result=new ArrayList<>();
Stack<Integer> stack=new Stack<>();
if(nums.length==0){
return result;
}
int n=nums.length;
BTSNode root=new BTSNode(nums[n-1]);//第一个结点,从尾部开始
stack.add(0);//第一个结点
for(int i=n-2;i>=0;i--){
count_small=0;
BTSNode temp=new BTSNode(nums[i]);
BTS(root,temp);
stack.add(count_small);
}
while(!stack.empty()){
result.add(stack.pop());
}
return result;
}
/**
*构造二叉搜索树,并计算count_small
* */
public void BTS(BTSNode root,BTSNode insert){
if(insert.val<=root.val){//往左边插入
root.count++;
if(root.left!=null){
BTS(root.left,insert);
}else{
root.left=insert;
}
}else{//往右边插入
count_small+=root.count+1;//统计左子树和根结点数量
if(root.right!=null){
BTS(root.right,insert);
}else{
root.right=insert;
}
}
}
}