Leetcode算法系列六(二分查找与二叉查找树)

1.5道经典 二分搜索与二叉查找( 排序)树 树的相关题目

预备知识:二分查找基础知识
例1:插入位置(easy) (二分查找)
例2:区间查找(medium) (二分查找)
例3:旋转数组查找(medium) (二分查找)
预备知识:二叉查找(排序)树基础知识
例4:二叉查找树编码与解码(medium)
例5:逆序数(hard) (二叉查找树应用)

例1:插入位置(easy) (二分查找)

  1. 搜索插入位置
    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
    你可以假设数组中无重复元素。
    示例 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) (二分查找)

  1. 在排序数组中查找元素的第一个和最后一个位置
    给定一个按照升序排列的整数数组 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) (二分查找)

  1. 搜索旋转排序数组
    给你一个整数数组 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. 序列化和反序列化二叉搜索树
    序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
    设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
    编码的字符串应尽可能紧凑。
    示例 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) (二叉查找树应用)

  1. 计算右侧小于当前元素的个数
    给定一个整数数组 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;
            }
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值