zoukankan      html  css  js  c++  java
  • 数据结构(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;
    }

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

  • 相关阅读:
    day 66 ORM django 简介
    day 65 HTTP协议 Web框架的原理 服务器程序和应用程序
    jQuery的事件绑定和解绑 事件委托 轮播实现 jQuery的ajax jQuery补充
    background 超链接导航栏案例 定位
    继承性和层叠性 权重 盒模型 padding(内边距) border(边框) margin 标准文档流 块级元素和行内元素
    属性选择器 伪类选择器 伪元素选择器 浮动
    css的导入方式 基础选择器 高级选择器
    03-body标签中相关标签
    Java使用内存映射实现大文件的上传
    正则表达式
  • 原文地址:https://www.cnblogs.com/akonoh/p/10216767.html
Copyright © 2011-2022 走看看