排序与检索

最大数

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

示例 1:

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

示例 2:

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

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

解题思路:制定排序规则,将两个数字颠倒连接比较就好了,比如10,2:比较102和210,显然210 大,那么排序就是2,10。

为了方便这种操作,我们简化,直接比较时候就转成string类型,这样比较好实现字符串的连接

class Solution {
public:
    static bool cmp(string s1, string s2)
    {
        return s1+s2 > s2+s1;
    }
    string largestNumber(vector<int>& nums) {
        vector<string> snum;
        for(int i =0; i < nums.size(); i++)
            snum.push_back(to_string(nums[i]));
        sort(snum.begin(), snum.end(), cmp);
        if(snum[0] == "0")//考虑排序后全为0的情况
            return "0";
        string ret = "";
        for(int i = 0; i < snum.size(); i++)
            ret += snum[i];
        return ret;
    }
};

摆动排序 II

给定一个无序的数组 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) {
        sort(nums.begin(), nums.end());
        vector<int> ret(nums);
        int n = ret.size()-1;
        for(int i = 1; i < nums.size(); i += 2)
            nums[i] = ret[n--];
        for(int i = 0; i < nums.size(); i += 2)
            nums[i] = ret[n--];
    }
};

寻找峰值

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

给定一个输入数组 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) 时间复杂度的。

第一种方法: 依次遍历数组, 时间复杂度O(N)

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        if(nums.size() == 1)
            return 0;
        if(nums[0] > nums[1])
            return 0;
        for(int i = 1; i < nums.size()-1; i++)
        {
            if(nums[i] > nums[i-1] && nums[i] > nums[i+1])
                return i;
        }
        if(nums[nums.size()-1] > nums[nums.size()-2])
            return nums.size()-1;
        return -1;
    }
};

思路二:使用二分法

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int n = nums.size();
        if(n == 1)
            return 0;
        int left = 0, right = n - 1;
        while(left <= right)
        {
            int mid = left + (right - left)/2;
            if(mid == 0)
            {
                if(nums[mid] > nums[mid+1])
                    return mid;
                else
                    left = mid + 1;
            }
            else if(mid == n-1)
            {
                if(nums[mid] > nums[mid - 1])
                    return mid;
                else
                    right = mid - 1;
            }
            else
            {
                if(nums[mid] > nums[mid-1] && nums[mid] > nums[mid+1])
                    return mid;
                else if(nums[mid] < nums[mid-1])
                    right = mid - 1;
                else if(nums[mid] < nums[mid+1] )
                    left = mid + 1;
            }
        }
        return -1;
    }
};

寻找重复数

给定一个包含 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. 数组中只有一个重复的数字,但它可能不止重复出现一次。

如果暴力求解,时间复杂度为O(n2),如果使用hash表,空间复杂度为O(n)。

解法一:使用类似寻找有环链表起点的方法,使用快慢指针,然后找到起点

参考:https://www.cnblogs.com/leaveMeAlone/p/9063790.html

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int slow = 0, fast = 0, t = 0;
        while(1)
        {
            slow = nums[slow];
            fast = nums[nums[fast]];
            if(fast == slow)
                break;
        }
        while(1)
        {
            fast = nums[fast];
            t = nums[t];
            if(fast == t)
                break;
        }
        return fast;
    }
};

解法二:这道题还有一种位操作 Bit Manipulation 的解法,也十分的巧妙。思路是遍历每一位,然后对于 32 位中的每一个位 bit,我们都遍历一遍从0到 n-1,我们将0到 n-1 中的每一个数都跟 bit 相 ‘与’,若大于0,则计数器 cnt1 自增1。同时0到 n-1 也可以当作 nums 数组的下标,从而让 nums 数组中的每个数字也跟 bit 相 ‘与’,若大于0,则计数器 cnt2 自增1。最后比较若 cnt2 大于 cnt1,则将 bit 加入结果 res 中。因为对于每一位,0到 n-1 中所有数字中该位上的1的个数应该是固定的,如果 nums 数组中所有数字中该位上1的个数多了,说明重复数字在该位上一定是1,这样我们把重复数字的所有为1的位都累加起来,就可以还原出了这个重复数字,参见代码如下:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int bit, cnt1, cnt2;
        int ret = 0;
        for(int i = 0; i < 32; i++)
        {
            bit = 1 << i;
            cnt1 = 0;
            cnt2 = 0;
            for(int k = 0; k < nums.size(); k++)
            {
                if(bit & k) cnt1++;
                if(nums[k] & bit) cnt2++;
            }
            if(cnt2 > cnt1)
                ret = ret + bit;
        }
        return ret;
    }
};

解法三:考虑用二分搜索法了,我们在区间 [0, n) 中搜索,首先求出中点 mid,然后遍历整个数组,统计所有小于等于 mid 的数的个数,如果个数小于等于 mid,则说明重复值在 [mid+1, n) 之间,反之,重复值应在 [0, mid)之间,然后依次类推,直到搜索完成,此时的 right 就是我们要求的重复值,参见代码如下:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int left = 0, right = nums.size();
        while(left < right)
        {
            int mid = left + (right - left)/2;
            int count = 0;
            for(int i = 0; i < nums.size();i++)
                if(nums[i] <= mid)
                    count++;
            if(count > mid)
                right = mid;
            else
                left = mid + 1;
        }
        return right;
    }
};

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

给定一个整数数组 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 个更小的元素.

(1)暴力求解,时间复杂度O(n2)超时

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        vector<int>count(n, 0);
        for(int i = nums.size()-2; i >= 0;i--)
        {
            int temp = 0;
            for(int j = i+1; j < nums.size(); j++)
            {
                if(nums[i] > nums[j])
                    temp++;
            }
            count[i] = temp;
        }
        return count;
    }
};

(2)将末尾的数组排序,使用二分查找,时间复杂度O(nlogn)

我们从暴力模拟法为起点进一步优化,我们看到每次我们都要从末尾遍历相同的元素,实际上我们可以建立一个保持排序的数组ret,这个数组代表:在nums[i]之后所有的数,并且已经排好序。

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        vector<int>count(n, 0);
        vector<int> ret;
        for(int i = n - 1; i >= 0;i--)
        {
            int left = 0, right = ret.size()-1;
            while(left <= right)
            {
                int mid = left + (right - left)/2;
                if(nums[i] <= ret[mid])
                    right = mid -1;
                else
                    left = mid + 1; 
            }
            count[i] = right + 1;
            ret.insert(ret.begin()+(right+1), nums[i]);
        }
        return count;
    }
};

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值