leetcode - cn | 排序与检索

最大数

给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。

示例 1:

输入: [10,2]
输出: 210

示例 2:

输入: [3,30,34,5,9]
输出: 9534330

说明: 输出结果可能非常大,所以你需要返回一个字符串而不是整数。

bool comp(string a,string b){
    //从大到小
    string ab=a+b;
    string ba=b+a;
    return ab>ba;  //这里一定是要大于或小于的时候返回true,不能是大于等于或小于等于
}

class Solution {
public:
    string largestNumber(vector<int>& nums) {
        if(nums.size()==0) return "0";
        vector<string> nums2;
        for(int i=0;i<nums.size();i++){
            nums2.push_back(to_string(nums[i]));
        }
        sort(nums2.begin(),nums2.end(),comp);
        string res="";  
        for(int i=0;i<nums2.size();i++){
            res=res+nums2[i];
        }
        if(res[0]=='0') return "0";
        return res;
    }
};

重新定义比较函数,将两个字符串进行比较,假设a与b进行比较,如果ab大于ba,那么a大于b。

需要注意的是

  • 在重写比较函数的时候,一定要是大于或小于的时候返回true,不能是大于等于或小于等于,会导致无穷无尽的比较,最终报错,terminate called after throwing an instance of 'std::length_error'
  • 两个字符串也可以直接用大于号小于号进行比较
  • 注意 正数、非负数等字眼,充分考虑边界值

摆动排序

给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

示例 1:

输入: nums = [1, 5, 1, 1, 6, 4]
输出: 一个可能的答案是 [1, 4, 1, 5, 1, 6]

示例 2:

输入: nums = [1, 3, 2, 2, 3, 1]
输出: 一个可能的答案是 [2, 3, 1, 3, 1, 2]

说明:
你可以假设所有输入都会得到有效的结果。

进阶:
你能用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现吗?

class Solution {
public:
    void wiggleSort(vector<int>& nums) {
        //下标为奇数的数要大于下标为偶数的数
        vector<int> temp(nums.size());
        int even;
        if(nums.size()%2==0) even=nums.size()/2;
        else even=nums.size()/2+1;  //偶数
        cout<<"even:"<<even<<endl;
        int odd=nums.size()-even;  //奇数
        cout<<"odd:"<<odd<<endl;
        sort(nums.begin(),nums.end());
        for(int i=0;i<even;i++){
            temp[i*2]=nums[even-1-i];
        }
        for(int i=0;i<odd;i++){
            temp[2*i+1]=nums[nums.size()-1-i];
        }
        nums=temp;
    }
};

从题目给出的样例可以看出是奇数位的值比偶数位的值大,那么可将数组进行排序,前一部分为偶数位的值,后一部分为奇数位的值,由于要求的是大于而不是大于小于,所以都从最后开始取,从前面开始取可能会导致奇偶交界的地方是相等的。


寻找峰值

峰值元素是指其值大于左右相邻值的元素。

给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。

数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞

示例 1:

输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,你的函数应该返回其索引 2。

示例 2:

输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5 
解释: 你的函数可以返回索引 1,其峰值元素为 2;
     或者返回索引 5, 其峰值元素为 6。

说明:

你的解法应该是 O(logN) 时间复杂度的。

class Solution {
public:
    bool finded=false;
    int peak=-1;
    
    
    int findPeakElement(vector<int>& nums) {
        //使用分治的方法,查找一个数
        if(nums.size()==0) return -1;
        else if(nums.size()==1) return 0;
        else{
            findPeak(0,nums.size()-1,nums);
            return peak;
        }
    }
    
    void findPeak(int begin,int end,vector<int>& nums){
        if(finded==true) return;
        if(begin>end||begin<0||end>=nums.size()) return;
        int mid=(begin+end)/2;
        if(mid==0){
            if(nums[mid]>nums[mid+1]){
                peak=mid;
                finded=true;
                return;
            }
        }else if(mid==nums.size()-1){
            if(nums[mid]>nums[mid-1]){
                peak=mid;
                finded=true;
                return;
            }
        }else{
            if(nums[mid]>nums[mid+1]&&nums[mid]>nums[mid-1]){
                peak=mid;
                finded=true;
                return;
            }
        }
        findPeak(begin,mid-1,nums);
        findPeak(mid+1,end,nums);
    }
};

