5217. 将杂乱无章的数字排序

https://leetcode-cn.com/problems/sort-the-jumbled-numbers/

好好的一道重排序,结果看到一个天秀之子在秀c++语法

属实有点秀了,特打算做个笔记

5217. 将杂乱无章的数字排序

给你一个下标从 0 开始的整数数组 mapping ,它表示一个十进制数的映射规则,mapping[i] = j 表示这个规则下将数位 i 映射为数位 j 。

一个整数 映射后的值 为将原数字每一个数位 i (0 <= i <= 9)映射为 mapping[i] 。

另外给你一个整数数组 nums ,请你将数组 nums 中每个数按照它们映射后对应数字非递减顺序排序后返回。

注意:

  • 如果两个数字映射后对应的数字大小相同,则将它们按照输入中的 相对顺序 排序。
  • nums 中的元素只有在排序的时候需要按照映射后的值进行比较,返回的值应该是输入的元素本身。

示例 1:

输入:mapping = [8,9,4,0,2,1,3,5,7,6], nums = [991,338,38]
输出:[338,38,991]
解释:
将数字 991 按照如下规则映射:
1. mapping[9] = 6 ,所有数位 9 都会变成 6 。
2. mapping[1] = 9 ,所有数位 1 都会变成 8 。
所以,991 映射的值为 669 。
338 映射为 007 ,去掉前导 0 后得到 7 。
38 映射为 07 ,去掉前导 0 后得到 7 。
由于 338 和 38 映射后的值相同,所以它们的前后顺序保留原数组中的相对位置关系,338 在 38 的前面。
所以,排序后的数组为 [338,38,991] 。

示例 2:

输入:mapping = [0,1,2,3,4,5,6,7,8,9], nums = [789,456,123]
输出:[123,456,789]
解释:789 映射为 789 ,456 映射为 456 ,123 映射为 123 。所以排序后数组为 [123,456,789] 。

提示:

  • mapping.length == 10
  • 0 <= mapping[i] <= 9
  • mapping[i] 的值 互不相同 。
  • 1 <= nums.length <= 3 * 104
  • 0 <= nums[i] < 109

上菜

下面进行相关知识补充

C++ iota函数用法详解

和 sort() 函数一样,实现 stable_sort() 的函数模板也位于<algorithm>头文件中,因此在使用该函数前,程序也应包含如下语句:

#include <algorithm>

并且,table_sort() 函数的用法也有 2 种,其语法格式和 sort() 函数完全相同(仅函数名不同):

//对 [first, last) 区域内的元素做默认的升序排序
void stable_sort ( RandomAccessIterator first, RandomAccessIterator last );
//按照指定的 comp 排序规则,对 [first, last) 区域内的元素进行排序
void stable_sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp );

其中,first 和 last 都为随机访问迭代器,它们的组合 [first, last) 用来指定要排序的目标区域;另外在第 2 种格式中,comp 可以是 C++ STL 标准库提供的排序规则(比如 std::greater<T>),也可以是自定义的排序规则。

举个例子:

#include <iostream> // std::cout
#include <algorithm> // std::stable_sort
#include <vector> // std::vector
//以普通函数的方式实现自定义排序规则
bool mycomp(int i, int j) {
return (i < j);
}
//以函数对象的方式实现自定义排序规则
class mycomp2 {
public:
bool operator() (int i, int j) {
return (i < j);
}
};

int main() {
std::vector<int> myvector{ 32, 71, 12, 45, 26, 80, 53, 33 };
//调用第一种语法格式,对 32、71、12、45 进行排序
std::stable_sort(myvector.begin(), myvector.begin() + 4); //(12 32 45 71) 26 80 53 33
//调用第二种语法格式,利用STL标准库提供的其它比较规则(比如 greater<T>)进行排序
std::stable_sort(myvector.begin(), myvector.begin() + 4, std::greater<int>()); //(71 45 32 12) 26 80 53 33

//调用第二种语法格式,通过自定义比较规则进行排序,这里也可以换成 mycomp2()
std::stable_sort(myvector.begin(), myvector.end(), mycomp);//12 26 32 33 45 53 71 80
//输出 myvector 容器中的元素
for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it) {
std::cout << *it << ' ';
}
return 0;
}

程序执行结果为:

12 26 32 33 45 53 71 80

那么,stable_sort() 函数的效率怎么样呢?当可用空间足够的情况下,该函数的时间复杂度可达到O(N*log2(N));反之,时间复杂度为O(N*log2(N)2),其中 N 为指定区域 [first, last) 中 last 和 first 的距离。

C++ transform(STL transform)函数用法详解

transform

算法transform()提供以下两种能力:

