映射算法_反排序算法_自创

版权声明:看我干嘛? 你又没打算转载我的博客~ https://blog.csdn.net/wjh2622075127/article/details/79947888

—————————2018-04-17更新————————

最下面有更新

—————————以下原博客————————

今天做2018年3月份ccf认证第二题的时候, 有个过程是关于对位映射的问题.

怎样的呢?

比如这样两个序列:

14 7 8
5 57 32

把他们排序之后分别是

7 8 14
5 32 57

那么

14 映射 57
7 映射 5
8 映射 32

目的是输出序列

57 5 32

怎么做呢?

用最暴力的方法, 先备份第一个序列, 然后分别对两个序列排序, 然后 遍历之前备份的第一个序列的每一个元素, 对已排序的第一序列进行查找比较, 如果和备份元素相同, 则输出同下标的第二个序列元素, 由此可以得到备份序列.

这种方法实现起来简单, 也非常容易理解, 但是, 时间复杂度为O(nlogn + n^2), nlogn排序, n^2映射. 等价于n^2, 消耗的资源过大.

以下代码

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 7;

void Function(int *a, int*b)
{
    int c[maxn]; //备份 
    for(int i = 0; i < maxn; ++i) {
        c[i] = a[i]; 
    }
    sort(a, a + maxn);
    sort(b, b + maxn);
    for(int i = 0; i < maxn; ++i) {
        for(int j = 0; j < maxn; ++j) {
            if(c[i] == a[j]) cout << b[j] << ' ';
        }
    }
}

int main()
{
    int a[maxn] = {4, 34, 7, 3, 1, 23, -3};  //无重复 
    int b[maxn] = {334, 4, -1, 34, -45, 456, 71};

    //想得到一个34, 456, 71, 4, -1, 334, -45序列
    Function(a, b); 
}

我经过仔细思考研究之后, 找到一种在第一个序列全为整数, 或某些适合情况的小数, 总之范围不是非常大的算法. 特点和桶排序很相似.

我从未见过关于这个问题的算法, 所以大言不惭地认为这个算法是我自创的. 也许将来会见到有人用这个方法, 但至少现在是我自己完全独立想出来的, 没有借鉴别人的.

思路比较复杂, 大致是找到数和排序的下标之间的关系, 得到一种映射, 这种映射的存储是离散化的, 就像桶排序的存储, 用数组可能会浪费大量空间.

哦! 突然想到了一种空间上的方法. 把这两个序列的元素都当时字符串处理, 然后用树存储, 似乎可以节省不少空间.


为什么我写着写着, 感觉STL里面map就是这样用这种类似的思路实现的啊, 有点方…

我觉得看代码可能更好理解, 里面有一点玄玄的东西.

注意: 这个思路对数据要求可能比较高

时间复杂度是O(nlongn + n), nlogn排序, n映射. 等价于nlogn

#include <iostream>
#include <algorithm>
using namespace std;

const int maxn = 7, MAX = 1000;
int a[maxn] = {4, 34, 7, 3, 1, 23, -3};  //无重复 
int b[maxn] = {334, 4, -1, 34, -45, 456, 71};

bool cmp_a(int x, int y)
{
    return a[x] < a[y];
}

bool cmp_b(int x, int y)
{
    return b[x] < b[y];
}

void Function(int *a, int *b, int *a_a, int *b_b)
{
    int a_map[MAX] = {};
    sort(a_a, a_a + maxn, cmp_a);
    sort(b_b, b_b + maxn, cmp_b);
    for(int i = 0; i < maxn; ++i) {
        a_map[a[a_a[i]]] = i;
    }
    for(int i = 0; i < maxn; ++i) {
        cout << b[b_b[a_map[a[i]]]] << ' ';
    }
}

int main()
{
    int a_a[maxn], b_b[maxn];
    for(int i = 0; i < maxn; ++i) {
        a_a[b_b[i] = i] = i;
    }

    //想得到一个34, 456, 71, 4, -1, 334, -45序列
    Function(a, b, a_a, b_b); 
}

见笑

大佬轻喷 orz

—————————以下2018-04-17更新————————

再研究了以下, 发现这种思路有更好理解, 更加简单的实现方法.

用这种思路解决问题的本质是什么呢?

