• 每日一题11.6


    11.6写题记录

    每日一题 LeetCode1356

    题目

    根据数字二进制下1的数目排序

    给你一个整数数组 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这是我第一次动手写位运算的题目,所以写题解的途中查了好些资料,菜菜。







  • 相关阅读:
    披萨
    扩展gcd
    LOJ6276 果树
    BZOJ 2038: [2009国家集训队]小Z的袜子(hose) | 莫队
    BZOJ 3052: [wc2013]糖果公园 | 树上莫队
    BZOJ 1878: [SDOI2009]HH的项链 | 莫队
    BZOJ 2453 维护队列 | 分块
    BZOJ 2821: 作诗(Poetize) | 分块
    BZOJ 2653 middle | 主席树
    BZOJ 1901: Zju2112 Dynamic Rankings | 带修改主席树
  • 原文地址:https://www.cnblogs.com/Cotmar/p/13940643.html
Copyright © 2020-2023  润新知