LeetCode954二倍数对数组

问题:二倍数对数组

给定一个长度为偶数的整数数组 A,只有对 A 进行重组后可以满足 “对于每个 0 <= i < len(A) / 2,都有 A[2 * i + 1] = 2 * A[2 * i]” 时,返回 true;否则,返回 false

 

示例 1:

输入:[3,1,3,6]
输出:false

示例 2:

输入:[2,1,2,6]
输出:false

示例 3:

输入:[4,-2,2,-4]
输出:true
解释:我们可以用 [-2,-4] 和 [2,4] 这两组组成 [-2,-4,2,4] 或是 [2,4,-2,-4]

示例 4:

输入:[1,2,4,16,8,4]
输出:false

 

提示:

  1. 0 <= A.length <= 30000
  2. A.length 为偶数
  3. -100000 <= A[i] <= 100000

链接:https://leetcode-cn.com/contest/weekly-contest-114/problems/array-of-doubled-pairs/

分析:

题目要求看给出的数组能否通过重新排序,使的所有的偶数位置值是前一个的奇数值的二倍。

那么可以通过将数据分为负数和非负数两组,分组的原因是方便处理,对于非负数,直接递增排序正向处理,对于负数,排序后需要反向处理。关键看绝对值,处理逻辑是一样的。

以非负数为例:

排序后最小的值肯定是放在奇数位,那么在后面查找他的2倍是否存在,如果不存在可以直接结束返回false

如果存在,则需要将该数字移除,因为每个数字只能用到一次。

比如给出的例子中第一组排序后是 1 3 3 6 ,1 的2倍2不存在,false

第二组: 1 2 2 6,1的二倍2存在,移除2,得到1 2 6,处理下一个数据2,2的2倍4不存在,false

第三组分为-4 -2和2 4 两组,都对应存在,true

第四组,排序后 1 2 4 4 8 16

1-> 2 存在,移除2数组变为1 4 4 8 16

处理下一个4,4->8 存在,移除8得到 1 4 4 16

处理下一个4,8不存在,false

 

AC Code:

 

 1 class Solution {
 2 public:
 3 bool canReorderDoubled(vector<int>& A) {
 4         bool ret = true;
 5         //首先正负分开
 6         vector<int> Pvec;
 7         vector<int> Nvec;
 8         for (unsigned int i = 0; i < A.size(); i++)
 9         {
10             if (A[i] < 0)
11             {
12                 Nvec.emplace_back(-1*A[i]);
13                 //Pvec.emplace_back(-1*A[i]);
14             }
15             else
16             {
17                 Pvec.emplace_back(A[i]);
18             }
19         }
20         if (Pvec.size() % 2 != 0)
21         {
22             return false;
23         }
24         sort(Pvec.begin(), Pvec.end());
25         int limit = Pvec.size() / 2;
26         for (unsigned int i = 0; i < limit;i++)
27         {
28             if (find(Pvec.begin() + i + 1, Pvec.end(), 2 * Pvec[i]) != Pvec.end())
29             {
30                 Pvec.erase(find(Pvec.begin() + i + 1, Pvec.end(), 2 * Pvec[i]));
31             }
32             else
33             {
34                 return false;
35             }
36         }
37         sort(Nvec.begin(), Nvec.end());
38         limit = Nvec.size() / 2;
39         for (unsigned int i = 0; i < limit;i ++)
40         {
41             if (find(Nvec.begin() + i + 1, Nvec.end(), 2 * Nvec[i]) != Nvec.end())
42             {
43                 Nvec.erase(find(Nvec.begin() + i + 1, Nvec.end(), 2 * Nvec[i]));
44             }
45             else
46             {
47                 return false;
48             }
49         }
50         return true;
51     }
52 };

 

其他:

1.sort 默认升序,对于负数可以通过乘以-1转换为整数,之前看别人的一个解答学到的,可以省去编写自定义排序函数。

2.vector中查看某个元素是否存在,通过find(.begin(),.end(),value),如果没找到,返回end(),如果找到了,返回对应的迭代器值(iterator)

3,vector 中删除元素通过索引,如果本身就是iterator,可以直接删除erase(iterator),如果是个index 数字,可以通过erase(.begin()+index)

