数据结构(1) 线性表技巧及应用:前缀和、排序(逆序对求法之一)

虽然线性表实在过于简单,几乎不会有大佬写它的应用
但是作为一个菜鸡的我还是打算归纳总结一下线性表一些应用和技巧

1.前缀和

emmmm
我们来看这样一个问题
已知一个序列s[ i ] (1<=i<=n),有m个请求,每个请求为两个整数a,b(1<=a<=b<=b)
i=abs[i]\sum_{i=a}^bs[i]
最朴实的求法显然是枚举s[a]到s[b]的所有数求和
时间复杂度是O(nm)
能否优化呢
我们采用一种名为前缀和的技术
即预处理出一个数组c[ i ] 来保存s[ i ] 的前缀和
c[i]=j=1is[j]c[i]=\sum_{j=1}^is[j]
那么i=abs[i]=c[b]c[a1]\sum_{i=a}^bs[i]=c[b]-c[a-1]
就很显然了求c[b]-c[a-1]的时间复杂度为O(1)
所以我们优化到了O(n+m)的复杂度
6666

2.排序(这也算?)

此处无视选择排序和冒泡排序
先讲排序的方法
常用的排序:
1.快速排序
快速排序是最常用的排序方法(因为平均复杂度最优)
在通常情况下我们不会手写快速排序
我们使用c++的情况下:

#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
int a[1000],n;
bool cmp(int a,int b)
{	
	return a>b;
}
int main()
{
	scanf("%d",&n);
	for(int i = 1; i <= n ; i ++)
	{
		scanf("%d",&a[i]);
	}
	sort(a+1,a+1+n);//从小到大
	sort(a+1,a+1+n,cmp);//从大到小
}

使用c语言的情况下:

#include <stdio.h>
#include <stdlib.h>
int a[1000],n;
char cmp1(int* a,int* b)
{ 
 return *a-*b;
}
char cmp2(int* a,int* b)
{ 
 return *b-*a;
}
int main()
{
 scanf("%d",&n);
 for(int i = 1; i <= n ; i ++)
  {
   scanf("%d",&a[i]);
  }
  qsort(a+1,n,sizeof(int),cmp1);//从小到大
    for(int i = 1; i <= n ; i ++)
  {
   printf("%d ",a[i]);
  }
    puts("");
  qsort(a+1,n,sizeof(int),cmp2);//从大到小
    for(int i = 1; i <= n ; i ++)
  {
   printf("%d ",a[i]);
  }
}

2.归并排序
这种排序存在的最大意义就是求逆序对
和快速排序一样都是借助二分的思想,相比快速排序更加稳定,但是需要额外空间。
在此引用百度百科的归并排序定义
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

现在我们来讲逆序对的求法
首先什么是逆序对?
学过线性代数的请翻教材
没学过线性代数的请百度
那么如何利用归并排序的特点来完成逆序对的求解呢
在归并排序归并两个有序数列a,b时,
显然有会有在数列a的范围[l,m],数列b的下标范围[m+1,r]
那么对于下标b[j]的数,若将b[j]放入归并后的数列,且此时a的记录位置为i,显然增加了m+1-l个逆序对
对所有这些数求和就可以了!
附上练习题
洛谷P1966 火柴排队
https://www.luogu.org/problemnew/show/P1966

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;int b[100005],a[100005],temp[100005];long long n;
struct node{
 int num;int pos; 
}x[100005],y[100005];
bool cmp(node a,node b){
 return a.num<b.num ;
}
void merge(int l,int m ,int r){
 int i = l,j=m+1,k=l;
 while(i<=m&&j<=r){
  if(a[i]>a[j]){
   
   temp[k++]=a[j++];
   n+=m+1-i;n%=99999997;
  }
  else temp[k++]=a[i++];
  
 }
 while(i<=m)temp[k++]=a[i++];
 while(j<=r)temp[k++]=a[j++];
 for(int ii = l ; ii<=r ;ii++)
 a[ii]=temp[ii];
}
void mergesort(int l, int r){
 if(l<r){
 int mid = (l + r )/2;
 mergesort(l,mid);
 mergesort(mid+1,r);
 merge(l,mid,r);
}
} 
int main(){
 int num,m;
 scanf("%d",&num);
 for(int i = 0 ; i < num ; i ++)
 {
  scanf("%d",&m);
  x[i].num= m;
  x[i].pos=i;
 }
 for(int i = 0 ; i < num ; i ++)
 {
  scanf("%d",&m);
  y[i].num= m;
  y[i].pos= i;
 }
 sort(x,x+num,cmp);
 sort(y,y+num,cmp);
  for(int i = 0 ; i < num ; i ++)
 {
  a[x[i].pos]=y[i].pos ;
 }
 mergesort(0,num-1);
 cout<<n;
}

排序的其他应用包括贪心和便于二分查找等,我将来再发布新的专题

posted @ 2018-11-22 22:09  akonoh  阅读(481)  评论(0编辑  收藏  举报