计算数组的小和
https://www.nowcoder.com/practice/6dca0ebd48f94b4296fc11949e3a91b8
import java.util.*;
/**
* NC349 计算数组的小和
* @author d3y1
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型ArrayList
* @return long长整型
*/
public long calArray (ArrayList<Integer> nums) {
// return solution1(nums);
return solution2(nums);
// return solution3(nums);
}
/**
* 二分: 超时!
* @param nums
* @return
*/
private long solution1(ArrayList<Integer> nums){
long result = 0;
int n = nums.size();
ArrayList<Integer> list = new ArrayList<>();
list.add(nums.get(0));
// pos: 当前num(target)插入list位置(索引)
int left,right,mid,last,target,pos,sum;
for(int i=1; i<n; i++){
last = list.get(i-1);
target = nums.get(i);
sum = 0;
// 直接附加
if(last <= target){
pos = i;
list.add(pos, target);
}
// 二分查找
else{
left = 0;
right = i-1;
while(left <= right){
mid = (left+right)>>1;
if(list.get(mid) <= target){
left = mid+1;
}else if(list.get(mid) > target){
right = mid-1;
}
}
pos = left;
list.add(pos, target);
}
// 求和(小于等于当前num(target))
for(int j=0; j<pos; j++){
sum += list.get(j);
}
result += sum;
}
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 树状数组(Binary Indexed Tree)(也称Fenwick树): 动态维护前缀和
*
* 参考: https://www.bilibili.com/video/BV1pE41197Qj/?vd_source=fa6990445a0e68c4b18e85e07647ab23
*
* @param nums
* @return
*/
private long solution2(ArrayList<Integer> nums){
long result = 0;
int n = nums.size();
// 离散化处理
discretization(nums);
// 初始化
init(nums.size()+1);
int num;
for(int i=0; i<n; i++){
num = nums.get(i);
// 树状数组id
int id = getId(num);
// 根据id获取前缀和
result += query(id);
// 更新前缀和
update(id, num);
}
return result;
}
// 树状数组t
private int[] t;
// 将nums离散化, 去重+排序, 转化为数组a
private int[] a;
/**
* 初始化
* @param length
*/
private void init(int length) {
t = new int[length];
Arrays.fill(t, 0);
}
/**
* 非负整数n在二进制表示下最低位1及后面的0所构成的数值
*
* 示例:
* lowBit(44) = lowBit(101100[2进制]) = 100[2进制] = 4[10进制]
*
* n 101100
* 取反: ~n 010011
* 取反+1: ~n+1 010100
*
* ~n+1 = -n (计算机存储使用补码, 取反加1后的值即为负的这个数)
*
* lowBit(44) = n & (~n+1) = n & (-n)
*
* @param x
* @return
*/
private int lowBit(int x) {
return x & (-x);
}
/**
* 更新前缀和: 从当前节点往树根逐层更新父节点
* @param pos
* @param num
*/
private void update(int pos, int num) {
while (pos < t.length) {
t[pos] += num;
// 当前节点的父节点
pos += lowBit(pos);
}
}
/**
* 查询前缀和: 从当前节点往左上逐个累加节点值
* @param pos
* @return
*/
private long query(int pos) {
long ret = 0;
while (pos > 0) {
ret += t[pos];
// 当前节点的左上节点
pos -= lowBit(pos);
}
return ret;
}
/**
* 离散化处理
*
* nums数组中可能有负数或者可能稀疏 -> 离散化处理
*
* @param nums
*/
private void discretization(ArrayList<Integer> nums) {
Set<Integer> set = new HashSet<>(nums);
int size = set.size();
a = new int[size];
int index = 0;
for (int num : set) {
a[index++] = num;
}
Arrays.sort(a);
}
/**
* 根据num获取树状数组id: num -> id
* @param num
* @return
*/
private int getId(int num) {
return Arrays.binarySearch(a, num)+1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* 归并
*
* nums[i]最终累加次数 等价于 后面有多少个大于等于nums[i]的数(count[i])
*
* 示例:
* i 0 1 2 3 4 5
* nums 1 3 5 2 4 6
* count 5 3 1 2 1 0
*
* result = 1*5 + 3*3 + 5*1 + 2*2 + 4*1
* = 5 + 9 + 5 + 4 + 4
* = 27
*
* @param nums
* @return
*/
private long solution3(ArrayList<Integer> nums){
Integer[] numsArr = new Integer[nums.size()];
nums.toArray(numsArr);
return mergeSort(numsArr, 0, nums.size()-1);
}
/**
* 归并排序
* @param nums
* @param left
* @param right
* @return
*/
private long mergeSort(Integer[] nums, int left, int right) {
if(left >= right) {
return 0;
}
int mid = left+((right-left)>>1);
return mergeSort(nums, left, mid)+mergeSort(nums, mid+1, right)+merge(nums, left, mid, right);
}
/**
* 合并
* @param nums
* @param left
* @param mid
* @param right
* @return
*/
private long merge(Integer[] nums, int left, int mid, int right) {
long result = 0L;
int[] help = new int[right-left+1];
int i = 0;
int p = left;
int q = mid+1;
while(p<=mid && q<=right) {
result += (nums[p]<=nums[q]) ? nums[p]*(right-q+1) : 0;
help[i++] = (nums[p]<=nums[q]) ? nums[p++] : nums[q++];
}
while(p <= mid) {
help[i++] = nums[p++];
}
while(q <= right) {
help[i++] = nums[q++];
}
for(i=0; i<help.length; i++) {
nums[left+i] = help[i];
}
return result;
}
}