4,第一code:

 1 #pragma GCC optimize("O3", "unroll-loops")
 2 // God Help me !!
 3 #include <bits/stdc++.h>
 4 using namespace std;
 5 // #define watch(x) cout << (#x) << " is " << (x) << endl
 6 
 7 #define FILES freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout)
 8 #define FAST ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
 9 #define FIXED cout << fixed << setprecision(20)
10 #define RANDOM srand(time(nullptr))
11 // #define int long long
12 #define MOD 1000000007
13 #define sz(a) (ll)a.size()
14 #define pll pair<ll,ll>
15 #define rep(i,a,b) for(int i=(int)a;i<(int)b;i++)
16 #define sep(i,a,b) for(int i=(int)a;i>=(int)b;i--)
17 #define mll map<int,int>
18 #define vl vector<int>
19 #define pb push_back
20 #define lb lower_bound
21 #define ub upper_bound
22 #define all(a) a.begin(),a.end()
23 #define F first
24 #define S second
25 #define endl "\n"
26 #define MAXN6 3000005
27 #define MAXN3 3005
28 #define MAXN5 300005
29 
30 #define watch(...) ZZ(#__VA_ARGS__, __VA_ARGS__)
31 template <typename Arg1> void ZZ(const char* name, Arg1&& arg1){std::cerr << name << " = " << arg1 << endl;}
32 template <typename Arg1, typename... Args>void ZZ(const char* names, Arg1&& arg1, Args&&... args)
33 {
34     const char* comma = strchr(names + 1, ',');
35     std::cerr.write(names, comma - names) << " = " << arg1;
36     ZZ(comma, args...);
37 }
38  
39 
40 class Solution {
41 public:
42     bool canReorderDoubled(vector<int>& A) {
43         int positive[100001], negative[100001];
44         memset(positive, 0, sizeof(positive));
45         memset(negative, 0, sizeof(negative));
46         for (int x: A) {
47             if (x >= 0) positive[x]++;
48             else negative[-x]++;
49         }
50         if (positive[0] % 2 != 0) return false;
51         for (int i = 1; i <= 50000; i++) {
52             if (positive[i] != 0) {
53                 if (positive[i*2] < positive[i]) return false;
54                 positive[i*2] -= positive[i];
55             }
56         }
57         for (int i = 1; i <= 50000; i++) {
58             if (negative[i] != 0) {
59                 if (negative[i*2] < negative[i]) return false;
60                 negative[i*2] -= negative[i];
61             }
62         }
63         return true;
64     }
65 };

看到第一code 用来桶的那种方式 ,通过申请足够大的数组,下标就是值,删除通过--实现,而vector中删除需要移动大量数据,随意AC虽然过了,时间800+

完赛后QQ群里面有提到这个题 其实核心思路就是分组,排序, 查找元素是否存在以及删除。

那么可以通过map来做,map存储出现的数字以及个数 ,key是数字,value是数字个数,如果数字被用过了,value--,通过这种方式模拟删除。

这样也能AC,而且时间从800+降到96,虽然时间和很多因素有关, 比如服务器状态、测试数据等,但是下降一个数量级还是很能说明问题的。

