• DS实验题 Inversion


    题目:

    解题过程:

    第一次做这题的时候,很自然的想到了冒泡和选择,我交的代码是用选择写的。基本全WA(摊手)。

    贴上第一次的代码:

    //  
    //  main.cpp  
    //  sequenceschange  
    //  
    //  Created by wasdns on 16/10/7.  
    //  Copyright ? 2016年 wasdns. All rights reserved.  
    //  
      
    #include <iostream>  
    #include <string>  
    #include <cstdio>  
    #include <algorithm>  
    using namespace std;  
      
    int seq[50005];  
      
    int main() {  
          
        int i, j, n;  
          
        cin >> n;  
        for (i = 0; i < n; i++) {  
            cin >> seq[i];  
        }  
          
        int cnt = 0;  
        int t = 0;  
        for (i = n - 1; i >= 0; i--) {  
              
            for (j = i - 1; j >= 0; j--) {  
                  
                if(seq[i] > seq[j]) break;  
                  
                cnt ++;  
                  
                t = seq[i];  
                seq[i] = seq[j];  
                seq[j] = t;  
            }  
        }  
          
        cout << cnt << endl;  
          
        return 0;  
    }
    
    

    冒泡和选择,这两种算法的问题在于,会有重复比较的元素,不满足题意要求的最小比较次数。

    本题也是比较经典的逆序对问题,解决的方法是插入排序。

    这里给出链接参考:九大排序算法再总结

    第二种算法(分治加插入排序)思想:

    给出一个数组,以及给出左边界l右边界r代表需要排序的序列范围,将这个序列不断分成两个部分,直到递归到单个元素再向上进行Union的操作,Union的操作通过插入排序来实现。

    //
    //  main.cpp
    //  逆序对
    //
    //  Created by wasdns on 16/12/1.
    //  Copyright © 2016年 wasdns. All rights reserved.
    //
    
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    int a[50010];
    
    /*
        用来debug的函数
    */
    void printa(int l, int r)
    {
        for (int i = l; i <= r; i++) {
            printf("%d ", a[i]);
        }
        printf("
    ");
    }
    
    /*
        交换函数
    */
    void swap(int i, int j)
    {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
    
    /*
        Union 两部分合并函数
    */
    int Union(int l, int r)
    {
        if (l == r) return 0;
        
        int i, p1, p2;                        //p1指向序列A的元素,p2指向序列B的元素
        
        int cnt = 0;                          //记录比较次数
        
        for (i = l+1; i <= r; i++)
        {
            p1 = i-1;
            p2 = i;
        
            while (a[p2] < a[p1] && p1 != 0)
            {
                swap(p1, p2);
                
                cnt++;
                
                p1--;
                p2--;
            }
        }
        
        return cnt;
    }
    
    /*
        排序算法主体
    */
    
    int divsort(int l, int r)
    {
        //printf("div:");
        //printa(l, r);
        
        if (l == r) return 0;
        
        int cnt = 0;
        
        int mid = (l+r) / 2;
        
        int ltime = divsort(l, mid);
        int rtime = divsort(mid+1, r);
        
        cnt += ltime;
        cnt += rtime;
        
        cnt += Union(l, r);
        
        //printf("afterdiv:");
        //printa(l, r);
        
        return cnt;
    }
    
    void Initial(int n)
    {
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
        }
    }
    
    int main()
    {
        int n;
        
        cin >> n;
        
        Initial(n);
        
        int cnt;
        
        cnt = divsort(1, n);
        
        printf("%d
    ", cnt);
        
        return 0;
    }
    
    

    结果是部分点TLE:

    分析下算法复杂度:选择最后一次的Union操作,最坏情况下一共有50000个节点,从l到mid是25000个排好序的元素,从mid开始到r有25000个无序的元素,将后面的25000个元素插入到前面的序列,>=25000*25000的时间;复杂度为O(n^2),因此TLE。

    于是选择使用归并排序,之前的操作和算法二的思想类似,但是修改了Union的操作:维护一个数组b,使用两个指针指向两个待排序的序列,把这两个指针分别指向的元素进行比较,较小的元素加入到b中,指向它的指针后移,直到到达边界。

    另外一个需要care的点是cnt的计数

    倘若先前的序列叫做A,后面的序列叫做B,当判断出B序列的当前指向元素较小进入数组b时,相当于是通过mid+1-p1(p1是当前指针指向A的位置)次比较的操作将元素移至有序的位置。

    int Union(int l, int r)
    {
        if (l == r) return 0;
        
        int mid = (l+r) / 2;
        
        int i, p1 = l, p2 = mid+1;
        
        int cnt = 0;
        
        int tot = 1;
        
        while (p1 <= mid && p2 <= r)
        {
            if (a[p1] > a[p2]) {
                
                cnt += mid+1-p1;          //相当于比较了mid+1-p1次
                
                b[tot++] = a[p2++];
            }
            
            else b[tot++] = a[p1++];
        }
        
        if (p1 > mid && p2 <= r)
        {
            while (p2 <= r)
            {
                b[tot++] = a[p2++];
            }
        }
        
        else if (p2 > r && p1 <= mid)
        {
            while (p1 <= mid)
            {
                b[tot++] = a[p1++];
            }
        }
        
        for (i = l; i <= r; i++)
        {
            a[i] = b[i-l+1];
        }
        
        return cnt;
    }
    
    int divsort(int l, int r)
    {
        //printf("div:");
        //printa(l, r);
        
        if (l == r) return 0;
        
        int cnt = 0;
        
        int mid = (l+r) / 2;
        
        int ltime = divsort(l, mid);
        int rtime = divsort(mid+1, r);
        
        cnt += ltime;
        cnt += rtime;
        
        cnt += Union(l, r);
        
        //printf("afterdiv:");
        //printa(l, r);
        
        return cnt;
    }
    
    

    算法复杂度:O(n)

    2016/12/2

  • 相关阅读:
    Nginx的反向代理和负载均衡
    大数据开发——Hive笔记
    Mysql-索引分析查询性能
    数据库原理-事务隔离与多版本并发控制(MVCC)
    并发编程实战--知识图谱
    并发编程实战-线程池
    并发编程实战-保证线程安全方式
    Synchronized和ReentTrantLock二者区别
    并发编程实战-J.U.C核心包
    并发编程实战-ConcurrentHashMap源码解析
  • 原文地址:https://www.cnblogs.com/qq952693358/p/6124225.html
Copyright © 2020-2023  润新知