LeetCode 315. 计算右侧小于当前元素的个数(二叉查找树&二分查找&归并排序逆序数总结)

1. 题目

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

示例:
输入: [5,2,6,1]
输出: [2,1,1,0] 
解释:
5 的右侧有 2 个更小的元素 (21).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2. 解题

2.1 二叉查找树

我的博客 二叉查找树

  • 每个节点增加一个数据count(记录比这个节点小的元素个数)
  • 如果新加入的节点小于当前节点cur,cur->count++
  • 从根节点root向下查找,满足条件则累加count值&&路过的比自己小的元素个数
  • 特别注意,将原数组逆向(从右向左)插入BST(题目要求找的是右边比我小的)
  • 时间复杂度O(nlgn)

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

  • 最坏情况下,BST退化成链表,时间复杂度变成O(n2)
class Node	//树的节点
{
public:
	int val;
	int count;//小于该节点的个数
	Node *left, *right;
	Node(int v): val(v), count(0), left(NULL), right(NULL){}
};
class BST
{
	Node *root;
public:
	BST():root(NULL){}
	int insert(int n)
	{
		return insert(n, root);
	}
private:
	int insert(int n, Node* &cur)//指针的引用,将节点之间连起来
	{
		if(!cur)
		{
			cur = new Node(n);//创建的新节点cur更新至调用处的参数
			return 0;//自己等于自己,不用加,返回0
		}
		else
		{
			if(n > cur->val)
				//我比当前大,+1,还要 +之前记录的比它小的个数,+它右侧的
				return 1 + cur->count + insert(n,cur->right);
			if(n < cur->val)
			{	//我比当前小
				cur->count++;	//比当前小的记录 +1
				return insert(n,cur->left);//去左边子树里查找
			}
			else// ==,+比当前小的个数,+右侧里面的
				return cur->count + insert(n,cur->right);
		}
	}
};
class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        vector<int> ans(nums.size());
        BST tree;
        for(int i = nums.size()-1; i >= 0; --i)
        	ans[i] = tree.insert(nums[i]);
        return ans;
    }
};

在这里插入图片描述

2.2 二分插入

  • 开辟一个空的数组,用于存放已排序的数据
  • 将原数组,从右向左,二分插入至新数组,记录插入的位置(前面有多少小于我的)
class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        if(nums.empty())
        	return {};
        vector<int> ans, sorted;
        int pos;
        vector<int>::iterator it;
        for(int i = nums.size()-1; i >= 0; --i)
        {
        	pos = binaryInsert(sorted,nums[i]);
        	it = sorted.begin()+pos;
        	sorted.insert(it,nums[i]);//可能导致vector数据搬移
        	ans.push_back(pos);
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
    
    int binaryInsert(vector<int>& sorted, int num)
    {
    	int left = 0, right = sorted.size()-1, mid;
    	while(left <= right)
    	{
    		mid = left+((right-left)>>1);
    		if(sorted[mid] < num)
    			left = mid+1;
    		else if(sorted[mid] >= num)
    			right = mid-1;
    	}
    	return left;//自己在纸上测试下,只能是left
    }
};
  • 时间复杂度也是O(nlgn)
  • 以下结果时间偏长,可能是在vector中间插入数据导致的数据搬移,消耗了时间
    在这里插入图片描述

2.3 归并排序

参考分治,归并求逆序度

借一张图理解一下
在这里插入图片描述

  • 需要归并求解,但是归并排序过程中,数据下标变动了,需要建立一个下标数组
  • 对下标数组idx进行排序(比较大小的时候用nums数组代入比较)
  • 计算逆序数方法(只能取一种,不要同时用)
  • 1、当前序数组写入时,计算后序中已经出列的个数(它们均小于刚写入的),j-(mid+1),有后续操作(前序没写完时,继续累加)
  • 2、当后序数组写入时,计算前序中还有多少没有出队(它们均大于刚写入的),mid-i+1,无后序操作(因为,前序出队完毕,剩余0,或者,后序写入完毕)
    在这里插入图片描述
    再借两张图总结下
    在这里插入图片描述
    在这里插入图片描述
class Solution {
	vector<int> ans;//存储结果
	vector<int> temp;//归并排序临时空间
	vector<int> idx;//归并排序的对象,排的是---下标,不是数值
public:
    vector<int> countSmaller(vector<int>& nums) {
        if(nums.empty())
        	return {};
        ans.resize(nums.size());
        temp.resize(nums.size());
        idx.resize(nums.size());
        for(int i = 0; i < nums.size(); ++i)
        {
        	idx[i] = i;
        	ans[i] = 0;
        }
        mergeSort(nums,0,nums.size()-1);
        return ans;
    }

    void mergeSort(vector<int> &nums, int l, int r)
    {
    	if(l == r)
    		return;
    	int mid = l+((r-l)>>1);
    	mergeSort(nums,l,mid);
    	mergeSort(nums,mid+1,r);
    	merge(nums,l,mid,r);
    }
    void merge(vector<int>& nums, int l, int mid, int r)
    {
    	int i = l, j = mid+1, k = l;
    	while(i <= mid && j <= r)
    	{
    		if(nums[idx[i]] <= nums[idx[j]])
    		{
    			ans[idx[i]] += j-(mid+1);
    			temp[k++] = idx[i++];
    		}
    		else
    		{
    			temp[k++] = idx[j++];
    		}
    	}
    	while(i <= mid)
    	{
    		ans[idx[i]] += j-(mid+1);//or ans[idx[i]] += r-mid;
			temp[k++] = idx[i++];
    	}
    	while(j <= r)
    	{
    		temp[k++] = idx[j++];
		}
    	for(i = l; i <= r; ++i)
    		idx[i] = temp[i];
    }
};

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Michael阿明

如果可以,请点赞留言支持我哦!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值