map实现:

 1 class Solution {
 2 public:
 3     bool canReorderDoubled(vector<int>& A) {
 4         bool ret = true;
 5         map<int, int> Ndata;
 6         map<int, int> Pdata;
 7         int Nnum = 0;
 8         for (int i : A)
 9         {
10             if (i >= 0)
11             {
12                 if (Pdata.find(i) != Pdata.end())
13                 {
14                     Pdata[i]++;
15                 }
16                 else
17                 {
18                     Pdata[i] = 1;
19                 }
20             }
21             else
22             {
23                 Nnum++;
24                 if (Ndata.find(-1*i) != Ndata.end())
25                 {
26                     Ndata[-1 * i]++;
27                 }
28                 else
29                 {
30                     Ndata[-1 * i] = 1;
31                 }
32             }
33         }
34 
35         if (Nnum % 2!= 0)
36         {
37             return false;
38         }
39         //处理非负数
40         map<int, int>::iterator it = Pdata.begin();
41         int Limit = (A.size()-Nnum) / 2;
42         for (unsigned int i = 0; i < Limit; )
43         {
44             if ((*it).second == 0)
45             {
46                 it++;
47                 continue;
48             }
49             int tmpdata = (*it).first;
50             (*it).second--;
51             map<int, int>::iterator tmpit;
52             tmpit = Pdata.find(2 * (*it).first);
53             if (tmpit == Pdata.end() || (*tmpit).second==0)
54             {
55                 return false;
56             }
57             else
58             {
59                 (*tmpit).second--;
60             }
61             if ((*it).second == 0)
62             {
63                 it++;
64             }
65             i++;
66         }
67 
68         it = Ndata.begin();
69         Limit = Nnum / 2;
70         for (unsigned int i = 0; i < Limit;)
71         {
72             if ((*it).second == 0)
73             {
74                 it++;
75                 continue;
76             }
77             int tmpdata = (*it).first;
78             (*it).second--;
79             map<int, int>::iterator tmpit;
80             tmpit = Ndata.find(2 * (*it).first);
81             if (tmpit == Ndata.end() || (*tmpit).second == 0)
82             {
83                 return false;
84             }
85             else
86             {
87                 (*tmpit).second--;
88             }
89             if ((*it).second == 0)
90             {
91                 it++;
92             }
93             i++;
94         }
95 
96         return ret;
97     }
98 };

一段时间不用map,手生了。

a.1 map个数是值的个数,这一个题中直接用map的size得不到正负数个数,需要value累加或者通过额外的一个变量记录

a.2 如果因为个数为零跳过,那么index i不应增加,所以i的改变时机要放到后面合适的地方,而不是for内

a.3 如果数据涉及到底层的移动变换等,最好能够避免,是在避免不了也尽量集中处理 ,记得之前看到一本书提到C++的一些高级特性的时候有提到这个,一时想不起来的,书读百遍,有时间还是得在读一遍。

a.4使用的C++,感觉自己用的这套测试测试函数挺不错的,再也不用通过OJ debug了,而且能够回归测试,避免修改过后顾头不顾尾,记下来,有需要的可以拿去。

比如对于954的测试:

 1 void test954()
 2 {
 3     Solution954 S;
 4     vector<int> A;
 5     bool ans;
 6     bool tans;
 7 
 8 
 9     A = vector<int>{ 1,2,4,16,8,4 };
10     tans = false;
11     ans = S.canReorderDoubled(A);
12     cout << "ans:" << ans << "  tans:" << tans << "  ### " << (ans == tans) << endl;
13 
14     A = vector<int>{ 2,1,2,6 };
15     tans = false;
16     ans = S.canReorderDoubled(A);
17     cout << "ans:" << ans << "  tans:" << tans << "  ### " << (ans == tans) << endl;
18 
19 
20     A = vector<int>{ 2,1,2,1,1,1,2,2};
21     tans = true;
22     ans = S.canReorderDoubled(A);
23     cout << "ans:" << ans << "  tans:" << tans << "  ### " << (ans == tans) << endl;
24 
25 
26 
27     A = vector<int>{ 3,1,3,6 };
28     tans = false;
29     ans = S.canReorderDoubled(A);
30     cout << "ans:" << ans << "  tans:" << tans << "  ### " << (ans == tans) << endl;
31 
32 
33 
34     A = vector<int>{ 4,-2,2,-4 };
35     tans = true;
36     ans = S.canReorderDoubled(A);
37     cout << "ans:" << ans << "  tans:" << tans << "  ### " << (ans == tans) << endl;
38 
39     A = vector<int>{ 1,2,4,16,8,4 };
40     tans = false;
41     ans = S.canReorderDoubled(A);
42     cout << "ans:" << ans << "  tans:" << tans << "  ### " << (ans == tans) << endl;
43 }

如果有新的测试数据只需要加进去就行。如果用来测试新的类,只需要根据需要设定数据类型,返回值类型,然后在具体的测试用例里面设置就可以了。如果输出目标答案tans和时机答案ans一致,则测试通过。如果是一些符合数据,需要自己编写判断是否相等的函数。

转载于:https://www.cnblogs.com/youdias/p/10091245.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值