1.第一形式有4个参数,把源区间的元素转换到目标区间。也就是说,复制和修改元素一气呵成;
2.第二形式有5个参数,将前两个源序列中的元素合并,并将结果写入目标区间。

注意:含有修改元素,仅仅只做复制元素,可以使用copy()。

第一种形式

transform(sourceBeg,sourceEnd,destBeg,op)

(1)针对源区间[sourceBeg,sourceEnd)中的每一个元素调用:op(elem) 并将结果写到以destBeg起始的目标区间内;

(2)返回目标区间内“最后一个被转换元素”的下一个位置,也就是第一个未被覆盖的元素位置;

(3)调用者必须确保目标区间有足够的空间,要不就得使用插入型迭代器;

(4)sourceBeg于destBeg可以相同,所以,和for_each()算法一样,你可以使用这个算法来变动某一序列内的元素;

(5)如果想以某值替换符合某一准则的元素,应使用replace()算法;

(6)复杂度:线性;


代码示例(第一种形式):
 

#include"fuzhu.h"
 
using namespace std;
 
int main()
{
	vector<int> coll1;
	list<int> coll2;
 
	INSERT_ELEMENTS(coll1,1,9);
 
	PRINT_ELEMENTS(coll1,"coll1: ");
 
	transform(coll1.begin(),coll1.end(),coll1.begin(),negate<int>());
 
	PRINT_ELEMENTS(coll1,"negated: ");
 
	transform(coll1.begin(),coll1.end(),back_inserter(coll2),bind2nd(multiplies<int>(),10));
 
	PRINT_ELEMENTS(coll2,"coll2: ");
 
	transform(coll2.rbegin(),coll2.rend(),ostream_iterator<int>(cout," "),negate<int>());
	cout<<endl;
 
	system("pause");
	return 0;
}

运行结果:

coll1:1 2 3 4 5 6 7 8 9
negated: -1 -2 -3 -4 -5 -6 -7 -8 -9
coll2: -10 -20 -30 -40 -50 -60 -70 -80 -90
90 80 70 60 50 40 30 20 10

第二种形式

transform(source1Beg,source1End,source2Beg,destBeg,op)

(1)针对第一源区间[source1Beg,source1End)以及“从source2Beg开始的第二源区间”的对应元素,调用:op(source1Elem,source2Elem) 并将结果写入以destBeg起始的目标区间内;

(2)返回区间内的“最后一个被转换元素”的下一位置,就是第一个未被覆盖的元素的位置;

(3)调用者必须保证第二源区间有足够空间(至少拥有和第一区间相同的空间大小);

(4)调用者必须确保目标区间有足够空间,要不就得使用插入型迭代器;

(5)source1Beg,source2Beg,destBeg可以相同。所以,可以让元素自己和自己结合,然后将结果覆盖至某个序列;

(6)复杂度:线性;
 

代码示例:

#include"fuzhu.h"
 
using namespace std;
 
int main()
{
	vector<int> coll1;
 
	list<int> coll2;
 
	INSERT_ELEMENTS(coll1,1,9);
 
	PRINT_ELEMENTS(coll1,"coll1: ");
 
	transform(coll1.begin(),coll1.end(),coll1.begin(),coll1.begin(),multiplies<int>());//1*1  2*2 
 
	PRINT_ELEMENTS(coll1,"squared: ");
 
	transform(coll1.begin(),coll1.end(),coll1.rbegin(),back_inserter(coll2),plus<int>());//1+81  4+64 
 
	PRINT_ELEMENTS(coll2,"coll2: ");
 
	cout<<"diff: ";
	transform(coll1.begin(),coll1.end(),coll2.begin(),ostream_iterator<int>(cout," "),minus<int>());//1-82  4-68
	cout<<endl;
 
	system("pause");
	return 0;
}

运行结果:

coll1:1 2 3 4 5 6 7 8 9
squared: 1 4 9 16 25 36 49 64 81
coll2: 82 68 58 52 50 52 58 68 82
diff: -81 -64 -49 -36 -25 -16 -9  -4 -1

c++11 匿名函数[] [this] [&] [=]

C++11的新特性,匿名函数(Lambda)

auto:当编译器能够在一个变量的声明时候就推断出它的类型,那么你就能够用auto关键字来作为他们的类型

例如这样

vector<int> vec;
vector<int>::iterator itr = vec.iterator();

变成这样

vector<int> vec;
auto itr = vec.iterator();

然后是 新的返回语法,后置了…

由这样

Person::PersonType Person::getPersonType ()
{
    return _person_type;
}

变成这样

auto Person::getPersonType () -> PersonType
{
    return _person_type;
}

还有 类型获取(Decltype)

int x = 3;
decltype(x) y = x; // same thing as auto y = x;

