计算右侧小于当前元素的个数【php版】

47 篇文章 1 订阅

在这里插入图片描述

方法1: 树状数组动态维护前缀和 (关于什么是树状数组,网上介绍的有很多,这儿不作赘述)


	/**
	 * 使用树状数组
	 * @param Integer[] $nums
	 * @return Integer[]
	 */
	function countSmaller($nums) {
		$len = count($nums);
		if ($len == 0) {
			return [];
		}
		// 将nums中的元素去重再排序
		$uniqueArr = [];
		foreach ($nums as $num) {
			$uniqueArr[$num] = 1;
		}
		$uniqueArr = array_keys($uniqueArr);
		sort($uniqueArr);
		$uniqueArr = array_flip($uniqueArr);
		$size = count($uniqueArr);

		// 初始化树状数组C
		$c = array_fill(1, $size, 0);

		// 函数用于返回nums中的元素对应在C中的索引(从1开始)
		$findIdx = function($num) use ($uniqueArr) {
			return $uniqueArr[$num] + 1;
		};

		// 获取某数的低位码数,如6(110)  返回 2(10)
		$getLowBit = function($x) {
			return $x & (-$x);
		};
		// 更新C中的数据
		$updateC = function($idx) use (&$c, $size, $getLowBit) {
			while ($idx <=  $size) {
				$c[$idx]++;
				$idx += $getLowBit($idx);
			}
		};
		// 结果数组
		$updateRlt = function($i, $c, &$rlt, $idx) use ($getLowBit) {
			$rlt[$i] = 0;
			while ($idx > 0) {
				$rlt[$i] += $c[$idx];
				$idx -= $getLowBit($idx);
			}
		};
		$rlt = new SplFixedArray($len);
		// 倒序遍历nums
		for ($i=$len-1; $i>=0; $i--) {
			// 获取nums[i]对应的c中的索引
			$idx = $findIdx($nums[$i]);
			// 更新c中的次数
			$updateC($idx);

			// 更新结果数组
			$updateRlt($i, $c, $rlt, $idx-1);
		}
		return $rlt->toArray();
	}

方法二: 归并排序法

<?php
class Solution {
	private $rlt = null;

	/**
	 * 方法二,归并排序法
	 * @param $nums
	 */
	function countSmallerV2($nums) {
		$len = count($nums);
		if ($len == 0) {
			return [];
		}
		// 为了维持排序的元素索引,此处将索引和原数组同步进行归并排序
		$idxArr = array_keys($nums);
		$arrForMerge = new SplFixedArray($len);
		$arrForIdx = new SplFixedArray($len);
		$this->rlt = array_fill(0, $len, 0);

		$this->sort($nums, $idxArr, 0, $len-1, $arrForMerge, $arrForIdx);

		return $this->rlt;
	}

	function sort(&$nums, &$idxArr, $begin, $end, &$arrForMerge, &$arrForIdx) {
		if ($begin >= $end) {
			return;
		}

		$mid = $begin + intdiv($end-$begin, 2);
		$this->sort($nums, $idxArr, $begin, $mid, $arrForMerge, $arrForIdx);
		$this->sort($nums, $idxArr,$mid+1, $end, $arrForMerge, $arrForIdx);

		$this->merge($nums, $idxArr, $begin, $mid, $end, $arrForMerge, $arrForIdx);
	}

	function merge(&$nums, &$idxArr, $begin, $mid, $end, &$arrForMerge, &$arrForIdx) {
		$i = $begin;
		$j = $mid+1;
		$k = $begin;

		while ($i <= $mid && $j <= $end) {
			if ($nums[$i] <= $nums[$j]) {
				$arrForMerge[$k] = $nums[$i];
				$arrForIdx[$k] = $idxArr[$i];
				// 关键处在此,当左边的数小于等于右边的数时,已经排序好了的在临时数组中的右边的数(这些数小于当前左边的数)的个数就是原数组中在左边数右侧且小于左边数的贡献值,将其累加
				// 这儿的idexArr[i]体现了索引数组与原数组同步排序的好处,可以拿到元素在原数组中的索引。
				$this->rlt[$idxArr[$i]] += $j - $mid - 1;
				$k++;
				$i++;
			} else {
				$arrForMerge[$k] = $nums[$j];
				$arrForIdx[$k] = $idxArr[$j];
				$j++;
				$k++;
			}
		}
		while ($i <= $mid) {
			$arrForMerge[$k] = $nums[$i];
			$arrForIdx[$k] = $idxArr[$i];

			$this->rlt[$idxArr[$i]] += $j - $mid - 1;

			$k++;
			$i++;
		}

		while ($j <= $end) {
			$arrForMerge[$k] = $nums[$j];
			$arrForIdx[$k] = $idxArr[$j];
			$j++;
			$k++;
		}

		for ($i=$begin; $i<$k; $i++) {
			$nums[$i] = $arrForMerge[$i];
			$idxArr[$i] = $arrForIdx[$i];
		}
	}
}
$s = new Solution();
var_dump($s->countSmallerV2([5,2,6,1]));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值