爱丽丝和鲍勃有不同大小的糖果棒:A[i] 是爱丽丝拥有的第 i 根糖果棒的大小,B[j] 是鲍勃拥有的第 j 根糖果棒的大小。
因为他们是朋友,所以他们想交换一根糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。)
返回一个整数数组 ans,其中 ans[0] 是爱丽丝必须交换的糖果棒的大小,ans[1] 是 Bob 必须交换的糖果棒的大小。
如果有多个答案,你可以返回其中任何一个。保证答案存在。
//来源:力扣(LeetCode)
//链接:https://leetcode-cn.com/problems/fair-candy-swap
//著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
记数列A的要交换元素为x,数列B的要交换元素为y
同时,数列A的和为SumA, 数列B的和为SumB
则: SumA-x+y = SumB+x-y
整理得:(SumB-SumA)/2 = y-x
(不能调换顺序,不能添加绝对值)
得出上式后,就可以遍历B数列,找出当y=B[i]时,期待的x。
如果A中存在x,则直接返回x和y。
代码如下:
#include "iostream"
#include "vector"
#include "unordered_set"
#include "numeric"
using namespace std;
class Solution {
public:
vector<int> fairCandySwap(vector<int>& A, vector<int>& B) {
int sumA = accumulate(A.begin(), A.end(), 0);
int sumB = accumulate(B.begin(), B.end(), 0);
int delta = (sumA - sumB) / 2;
unordered_set<int> rec(A.begin(), A.end()); //将A中所有元素浅拷贝入一个新的unsorted_map
vector<int> ans; //不需要静态
for (auto& y : B) {
int x = y + delta;
if (rec.count(x)) { //若rec中有x,则返回1
ans = vector<int>{x, y};
break;
}
}
return ans;
}
};
int main(int argc, char const *argv[])
{
vector<int>a = {1,2};
vector<int>b = {2,3};
Solution s;
vector<vector<int> > c;
//未结束
c.push_back(s.fairCandySwap(a,b));
for (int i = 0; i < c[0].size(); ++i)
{
cout<<c[0][i]<<" ";
}
cout<<endl;
return 0;
}
为了让纯新手(完全没有接触过STL的)更快的了解代码中的语句,下面将会结合我自己刚上手的时候的理解来简略地解释代码。高手不喜勿喷。
对于没有学过面向对象的同学,记住先去学面向对象的程序设计。
vector
是容器,可以理解为一个数组
。vector<int> a;
上面的语句意思是创建了一个int型的vector,对象名为a
。
vector<vector<int> > a; //注意两个>之间的空格,目的是防止编译器将这个和std的>>搞混
//如果没有空格,就会报错
上述语句的意思是创建了一个二维的int型vector,对象名为a。可以直接看作是二维数组。
vector
有一些简单的操作:a={}
变成a={5}
vector<int> a;
a.push_back(5);
vector<vector<int> > a;
vector<int> temp;
temp.push_back(2);
temp.push_back(3);
a.push_back(temp);
temp.push_back(3);
temp.push_back(4);
a.push_back(temp);
accumulate(a.begin(),a.end(),0);
unordered_set
unordered_set
可以直接看成一个无序数组
(内含哈希表的迭代器,新手可以完全不用了解这个,因为不了解也可以使用),在代码中,它的目的是查找。比如你想查一个无序数组中有没有值5,则可以使用
set.count(5)
来查找。其中set
是一个unsorted_set类型的无序数组,比如{1,6,8,5,3,0,9,3}
。这样set.count(5)的意思是:set
里有没有5?有的话返回值为1,反之为0。可以作为判断的条件直接使用。
如果没有见过这样的语法,你看到以下程序会很困惑
for (auto& y : B){/*语句*/}
这个语法的意思是在B这个数列(vector……)中,从头到尾进行遍历。其中y是B中的元素。
比如:B={1,2,3,4,5}
在第三次循环中,在循环主题中使用到y,y=3。
新手在看懂leetcode中的解答后,会尝试在自己的IDE中尝试打一遍Solution。但是打完后可能不知道如何测试,因为leetcode并没有给出主函数。再此特地补出主函数。
int main(int argc, char const *argv[])
{
vector<int>a = {1,2};
vector<int>b = {2,3};
Solution s;
vector<vector<int> > c;
c.push_back(s.fairCandySwap(a,b));
for (int i = 0; i < c[0].size(); ++i)
{
cout<<c[0][i]<<" ";
}
cout<<endl;
return 0;
}
主函数中包含了vector的初始化,如何对返回值为vector的函数的处理以及将二维vector的遍历。
s.fairCandySwap(a,b)
返回值是一个vector,所以用一个二维的vector将返回值push_back。这样我们就可以把返回值存放在一个有主函数生存期的变量中。
在上面的推导中,我们已经得出了以下算式:
(SumB-SumA)/2 = y-x
在普通方法中,是通过遍历y寻找可能的x实现的交换。但是其中包含了查找,时间复杂度会和查找的方式正相关。双指针法给出了另一种从查找方式。
将A和B均从小到大排序。从最小的脚标开始遍历(i=j=0)。
当y-x符合要求,直接输出。
当y-x小于要求,说明y小了(从小到大排序),则j++
。
当y-x大于要求,说明x小了(从小到大排序),则i++
。
代码如下:
#include "iostream"
#include "vector"
#include "numeric"
#include "algorithm"
using namespace std;
class Solution {
public:
vector<int> fairCandySwap(vector<int>& A, vector<int>& B) {
int sumA = accumulate(A.begin(),A.end(),0);
int sumB = accumulate(B.begin(),B.end(),0);
int target = (sumB - sumA)/2;
sort(A.begin(),A.end());
sort(B.begin(),B.end());
vector<int> ans;
int i = 0;
int j = 0;
while(i<A.size()&&j<B.size()){
int temp = B[j]-A[i];
if(temp == target){
ans.push_back(A[i]);
ans.push_back(B[i]);
break;
}
if(temp < target){
j++;
}
if(temp > target){
i++;
}
}
return ans;
}
};
int main(int argc, char const *argv[])
{
vector<int>a = {1,2};
vector<int>b = {2,3};
Solution s;
vector<vector<int> > c;
c.push_back(s.fairCandySwap(a,b));
for (int i = 0; i < c[0].size(); ++i)
{
cout<<c[0][i]<<" ";
}
cout<<endl;
return 0;
}