一
打乱数组
打乱一个没有重复元素的数组。
示例:
// 以数字集合 1, 2 和 3 初始化数组。 int[] nums = {1,2,3}; Solution solution = new Solution(nums); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。 solution.shuffle(); // 重设数组到它的初始状态[1,2,3]。 solution.reset(); // 随机返回数组[1,2,3]打乱后的结果。 solution.shuffle();
class Solution
{
vector<int> source;
public:
Solution(vector<int> nums)
{
source = nums;
}
vector<int> reset()
{
return source;
}
vector<int> shuffle()
{
vector<int> shuffleArr = source;
for (int i = 0; i < shuffleArr.size(); i++)
{
int x = rand() % shuffleArr.size();
swap(shuffleArr[i], shuffleArr[x]);
}
return shuffleArr;
}
};
二
递增的三元子序列
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。
数学表达式如下:
如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。
示例 1:
输入: [1,2,3,4,5] 输出: true
示例 2:
输入: [5,4,3,2,1] 输出: false
class Solution
{
public:
bool increasingTriplet(vector<int> &nums)
{
int min1 = __INT_MAX__;
int min2 = __INT_MAX__;
for (int i = 0; i < nums.size(); i++)
{
if(nums[i] <= min1)
{
min1 = nums[i];
}
else if(nums[i] <= min2)
{
min2 = nums[i];
}
else
{
return true;
}
}
return false;
}
};
三
除自身以外数组的乘积
给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
示例:
输入: [1,2,3,4] 输出: [24,12,8,6]
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
方法一:
class Solution
{
public:
vector<int> productExceptSelf(vector<int> &nums)
{
int len = nums.size();
vector<int> pre(len);
vector<int> post(len);
vector<int> ans(len);
int cnt = nums[0];
for (int i = 1; i < nums.size(); i++)
{
pre[i] = cnt;
cnt *= nums[i];
}
cnt = nums[len - 1];
for (int i = len - 2; i >= 0; i--)
{
post[i] = cnt;
cnt *= nums[i];
}
for (int i = 0; i < len; i++)
{
if(i == 0)
{
ans[i] = post[i];
}
else if(i == len - 1)
{
ans[i] = pre[i];
}
else
{
ans[i] = pre[i] * post[i];
}
}
return ans;
}
};
方法二(常数空间):
走两遍,第一次完成前面的乘积。
第二次完成后面的乘积
class Solution
{
public:
vector<int> productExceptSelf(vector<int> &nums)
{
int left = 1;
int right = 1;
vector<int> ans(nums.size());
for (int i = 0; i < nums.size(); i++)
{
ans[i] = left;
left *= nums[i];
}
for (int i = nums.size() - 1; i >= 0; i--)
{
ans[i] *= right;
right *= nums[i];
}
return ans;
}
};
四
两个数组的交集II
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2,2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出: [4,9]
说明:
- 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
- 我们可以不考虑输出结果的顺序。
进阶:
- 如果给定的数组已经排好序呢?你将如何优化你的算法?
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
- 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
方法一:
class Solution
{
public:
vector<int> intersect(vector<int> &nums1, vector<int> &nums2)
{
map<int, int> n1;
map<int, int> n2;
vector<int> ans;
for(int i : nums1)
{
n1[i]++;
}
for(int i : nums2)
{
n2[i]++;
}
for(int i : nums1)
{
while(n1[i] > 0 && n2[i] > 0)
{
ans.push_back(i);
n1[i]--;
n2[i]--;
}
}
return ans;
}
};
方法二(假如排好序):
class Solution
{
public:
vector<int> intersect(vector<int> &nums1, vector<int> &nums2)
{
//排序好的情况
sort(nums1.begin(), nums1.end());
sort(nums2.begin(), nums2.end());
int i = 0, j = 0;
vector<int> ans;
while(i < nums1.size() && j < nums2.size())
{
if(nums1[i] == nums2[j])
{
ans.push_back(nums1[i]);
i++;
j++;
}
else if(nums1[i] > nums2[j])
{
j++;
}
else
{
i++;
}
}
return ans;
}
};
五
移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12] 输出: [1,3,12,0,0]
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
冒泡算法改进+优化
class Solution
{
public:
void moveZeroes(vector<int> &nums)
{
for (int i = 0; i < nums.size() - 1; i++)
{
bool flag = false;
for (int j = 1; j < nums.size() - i; j++)
{
if(nums[j - 1] == 0)
{
swap(nums[j], nums[j - 1]);
flag = true;
}
}
if(!flag)
return;
}
}
};
六
存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例 1:
输入: [1,2,3,1] 输出: true
示例 2:
输入: [1,2,3,4] 输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2] 输出: true
class Solution
{
public:
bool containsDuplicate(vector<int> &nums)
{
if(nums.size() <= 1)
return false;
sort(nums.begin(), nums.end());
for (int i = 1; i < nums.size(); i++)
{
if(nums[i] == nums[i - 1])
return true;
}
return false;
}
};
七
旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右旋转 1 步: [7,1,2,3,4,5,6] 向右旋转 2 步: [6,7,1,2,3,4,5] 向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2 输出: [3,99,-1,-100] 解释: 向右旋转 1 步: [99,-1,-100,3] 向右旋转 2 步: [3,99,-1,-100]
加入T代表倒置
[A(T)B(T)] = [BA]
注意k大于数组长度的情况
class Solution
{
public:
void rotate(vector<int> &nums, int k)
{
int len = nums.size();
k = k - (k / len) * len;
Inversion(nums, 0, len - k - 1);
Inversion(nums, len - k, len - 1);
Inversion(nums, 0, len - 1);
}
void Inversion(vector<int> &arr, int s, int e)
{
for (; s < e; s++, e--)
{
swap(arr[s], arr[e]);
}
}
};
八
乘积最大子序列
给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
示例 1:
输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
最大值可能来源于:1.最大值乘正数 2.最小值乘负数
所以用两个变量来维护到第i个位置时,靠近i的前面的乘积子序列。
class Solution
{
public:
int maxProduct(vector<int> &nums)
{
if(nums.size() == 0)
return 0;
int _max = nums[0];
int _min = nums[0];
int ans = _max;
for (int i = 1; i < nums.size(); i++)
{
int temp = _min;
_min = min(min(_min * nums[i], nums[i]), _max * nums[i]);
_max = max(max(_max * nums[i], nums[i]), temp * nums[i]);
ans = max(_max, ans);
}
return ans;
}
};