Triple Inversion - 二分查找


题目地址

https://binarysearch.com/problems/Triple-Inversion

题目描述

题目描述在这里插入图片描述


思路

  1. 原本我想的是对遍历到的数之后的数排序,看有多少满足 nums[i] > nums[j] * 3,这样不可避免的要排序很多次,不出乎意料地超时了……那就反过来,对已遍历过的数排序,看当前遍历到的数之前有多少满足nums[j] * 3< nums[i]的
    [已经遍历过的有序数组,索引范围为[0, i - 1] | 遍历到的当前数,索引为i | 还未遍历到的数组]
  2. 在已遍历过的数构成的有序数组里,通过二分法找到满足条件的最小的索引,也就是通过二分法找左边界
  3. 那么这个最小索引之后一直到 i 之间的数字都是满足条件的数字,令结果res = i - leftBorder
  4. 接下来要将当前数加到排序数组中,还是通过二分法找到比当前数大的最小索引,在该索引位置插入当前数
  • 二分法步骤
  1. left = 0right = 排序数组末尾索引
  2. mid = (left + right) / 2
  3. 如果sortedNums.get(mid) <= target,mid太小了,要增加mid,left = mid + 1
  4. 如果sortedNums.get(mid) > target,mid可能偏大,也有可能就是我们想要的结果,令right = mid - 1
    那么查找区间变成了[left, mid - 1],如果mid不是我们想要的左边界,那就丢弃了;如果mid是我们想要的左边界,那么在这之前应当已满足left == mid == 之前的right,right减少了之后left > right,不再满足循环条件,返回left;如果数组里的所有数都不符合条件,那根本不会走这个语句

PS: Python有时候要超时,如果超时了多跑几遍会通过的;Java不会超时

代码(Python)

class Solution:
    def solve(self, nums):
        res = 0
        sorted_nums = []
        for i in range(len(nums)):
            left_border = self.find_left_border(sorted_nums, nums[i] * 3)
            res += i - left_border
            insert_index = self.find_left_border(sorted_nums, nums[i])
            sorted_nums.insert(insert_index, nums[i])
        return res

    def find_left_border(self, sorted_nums, target):
        left = 0
        right = len(sorted_nums) - 1
        while left <= right:
            mid = (left + right) // 2
            if sorted_nums[mid] <= target:
                left = mid + 1
            else:
                right = mid - 1
        return left

代码(Java)

import java.util.*;

class Solution {
    public int solve(int[] nums) {
        int res = 0;
        List<Integer> sortedNums = new ArrayList<>();
        for(int i = 0; i < nums.length; i++){
            int leftBorder = findLeftBorder(sortedNums, nums[i] * 3);
            res += i - leftBorder;
            int insertIndex = findLeftBorder(sortedNums, nums[i]);
            sortedNums.add(insertIndex, nums[i]);
        }
        return res;
        
    }
    private int findLeftBorder(List<Integer> sortedNums, int target){
        int left = 0, right = sortedNums.size() - 1;
        while(left <= right){
            int mid = (left + right) / 2;
            if(sortedNums.get(mid) <= target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return left;
    }
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),for循环一定有 O ( n ) O(n) O(n),for循环里面的二分法有 O ( l o g n ) O(logn) O(logn),插入操作最差有 O ( n ) O(n) O(n),最后就是 O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n ) O(n) O(n)
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值