|
题目 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。 示例1:输入:[7,5,6,4]输出:5 示例2:输入:[10,9,8,7,6,5,4,3,2,1]输出:45暴力法 暴力法的基本思想是:通过两层循环来遍历数组中的每一个元素,并与它后面的所有元素进行比较;如果前面的数大于后面的数,则构成一个逆序对,并增加计数器。使用暴力法求解本题的主要步骤如下。 1、初始化一个计数器count为0,用于记录逆序对的数量。 2、遍历数组中的每一个元素i,对于每一个i,再从i+1到数组的末尾进行遍历,检查是否存在比i更大的元素。 3、如果存在,则将计数器count加一。 4、当所有的元素都被检查过后,返回计数器count的值。 根据上面的算法步骤,我们可以得出下面的示例代码。defcalc_inversion_pairs_by_brute_force(nums):count=0#遍历数组中的每一个元素foriinrange(len(nums)):#再遍历该元素之后的所有元素forjinrange(i+1,len(nums)):#如果前一个元素大于后一个元素,则构成逆序对ifnums[i]>nums[j]:count+=1returncountnums1=[7,5,6,4]print(calc_inversion_pairs_by_brute_force(nums1))nums2=[10,9,8,7,6,5,4,3,2,1]print(calc_inversion_pairs_by_brute_force(nums2))归并排序法 归并排序法的基本思想是:采用分治策略,也就是将数组分成两半,分别计算每半部分的逆序对数量,然后再计算跨这两半部分的逆序对数量。使用归并排序法求解本题的主要步骤如下。 1、将数组分成两半,递归地计算左边一半的逆序对数量和右边一半的逆序对数量。 2、合并两个已排序的半边数组时,统计跨两边的逆序对数量。 3、返回总的逆序对数量。 根据上面的算法步骤,我们可以得出下面的示例代码。defmerge_sort_array(nums,temp,left,right):#当数组只有一个元素时,不需要排序ifleft==right:return0#分割数组mid=(left+right)//2inversions=merge_sort_array(nums,temp,left,mid)inversions+=merge_sort_array(nums,temp,mid+1,right)#合并两个有序数组并计算逆序对i,j,pos=left,mid+1,leftwhilei0:result+=self.tree[index]index-=self.low_bit(index)returnresultdefcalc_inversion_pairs_by_bit(nums):#映射数组元素到连续整数区间unique_nums=sorted(set(nums))num_to_index={num:idx+1foridx,numinenumerate(unique_nums)}count=0#初始化树状数组bit=BinaryIndexedTree(len(num_to_index))#遍历数组fornuminreversed(nums):#将当前元素映射到对应的索引index=num_to_index[num]#查询逆序对数量count+=bit.query(index-1)#更新树状数组bit.update(index,1)returncountnums1=[7,5,6,4]print(calc_inversion_pairs_by_bit(nums1))nums2=[10,9,8,7,6,5,4,3,2,1]print(calc_inversion_pairs_by_bit(nums2))总结 显而易见,暴力法的时间复杂度为O(n^2),空间复杂度为O(1)。其中,n是数组的长度。其优点是实现简单,易于理解。但缺点也比较明显:效率低下,不适合处理大数据集。 归并排序法的时间复杂度为O(n*logn),空间复杂度为O(n),因为需要一个与原始数组相同大小的辅助数组用于合并操作。归并排序法是稳定的排序算法,适用于所有类型的输入。 树状数组法的时间复杂度和空间复杂度与归并排序法相同,但其实现比归并排序法简单。缺点是需要对数组中的元素进行预处理,将其映射到连续的整数区间。💡需要《Python面试宝典》完整源码的大佬们,可订阅专栏后,搜索微信公众号“希望睿智”私信获取。
|
|