看上去都是一些简单的卖弄技巧的东西啊,不过他们组合起来就变成酱紫:

template <typename Builder>
auto
makeAndProcessObject (const Builder& builder) -> decltype( builder.makeObject() )
{
    auto val = builder.makeObject();
    // do stuff with val
    return val;
}

这样就为代码带来很大的方便了,尤其是lambda中。

以前看python的时候,对lambda的理解是:如果一个函数只在某一小块地方被调用,其它地方不用的话,用lambda表达式会增加可读性以及减少代码量吧。

lambda的形式是:

[captures] (params) -> ret {Statments;}

captures的选项有这些:

    [] 不截取任何变量
    [&] 截取外部作用域中所有变量,并作为引用在函数体中使用
    [=] 截取外部作用域中所有变量,并拷贝一份在函数体中使用
    [=, &foo]   截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用
    [bar]   截取bar变量并且拷贝一份在函数体重使用,同时不截取其他变量
    [this]            截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。

都是关于怎么使用外部变量的。
最简单的例子,就像这样:

[] () -> int { return 1; } 

分析一下最上面的例子吧。

// Declare graph of factor types and build the factor structure
         std::vector<dtf::prior_t> priors;
         float tree_ms = ts::timing_ms([&]() {undefined
            graph = InitFactorGraph(forest, priors, dags_train, sigma_unary, sigma_pw,
               levels_unary, levels_pw, feat_box_max, feat_radius);
         });
         std::cout << "Decision tree induction took " << (tree_ms/1000.0) << "s" << std::endl;

首先ts::timing_ms是一个模板函数返回float:

// Execute 'func', and return the duration of the function call, in ms.
template <typename Func> inline float timing_ms(Func func)
{
   return timing_median_ms(1, func);
}

timing_ms 调用了  timing_median_ms,再调用func 得到float返回值。 这个func实际上就是lambda表达式了。表达式用到 [&],说明它能够以引用形式使用其他的外部变量,因为里面的一些参数,如graph,是一个外部变量 。

 
这样兜兜转转,实际上对功能更好地划分了吧。把实际要做的事放在lambda里,时间计数放在timing_ms封装好的地方。
 

直接排序

记忆化可加可不加,不加不要用to_string,会被卡常

class Solution {
public:
    vector<int> sortJumbled(vector<int>& mapping, vector<int>& nums) {
        stable_sort(begin(nums), end(nums), [trs = [&](auto x){
            int ret = 0, e = 1;
            do{
                ret += mapping[x%10]*e;
                x /= 10;
                e *= 10;
            }while(x);
            return ret;
        }](auto x, auto y){ return trs(x) < trs(y); });
        return nums;
    }
};

#尊重作者的成果

作者:MuriyaTensei
链接:https://leetcode-cn.com/problems/sort-the-jumbled-numbers/solution/c-zi-ding-yi-pai-xu-stlyi-ju-quan-zhi-xi-4eiv/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

优化

class Solution {
public:
    vector<int> sortJumbled(vector<int>& mapping, vector<int>& nums) {
        int n = nums.size();
        vector<int> t(n), d(n);
        iota(d.begin(), d.end(), 0);
        transform(nums.begin(), nums.end(), t.begin(), [&](auto x){
            int ret = 0, e = 1;
            do{
                ret += mapping[x%10]*e;
                x /= 10;
                e *= 10;
            }while(x);
            return ret;
        });
        stable_sort(begin(d), end(d), [&](auto x, auto y){
            return t[x] < t[y];
        });
        for(int& x: d) x = nums[x];
        return d;
    }
};


作者:MuriyaTensei
链接:https://leetcode-cn.com/problems/sort-the-jumbled-numbers/solution/c-zi-ding-yi-pai-xu-stlyi-ju-quan-zhi-xi-4eiv/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这里用到了c++ 11 的新特性lamda匿名函数,我看了好久才吸收这部分精华

吸收知识后的代码

class Solution {
public:
    vector<int> sortJumbled(vector<int>& mapping, vector<int>& nums) {
        unordered_map<int, int> mp;
        for (auto num : nums){
            int ret = 0, mul = 1, temp = num;
            do{
                ret += mapping[temp % 10] * mul;
                temp /= 10;
                mul *= 10;
            }while(temp);
            mp[num] = ret;
        }
        stable_sort(nums.begin(), nums.end(), [&](auto a, auto b){
            return mp[a] < mp[b];
        });
        return nums;
    }
};

参考博客:

C++标准库---transform()_lanzh_-CSDN博客_c++ transform

c++11 匿名函数[] [this] [&] [=]_超超大爷的博客-CSDN博客_[&]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tp_0moyi0

新手入行,慢慢学习,慢慢积累

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值