目录
![](https://img-blog.csdnimg.cn/direct/90a4be20195744ef8e9bc7fdc357bd58.webp)
A.分治的基本概念
把一个任务,分成形式和原任务相同,但规模更小的几个部分任务(通常是两个部分),分别完成,或只需要选一部完成。然后再处理完成后的这一个或几个部分的结果,实现整个任务的完成。
B.归并排序
数组排序任务可以如下完成:
- 把前一半排序
- 把后一半排序
- 把两半归并到一个新的有序数组,然后再拷贝回原数组,排序完成。
步骤如下:
![](https://img-blog.csdnimg.cn/direct/e157b95d2bc940eeb4caaaa5b8aedfca.png)
![](https://img-blog.csdnimg.cn/direct/1639e594e5ce4d479a0ca5d4524dfa9d.png)
![](https://img-blog.csdnimg.cn/direct/21bf599f7b314c39b26e876c7c8d1d4f.png)
![](https://img-blog.csdnimg.cn/direct/63c6fde08ec746f9891b14fc8f1f7f70.png)
#include <stdio.h>
int a[10]={ 13,27,19,2,8,12,2,8,30,89};
int b[10];
void Merge(int a[],int s,int m,int e,int tmp[])
{//将数组a的局部a[s,m]和a[m+1,e]合并到tmp,并保证tmp有序,然后再拷贝回a[s,m]
int pb = 0;
int p1 = s, p2 = m+1;
while( p1<=m && p2<=e)
{
if( a[p1] < a[p2])
tmp[pb++] = a[p1++];
else
tmp[pb++] = a[p2++];
}
while( p1 <= m)
{
tmp[pb++] = a[p1++];
}
while( p2 <= e)
{
tmp[pb++] = a[p2++];
}
for(int i=0;i < e-s+1;i++)
{
a[s+i] = tmp[i];
}
}
void MergeSort(int a[],int s,int e,int tmp[])
{
if( s<e)
{
int m = s + (e-s)/2;
MergeSort(a,s,m,tmp);
MergeSort(a,m+1,e,tmp);
Merge(a,s,m,e,tmp);
}
}
int main()
{
int size = sizeof(a)/sizeof(int);
MergeSort(a,0,size-1,b);
for(int i = 0;i < size; ++i)
{
printf("%d",a[i]);
}
printf("\n");
return 0;
}
C.快速排序
数组排序任务可以如下完成:
- 设k=a[0], 将k挪到适当位置,使得比k小的元素都在k左边,比k大的元素都在k右边,和k相等的,不关心在k左右出现均可 (O(n)时间完成)
- 把k左边的部分快速排序
- 把k右边的部分快速排序
步骤如下:
![](https://img-blog.csdnimg.cn/direct/92c9931b678a47e78b4c6ab3b37e3e40.png)
![](https://img-blog.csdnimg.cn/direct/e67b51ef5f474cf6b7f0a32e915fded7.png)
![](https://img-blog.csdnimg.cn/direct/e656e9a97c994d06b5530f3d32bf94ed.png)
![](https://img-blog.csdnimg.cn/direct/4ac495315b9c4d33bfebe5d5c7c30bac.png)
![](https://img-blog.csdnimg.cn/direct/2aead5a20f224bfd8ad1ed419d8d77b4.png)
![](https://img-blog.csdnimg.cn/direct/0276abacba0b4e5197f18518b2d7d3ee.png)
![](https://img-blog.csdnimg.cn/direct/105d716ef87947b78cd633e999f5c80e.png)
![](https://img-blog.csdnimg.cn/direct/dfec2538a66946478fcd032f248001ca.png)
![](https://img-blog.csdnimg.cn/direct/7aadab7a00b447b7bf85f9b92b98c6d3.png)
#include <stdio.h>
int a[10]={ 93,27,30,2,8,12,2,8,30,89};
void swap(int *a,int *b)//交换变量a,b值
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
void QuickSort(int a[],int s,int e)
{
if( s>=e)
return;
int k = a[s];
int i = s, j = e;
while( i != j )
{
while( j>i && a[j]>=k )
{
j--;
}
swap(a[i],a[j]);
while( i<j && a[i]<=k )
{
i++;
}
swap(a[i],a[j]);
}//处理完后,a[i] = k;
QuickSort(a,s,i-1);
QuickSort(a,i+1,e);
}
int main()
{
int size = sizeof(a)/sizeof(int);
QuickSort(a,0,size-1);
for(int i = 0;i < size; ++i)
{
printf("%d",a[i]);
}
printf("\n");
return 0;
}
D.输出前m大的数
题目描述
给定一个数组包含n个元素,统计前m大的数并且把这m个数从大到小输出。
输入
第一行包含一个整数n,表示数组的大小。n < 100000。
第二行包含n个整数,表示数组的元素,整数之间以一个空格分开 。每个整数的绝对值不超过100000000。
第三行包含一个整数m。m < n。
输出
从大到小输出前m大的数,每个数一行。
题目解答
- 解法一:
排序后再输出。
- 解法二:
用分治处理:
思路:把前m大的都弄到数组最右边,然后对这最右边m个元素排序,再输出
关键 :O(n)时间内实现把前m大的都弄到数组最右边1) 设key=a[0], 将key挪到适当位置,使得比key小的元素都在key左边,比key大的元素都在key右边(线性时间完成)
2) 选择数组的前部或后部再进行 arrangeRight操作引入操作 arrangeRight(k): 把数组(或数组的一部分)前k大的都弄到最右边
#include <stdio.h>
void swap(int *a,int *b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
void ArrangeRight(int *a,int left,int right,int key)
{
if(left >= right)
return;
int mid = a[left];
int i = left, j = right;
while(i != j)
{
while( j > i && a[j] >= mid)
j--;
swap(a[i],a[j]);
while( j > i && a[i] <= mid)
i++;
swap(a[i],a[j]);
}
if( right-i+1 > key)
return ArrangeRight(a,i+1,right,key);
else if( right-i+1 < key)
return ArrangeRight(a,0,i-1,key-(right-i+1) );
}
int main()
{
int n, num[100050];
scanf("%d",&n);
for(int i = 0;i < n; i++)
{
scanf("%d",&num[i]);
}
int m;
scanf("%d",&m);
ArrangeRight(num,0,n-1,m);
sort(num+(n-m),num+n);
for(int i = 0,j = n-1;i < m; i++, j--)
{
printf("%d",num[j]);
}
}
E.求排列的逆序数
题目描述
考虑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的一个排列,求它的逆序数。
题目解答
1) 将数组分成两半,分别求出左半边的逆序数和右半边的逆序数
2) 再算有多少逆序是由左半边取一个数和右半边取一个数构成(要求O(n)实现)——2) 的关键:左半边和右半边都是排好序的。
比如,都是从大到小排序的。这样,左右半边只需要从头到尾各扫一遍,就可以找出由两边各取一个数构成的逆序个数
![](https://img-blog.csdnimg.cn/direct/54470c634f63461381430c1555a93753.png)
![](https://img-blog.csdnimg.cn/direct/27c582df857c4aac9ea612dbfb9cc7ce.png)
![](https://img-blog.csdnimg.cn/direct/342b1d212c974c999949481dc1666455.png)
![](https://img-blog.csdnimg.cn/direct/cdca66e56b01409b96eb787ef0e6d7dc.png)
![](https://img-blog.csdnimg.cn/direct/5b9b62977e904b8a92b9df54328d4d0e.png)
![](https://img-blog.csdnimg.cn/direct/da88456aaf33448fa0f6751ceb85106a.png)
![](https://img-blog.csdnimg.cn/direct/7e966924de364cff8ae65bb09f44a78c.png)
#include <stdio.h>
int mergeSortAndCount(int a[], int left, int mid, int right)
{
int len1 = mid - left + 1;//左半部分lenth
int len2 = right - mid;//右半部分lenth
int al[len1], ar[len2];
int count = 0; //逆序数
int i, j;
for(i = 0; i < len1; i++)//左半部分数组
al[i] = a[left+i];
for(i = 0; i < len2; i++)//右半部分数组
ar[i] = a[mid+1+i];
i = 0;
j = 0;
int k;
for(k = left; k <= right; k++)
{
if(i >= len1)
{
a[k] = ar[j];
j++;
}
else if(j >= len2)
{
a[k] = al[i];
i++;
}
else if(al[i] > ar[j])
{
a[k] = al[i];
i++;
count += len2-j;
}
else
{
a[k] = ar[j];
j++;
}
}
return count;
}
int merge(int a[], int left, int right)
{
int mid = left + (right-left)/2;
int count = 0;
if(left < right)
{
count += merge(a, left, mid);
count += merge(a, mid+1, right);
count += mergeSortAndCount(a, left, mid, right);
}
return count;
}
int main()
{
int a[100];
int n;
scanf("%d", &n);
for(int i = 0; i< n; i++)
scanf("%d", &a[i]);
printf("逆序数为:%d\n", merge(a, 0, n-1) );
return 0;
}