算法学习5.3分治算法例子

例题1:输出前m大的数

描述
给定一个数组包含n个元素,统计前m大的数并且把这m个数从大到小输出。
输入
第一行包含一个整数n,表示数组的大小。n < 100000。
第二行包含n个整数,表示数组的元素,整数之间以一个空格分开 。每个整数的绝对值不超过100000000。
第三行包含一个整数m。m < n。
输出
从大到小输出前m大的数,每个数一行。
排序后再输出,复杂度 O(nlogn)

用分治处理:复杂度 O(n+mlogm)
思路:把前m大的都弄到数组最右边,然后对这最右边m个元素排序,再输出
关键 :O(n)时间内实现把前m大的都弄到数组最右边
引入操作 arrangeRight(k): 把数组(或数组的一部分)前k大的都弄到最右边

如何将前k大的都弄到最右边

1) 设key=a[0], 将key挪到适当位置,使得比key小的元素都在key左边,比key大的元素都在key右边(线性时间完成)
在这里插入图片描述
在这里插入图片描述

输出n个数中前m个大的数可以采用先排序在取前m个大的数,这个最好的时间复杂度为O(nlogn)。可以利用快排的思想加快时间复杂度,下面算法时间复杂度为O(n+mlogm),若m<<n时,则时间复杂度约为O(n)。

代码段

#include<iostream>
 
using namespace std;
int a[100];
 
void swap(int &a, int &b)
{
	int temp;
	temp = b;
	b = a;
	a = temp;
}
void QuickSort(int a[], int s, int e, int m)
{
	if (s >= e)
		return;
	int k = a[s];
	int i = s, j = e;
	while (i != j)
	{
		if (j > i && a[j] > k)
			j--;
		swap(a[i], a[j]);
		if (j > i && a[i] < k)
			i++;
		swap(a[i], a[j]);
	}
 
	int b = (e - j) + 1; // 判断右边大的数有几个
	if (m > b)           // 若小于m个,在左边再取m-b个
		QuickSort(a, s, i, m - b);
	if (m < b)           // 若大于m个,在右边再去m个
		QuickSort(a, j + 1, e, m);
}
 
int main()
{
	int m,n;
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	QuickSort(a, 0, n - 1, m);
	for (int i = n-1; i >= n-m; i--)
		cout << a[i] << ' ';
	cout << endl;
	system("pause");
}

例题2:求排列的逆序数

考虑1,2,…,n (n <= 100000)的排列i1,i2,…,in,如果其中存在j,k,满足 j < k 且 ij > ik, 那么就称(ij,ik)是这个排列的一个逆序。
一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。
现给定1,2,…,n的一个排列,求它的逆序数。

笨办法:O(n2)

分治:O(nlogn):

1) 将数组分成两半,分别求出左半边的逆序数和右半边的逆序数
2) 再算有多少逆序是由左半边取一个数和右半边取一个数构成(要求O(n)实现的关键:左半边和右半边都是排好序的。比如,都是从大到小排序的。这样,左右半边只需要从头到尾各扫一遍,就可以找出由两边各取一个数构成的逆序个数

总结:

由归并排序改进得到,加上计算逆序的步骤

MergeSortAndCount: 归并排序并计算逆序数

#include <iostream>
using namespace std;
long long sum = 0;//逆序数的个数 
void MergeSort(int * arr,int s,int e,int *temp);//归并排序
void MergeAndCountNum(int * arr,int s,int mid,int e,int *temp); //归并有序序列并计算逆序数的个数 
int main(){
	int a[100001],b[100001],n;
	cin >> n;
	for(int i=0;i<n;++i)
		cin >> a[i];
	MergeSort(a,0,n-1,b);
	cout << sum ;
	return 0;
} 
 
void MergeSort(int * arr,int s,int e,int * temp){//归并排序 
	if(s < e){ 
		int mid = s + (e - s)/2;
		MergeSort(arr,s,mid,temp);
		MergeSort(arr,mid+1,e,temp);
		MergeAndCountNum(arr,s,mid,e,temp);
	}
}
void MergeAndCountNum(int * arr,int s,int mid,int e,int * temp){//归并有序序列并计算逆序数的个数 
	int p1 = s, p2 = mid +1,index = 0;
	while(p1 <= mid && p2 <= e){
		if(arr[p1] < arr[p2])
			temp[index++] = arr[p1++];
		else{
			temp[index++] = arr[p2++];
			sum += mid - p1 +1;
		}
	}
	while(p1 <= mid)
		temp[index++] = arr[p1++];
	while(p2 <= e)
		temp[index++] = arr[p2++];
	for(int i = 0;i<e - s +1;++i)
		arr[s+i] = temp[i];
}

发布了20 篇原创文章 · 获赞 1 · 访问量 155

猜你喜欢

转载自blog.csdn.net/weixin_42503072/article/details/104459409