按照二分查找的思路完成


寻找重复数

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

输入: [1,3,4,2,2]
输出: 2

示例 2:

输入: [3,1,3,4,2]
输出: 3

说明:

  1. 不能更改原数组(假设数组是只读的)。
  2. 只能使用额外的 O(1) 的空间。
  3. 时间复杂度小于 O(n2) 。
  4. 数组中只有一个重复的数字,但它可能不止重复出现一次。
class Solution {
private:
    bool finded=false;
    int res=0;
public:
    int findDuplicate(vector<int>& nums) {
        //思路错了,所有数字在1-n之间,但是并不一定是1-n之间的所有数字都出现
        //二分查找,快慢指针找入口
        findRange(1,nums.size()-1,nums);
        return res;
    }
    
    void findRange(int begin,int end,vector<int> nums){
        if(finded==true){
            return;
        }
        if(begin>end) return;
        int mid=(begin+end)/2;
        bool left=count(begin,mid,nums);
        bool right=count(mid+1,end,nums);
        if(left==false){
            if(begin==mid){
                res=begin;
                finded=true;
            }
            else findRange(begin,mid,nums);
        }
        else if(right==false){
            if(mid+1==end){
                res=end;
                finded=true;
            }
            else findRange(mid+1,end,nums);
        }
    }
    
    bool count(int begin,int end,vector<int> nums){
        //考虑值为begin和end之间是否有重复的数
        int count=0;
        for(int i=0;i<nums.size();i++){
            if(nums[i]>=begin&&nums[i]<=end){
                ++count;
            }
        }
        //这里需要注意的是,如果一个范围内有重复的数(大于区间距离),那么另一个区间的数就
        //会是少的,所以如果按照下面的写法的话是会所有都返回false的
        // if(count==end-begin+1) return true;
        // else return false;
        if(count>end-begin+1) return false;
        else return true;
    }
};

计算右侧小于当前元素的个数

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。

示例:

输入: [5,2,6,1]
输出: [2,1,1,0] 
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.

##暴力遍历无疑是会超时的

###然而,使用归并排序依旧超时了

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        //我就知道暴力法会超时
        //归并排序,和求解逆序对数比较像
        //归并排序也超时
        vector<int> res(nums.size(),0);
        count(0,nums.size()-1,nums,res);
        return res;
    }
    
    void count(int begin,int end,vector<int>& nums,vector<int>& res){
        if(begin>=end) return;
        int mid=(begin+end)/2;
        count(begin,mid,nums,res);
        count(mid+1,end,nums,res);
        for(int i=begin;i<=mid;i++){
            for(int j=mid+1;j<=end;j++){
                if(nums[i]>nums[j]) ++res[i];
            }
        }
    }
};

计算右侧小于当前元素的个数

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。

示例:

输入: [5,2,6,1]
输出: [2,1,1,0] 
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.

我的天,这道题总算是通过了,参考网上的方法,使用树形数组完成,相当快

真的是太巧妙了吧

从后向前遍历数组,用arr存放各个数出现的次数(arr中的数为从小到大的顺序),getsum()得到比当前值小的数的个数,并且使用update逐渐更新数组

class Solution {
public:
	int n;
    int *arr;
    int lowbit(int t){
        return t&(-t);
    }
    
    int getsum(int pos){
        int ans=0;
        while(pos){
            ans+=arr[pos];
            pos-=lowbit(pos);
            if(pos==0) break;
        }
        return ans;
    }
    
    void update(int pos,int val){
        while(pos<n){  //注意这里要是小于号,否则会堆溢出
            arr[pos]+=val;
            pos+=lowbit(pos);
            if(pos==0) break;
        }
    }
    
    vector<int> countSmaller(vector<int>& nums) {
        n=nums.size();
        vector<int> res(n);
        if(n==0) return res;
        int minn=INT_MAX;
        int maxx=INT_MIN;
        for(int i=0;i<nums.size();i++){
            maxx=max(maxx,nums[i]);
            minn=min(minn,nums[i]);
        }
        n=maxx-minn+2;
        arr=new int[n];
        memset(arr,0,sizeof(int)*n);
        for(int i=nums.size()-1;i>=0;i--){
            res[i]=getsum(nums[i]-minn);
            update(nums[i]-minn+1,1);
        }
        return res;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值