最大数
给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。
示例 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 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
- 不能更改原数组(假设数组是只读的)。
- 只能使用额外的 O(1) 的空间。
- 时间复杂度小于 O(n2) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次。
如果暴力求解,时间复杂度为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;
}
};