就是获取a序列中每一个元素的的排名, 然后再b序列中找相同排名的数输出即可. 得到了一个排名, 找b序列中的相同排名元素非常简单, 只要给b排个序, 然后输出排名对应的角标元素即可. 现在的问题就是, 如何找到每个元素(按原序列顺序)的排名?

我想到了两种方法, 我觉得每一种都很有启迪.

第一种

要对a序列排序而不改变a的原有序列, 那么最好的方法就是给a的下标数组aa排序, a[aa[0…n-1]即为a排序后的序列, 而aa数组的值, 是一个[0, n)范围的值, 它们分别对应下标. 事实上, 它们也是有”大小”的. 它们的大小对应着原序列a的位置, 也就是说, 再对aa这个数组按照它的值排序, 就可以得到原来的序列.

从而又可以解决一类问题: 给序列排序后还原序列, 获取原序列每一个元素的排名, 或者说叫做反排序

注意: 常规的排序是获取当前排名的数, 而此算法解决的问题是, 获取元素的排名

本博客的这个问题正是使用了这种方法的特性, 两次下标排序, 得到了这两种序列的性质.

那么, 之前的问题就迎刃而解了. 思路比此前的思路更清晰, 也更简单简洁

时间复杂度还是O(nlogn), 不过实现起来简单多了. 也没有了此前介绍的类似桶排序存储对数据类型有要求的限制, 进步还是非常大的

#include <iostream>
#include <algorithm>
using namespace std;

int a[5] = {12, 34, 4, 33, 7};
int b[5] = {3, 44, 90, 4, 38};
int a_a[5], aa[5];

bool cmp(int x, int y)
{
    return a[x] < a[y];
}

bool cmp_2(int x, int y)
{
    return aa[x] < aa[y];
}

int main()
{
    for(int i = 0; i < 5; ++i) {
        a_a[aa[i] = i] = i;
    }
    sort(aa, aa + 5, cmp);
    sort(b, b + 5);
    sort(a_a, a_a + 5, cmp_2);

    cout << "原来的a序列:\n"; 
    for(int i = 0; i < 5; ++i) {
        cout << a[i] << ' ';
    }
    cout << endl;
    cout << "排序后的b序列:\n";
    for(int i = 0; i < 5; ++i) {
        cout << b[i] << ' ';
    }
    cout << endl << endl;
    for(int i = 0; i < 5; ++i) {
        cout << a[i] << " rank is " << a_a[i] << endl;
    }
    cout << endl;
    for(int i = 0; i < 5; ++i) {
        cout << a[i] << " 映射: " << b[a_a[i]] << endl; 
    }
}

第二种

这种算法的时间复杂度为O(R), R为元素的取值范围.

思路是桶排序的思路, 如果数据个数(整数)非常接近R, 那么这种方法是比较合算的. 相对来说空间上不会有那么大浪费.

可是什么样的序列会出现这种情况呢?

  1. 对0-n的所有数组成的乱序列进行排列, 这算是特殊情况了.
  2. 类似下标排序的, 下标的排序意味着顺序, 而非简单的大小, 或许从这种思路上出发, 可能会解决一些意想不到的问题. 但我还没有遇到过, 所以姑且做做猜想.

代码实现不算复杂

#include <iostream>
using namespace std;

const int Range = 0xfffff;
int rank[Range] = {};
int rankb[Range] = {};

int main()
{
    int a[5] = {12, 34, 4, 33, 7};
    int b[5] = {3, 44, 90, 4, 38};
    for(int i = 0; i < 5; ++i) {
        rank[a[i]] = 1;
    }
    int ran = 0;
    for(int i = 0; i < Range; ++i) {
        if(rank[i]) rank[i] = ran++;
    }
    ran = 0;
    for(int i = 0; i < 5; ++i) {
        rankb[b[i]] = 1;
    }
    for(int i = 0; i < Range; ++i) {
        if(rankb[i]) b[ran++] = i; 
    }
    cout << endl;
    cout << "b排序后的序列:\n";
    for(int i = 0; i < 5; ++i) {
        cout << b[i] << ' ';
    }
    cout << endl;
    cout << "\na的每个元素的排名\n";
    for(int i = 0; i < 5; ++i) {
        cout << a[i] << ' ' << rank[a[i]] << endl;
    }
    for(int i = 0; i < 5; ++i) {
        cout << a[i] << " 映射 " << b[rank[a[i]]] << '\n';
    }
    cout << endl;
}

—————————以上————————

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页