每日一题11.6
11.6写题记录
每日一题 LeetCode1356
题目
给你一个整数数组 arr 。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。
如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。
请你返回排序后的数组。
题解
首先肯定是最简单的想法,把1~10001每个二进制数有几个1打表,再根据1的个数将其重新排序,1数目相等的升序排列。
C++
排序改写了一下sort函数,排序方式改成了题目所要求的。
class Solution {
public:
int get(int a)
{
int n=0;
for(a;a>0;a/=2)
n+=a%2;
return n;
}
vector<int> sortByBits(vector<int>& arr)
{
vector<int>bit(10001,0);
for(auto x:arr)
bit[x]=get(x);
sort(arr.begin(),arr.end(),[&](int x,int y)
{
if(bit[x] < bit[y])
return true;
if(bit[x] > bit[y])
return false;
return x < y;
});
return arr;
}
};
Java
Java中没有vector,于是用ArryList和Collections来写。Collection中的sort与C++中的sort作用差不多,但操作对象一般是list。需要重写Comparator接口中的compare函数,该函数返回值大于小于等于0分别代表第一个数大于小于等于第二个数。
class Solution
{
public int[] sortByBits(int[] arr)
{
int[] bit = new int[10001];
List<Integer> list = new ArrayList<Integer>();
for (int x : arr)
{
list.add(x);
bit[x] = get(x);
}
Collections.sort(list, new Comparator<Integer>()
{
public int compare(Integer x, Integer y)
{
if (bit[x] != bit[y])
return bit[x] - bit[y];
else
return x - y;
}
});
for (int i = 0; i < arr.length; ++i)
arr[i] = list.get(i);
return arr;
}
public int get(int x)
{
int res = 0;
while (x != 0)
{
res += x % 2;
x /= 2;
}
return res;
}
}
PS:get函数里的for循环有些问题,不能像C++那样直接写for(a;a>0;a/=2)
必须要for(int x=a;x>0;x=x/2)
看了下书,上面只说“表达式一是初始化表达式”但我觉得以这个为理由有点扯,因为C++里表达式一也是initialization,也没在网上找到合理的解释,猜测应该跟JVM有关。等看完深入理解JVM来解答。
Go:
func get(x int) (c int)
{
for ; x > 0; x /= 2
c += x % 2
return
}
func sortByBits(a []int) []int
{
sort.Slice(a, func(i, j int) bool
{
x, y := a[i], a[j]
cx, cy := get(x), get(y)
return cx < cy || cx == cy && x < y
})
return a
}
时间复杂度\(O(nlogn)\)
空间复杂度\(O(n)\)
其他解法:
1.对于get(int a)函数的改进:
\( bit[i]=bit[i>>1]+(i\&1) \)
即 对于一个二进制数来说,它的1的个数等于右移一位的1的个数再加上其最后一位。
2.极(qi)简(ji)写(yin)法(qiao)
利用lamda表达式,bitset中的count函数,pair中默认比较大小的方式
\(\lambda\)
- lamda表达式 ——C++11标准
-
基本语法
[capture](parameters) mutable ->return-type{statement}
[capture]
:捕捉列表。捕捉列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数。捕捉列表能够捕捉上下文中的变量以供Lambda函数使用;(parameters)
:参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略;mutable
:mutable修饰符。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空);->return-type
:返回类型。用追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号->一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导;{statement}
:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
-
注意到,lamda表达式,与普通函数,最大的不同,在于拥有捕获列表。捕获列表有一下几种形式
- [var]表示值传递方式捕捉变量var;
- [=]表示值传递方式捕捉所有父作用域的变量(包括this);
- [&var]表示引用传递捕捉变量var;
- [&]表示引用传递方式捕捉所有父作用域的变量(包括this);
- [this]表示值传递方式捕捉当前的this指针。
-
注意的是,捕获列表可以组合,但是不能重复。
-
事实上,我认为lamda表达式就是一个用来增强代码可读性的函数,在泛型之上。
- bitset ——C++98标准
- 头文件和命名空间声明
#include<bitset> using namespace std::bitset
- 类声明时要给出长度值,如
bitset<32> bitvec
- 可用unsigned值和string对象来初始化bitset类(string对象可以是完整的也可以是一半,比如:
string str("1111111000000011001101"); bitset<32> bitvec5(str, 5, 4); // 4 bits starting at str[5], 1100 bitset<32> bitvec6(str, str.size() - 4); // use last 4 characters
- 函数
bitvec.any() //有1吗 bitvec.none() //有0吗 bitvec.count() //1的个数 bitvec.size() //位数 bitvec[pos] //pos处的二进制位 bitvec.test(pos) //pos处的二进制位是1吗 bitvec.set() //置1 bitvec.set(pos) //pos处置1 bitvec.reset() //置0 bitvec.reset(pos) // bitvec.flip() //取反 bitvec.flip(pos) // bitvec.to_ulong() //返回一个unsigned long值 os << bitvec //位集输出到os流
- 头文件和命名空间声明
- pair ——C++98标准
- 将2个数据合成一组数据的类,如map中的键值对。内部实现是一个结构体,两个成员变量是first和second。
- 头文件和类模板
#include <utility> template<class T1,class T2> struct pair
- 函数
pair<T1, T2> p1; //创建一个空的pair对象(使用默认构造),它的两个元素分别是T1和T2类型,采用值初始化。 pair<T1, T2> p1(v1, v2); //创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2。 make_pair(v1, v2); // 以v1和v2的值创建一个新的pair对象,其元素类型分别是v1和v2的类型。 p1 < p2; // 两个pair对象间的小于运算,其定义遵循字典次序:如 p1.first < p2.first 或者 !(p2.first < p1.first) && (p1.second < p2.second) 则返回true。 p1 == p2; // 如果两个对象的first和second依次相等,则这两个对象相等;该运算使用元素的==操作符。 p1.first; // 返回对象p1中名为first的公有数据成员 p1.second; // 返回对象p1中名为second的公有数据成员
- 创建pair对象时,必须提供两个类型名,两个类型名不必相同。定义时可以初始化。
- pair对象作返回值时可以用
std::tie
来接收,比如:
std::pair<std::string, int> getPreson() { return std::make_pair("Sven", 25); } int main(int argc, char **argv) { std::string name; int ages; std::tie(name, ages) = getPreson(); std::cout << "name: " << name << ", ages: " << ages << std::endl; return 0; }
- 去看了下源码,pair类是一个对象化的结构体,并且带参初始化参数类型是const T&。make_pair函数就是用来生成一个pair模板类对象的函数。pair类常用来作关联容器的成员函数的返回值,比如map和multimap。
题解:C++
class Solution {
public:
vector<int> sortByBits(vector<int>& arr) {
sort(arr.begin(), arr.end(), [&](int a, int b){
return make_pair(bitset<32>(a).count(), a) < make_pair(bitset<32>(b).count(), b);
});
return arr;
}
};
或
class Solution {
public:
vector<int> sortByBits(vector<int>& arr) {
sort(arr.begin(), arr.end(), [&](int a, int b){
return pair{__builtin_popcount(a), a} < pair{__builtin_popcount(b), b};
});
return arr;
}
};
来源:LeetCode:Monologue-S
Python3:
class Solution:
def sortByBits(self, arr: List[int]) -> List[int]:
return sorted(arr, key=lambda x:(bin(x).count("1"), x))
Python3性能竟然没有我想象当中那么差,震惊.jpg
总结
第一次正经写题解,写的好慢哦orz题目是很简单的题目,也没啥好总结的。昨天自己用快排写了一下,但性能属实辣鸡就不放了。
btw这是我第一次动手写位运算的题目,所以写题解的途中查了好些资料,菜菜。