训练题:逆序对
题目:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
方法一:归并排序
- 时间复杂度:O(nlogn)
- 空间复杂度:O(n)
归并排序刚好是将左半有序部分和右半有序部分进行合并。在这个时候我们可以统计逆序对。
int reversePairs(vector<int>& nums) {
if(nums.size() < 2) return 0;
const auto size = nums.size();
vector<int> tmpNums(size);
return mergeSort(nums, tmpNums, 0, size - 1);
}
int mergeSort(vector<int>& nums, vector<int>& tmpNums, int start, int end){
if(end <= start) return 0;
int mid = start + ((end - start) >> 1);
int left = start;
int right = mid + 1;
int k = left;
int count = mergeSort(nums, tmpNums, start, mid) + mergeSort(nums, tmpNums, mid + 1, end);
for(; left <= mid && right <= end;)
{
if(nums[left] <= nums[right]) tmpNums[k++] = nums[left++];
else{ // left > right
tmpNums[k++] = nums[right++];
// 此时比num[right]大的数是num[left] ... num[mid]这几个数。
count += (mid - left + 1);
}
}
for(; left <= mid; ++left, ++k) tmpNums[k] = nums[left];
for(; right <= end; ++right, ++k) tmpNums[k] = nums[right];
copy( tmpNums.begin() + start, tmpNums.begin() + end + 1, nums.begin() + start );
return count;
}
方法二:树状数组(待完成)
训练题:机器人运动范围
题目:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/
方法一:深度优先遍历(递归)
- 时间复杂度:O(mn)
- 空间复杂度:O(mn)
int count;
int tmp;
int movingCount(int m, int n, int k) {
if(m < 1 || n < 1 || m > 100 || n > 100 || k < 0 || k > 20) return -1;
// dataMap用于记录遍历过的地方。
vector<vector<int>> dataMap(m, vector<int>(n, 0));
traverse(dataMap, m, n, k, 0, 0);
return count;
}
void traverse(int dataMap[][100], const int& m, const int& n, const int& k, int x, int y){
if( x == m || y == n || dataMap[x][y]) return;
dataMap[x][y] = 1;
tmp = x % 10 + x / 10 + y % 10 + y / 10;
if(tmp > k) return;
++count;
// 优化:从上图我们看出规律:从左上往右下遍历,必然能全部遍历到。
// 往左(x-1)和往上(y-1)的遍历可以省去。
traverse(dataMap, m, n, k, x + 1, y);
traverse(dataMap, m, n, k, x, y + 1);
// traverse(dataMap, m, n, k, x - 1, y);
// traverse(dataMap, m, n, k, x, y - 1);
}
方法二:广度优先遍历(队列)
int movingCount(int m, int n, int k) {
if(m < 1 || n < 1 || m > 100 || n > 100 || k < 0 || k > 20) return -1;
int count = 0; // 可到达的格子数
vector<vector<int>> dataMap(m, vector<int>(n, 0)); // 访问记录,避免重复
queue<pair<char, char>> q; // 广度优先
q.push({0, 0});
for(; !q.empty(); q.pop()){
auto pos = q.front();
// 无效坐标:坐标超范围、已经访问过、坐标值数位之和大于k的坐标。
if(pos.first == m || pos.second == n || dataMap[pos.first][pos.second]) continue;
if(pos.first % 10 + pos.first / 10 + pos.second % 10 + pos.second / 10 > k) continue;
++count;
dataMap[pos.first][pos.second] = 1;
q.push({pos.first + 1, pos.second});
q.push({pos.first, pos.second + 1});
}
return count;
}
方法三:递推法
int movingCount(int m, int n, int k) {
if(m < 1 || n < 1 || m > 100 || n > 100 || k < 0 || k > 20) return -1;
int count = 0;
// canVisited[i][j] = canVisited[i-1][j] | canVisited[i][j-1]
// 即只能从左边或者上边的元素过来。
vector<vector<char>> canVisited(m, vector<char>(n, 0));
canVisited[0][0] = 1;
for(char row = 0; row < m; ++row){
for(char col = 0; col < n; ++col){
if(row%10 + row/10 + col%10 + col/10 > k) continue;
if(row - 1 >= 0) canVisited[row][col] |= canVisited[row-1][col];
if(col - 1 >= 0) canVisited[row][col] |= canVisited[row][col-1];
if(canVisited[row][col]) ++count;
}
}
return count;
}
训练题:数组组成最小数
string minNumber(vector<int>& nums) {
const auto size = nums.size();
int len = 0; // nums的元素个数
int maxLen; // 最长元素长度
vector<string> strs; // 将数字都转化成string
strs.reserve(size);
// 将所有数字转化成string
for(int i = 0, tmpLen; i < size; ++i) {
strs.push_back(to_string(nums[i]));
tmpLen = strs.back().size();
maxLen = max(maxLen, tmpLen); // 记录最长元素
len += tmpLen; // 统计拼接成的string的总长度
}
// 对字符串数组进行排序,tmpStr1、tmpStr2是为了避免排序比较中的str1+str2<str2+str1的低性能代码。
string tmpStr1, tmpStr2;
tmpStr1.reserve(maxLen << 1);
tmpStr2.reserve(maxLen << 1);
sort(strs.begin(), strs.end(), [&](string &a, string &b) -> bool {
// "3"和"34":"334" < "343"
// "12"和9: "129" < "912"
tmpStr1.resize(0);
tmpStr2.resize(0);
tmpStr1.append(a).append(b);
tmpStr2.append(b).append(a);
return tmpStr1 < tmpStr2;
});
// 将已排好序的vector<string>拼接成string
string ret;
ret.reserve(len);
for(const auto& str: strs) {
ret.append(str);
}
return ret;
}
训练题:构建乘积数组
vector<int> constructArr(vector<int>& a) {
const auto size = a.size();
vector<int> ret(size,1);
int left = 1, right = 1;
for(int i = 1; i < size; ++i){
ret[i] *= (left *= a[i - 1]);
}
for(int i = size - 2; i >=0; --i){
ret[i] *= (right *= a[i + 1]);
}
return ret;
}
训练题:找数字
场景一:数组中数字出现的次数
vector<int> singleNumbers(vector<int>& nums) {
int a = 0, b = 0; // 待找出的两个数为a、b
int XOR = 0; // XOR = a ^ b
int div = 1; // div为XOR二进制数中最右边第一个1。
// XOR的最终结果为a 异或 b的结果,相同的两个数异或=0
for(const auto& num : nums) XOR ^= num;
// div为XOR二进制数中最右边第一个1。
while(!(XOR & div)) div <<= 1;
// div = (~XOR + 1) & XOR;
// div = XOR & (-XOR); // 负数为补码,补码 = 反码 + 1。
// div表示,a、b在这个位上是不同的。我们以此来将数组分成两组分别进行异或即可找出这两个数。
for(const auto & num: nums) {
if(num & div) a ^= num;
else b ^= num;
}
return {a, b};
}
场景二
int singleNumber(vector<int>& nums) {
// 保存所有数字在32位的每个位上1出现的次数
unsigned short bits[32] = {0};
char offset = 0;
for(const auto& num : nums){
for(char offset = 0; offset < 32; ++offset) {
if(num & (1 << offset)) ++bits[offset];
}
}
// 设任意位上的次数=n,则若n%3==0,则表示目标数字在该位置上是0,反之则为1。
int fuck = 0;
for(char idx = 0; idx < 32; ++idx){
fuck |= (bits[idx] % 3) << idx;
}
return fuck;
}
训练题:0~n-1中缺失的数
int missingNumber(vector<int>& nums) {
int left = 0;
// 二分查找法
for(int right = nums.size() - 1, mid; left <= right;){
mid = left + ((right - left) >> 1);
if(nums[mid] == mid) left = mid + 1;
else right = mid - 1;
}
return left;
}
训练题:和为s的数
方法一:哈希表
vector<int> twoSum(vector<int>& nums, int target) {
set<int> container;
for(const auto& num : nums){
if(container.count(num)){
return {target - num, num};
}
else container.insert(target - num);
}
return {};
}
方法二:双指针
vector<int> twoSum(vector<int>& nums, int target) {
for(int left = 0, right = nums.size() - 1; left < right;){
if(target - nums[left] == nums[right]) return {nums[left], nums[right]};
else if(target - nums[left] > nums[right]) ++left;
else --right;
}
return {};
}
训练题:合并区间
按区间的左元素升序排序,得到的区间序列,其中连续的区间肯定是在一块的。
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.size() < 2) return intervals;
sort(intervals.begin(), intervals.end());
vector<vector<int>> ret{intervals[0]};
vector<int> *tmp = nullptr;
for(const auto& vec : intervals)
{
tmp = &ret.back();
if((*tmp)[1] >= vec[0]) (*tmp)[1] = max((*tmp)[1], vec[1]);
else ret.push_back(vec);
}
return ret;
}
训练题:搜索插入位置
int searchInsert(vector<int>& nums, int target) {
int left = 0;
for(int right = nums.size() - 1, mid; left < right;)
{
mid = ((left + right) >> 1);
if(nums[mid] == target) return mid;
else if(nums[mid] > target) right = mid;
else left = mid + 1;
}
if(nums[left] < target) return left + 1;
return left;
}
训练题:中心下标
// leftSum: 左边和
// idx: 当前遍历索引,左边就是左边和
// total: 数组总和
// leftSum * 2 + nums[idx] == total
int pivotIndex(vector<int>& nums) {
int sum = 0, leftSum = 0;
for(const auto& num : nums) sum += num;
unsigned short size = nums.size();
for(unsigned short idx = 0; idx < size; ++idx)
{
if((leftSum * 2) + nums[idx] == sum) return idx;
leftSum += nums[idx];
}
return -1;
}
训练题:奇数在偶数的前面
方法一:双指针
vector<int> exchange(vector<int>& nums) {
if( nums.size() < 2 ) { return nums; }
// 快速排序的partition分区思想。
for( unsigned short left = -1, right = nums.size();; )
{
while( ++left < right && (nums[left] & 0x1) );
while( --right > left && !(nums[right] & 0x1) );
if( left < right ) { std::swap( nums[left], nums[right] ); }
else { break; }
}
return nums;
}
方法二:快慢指针
- slow指向待插入奇数的位置。
- fast指向找到的奇数 ```cpp
vector
const auto size = nums.size();
for( unsigned short slow = 0, fast = 0;; )
{
while( fast < size && !( nums[fast] & 0x1 ) ) { ++fast; }
if( fast < size ) { std::swap( nums[fast++], nums[slow++] ); }
else { break; }
}
return nums;
}
<a name="FCNIY"></a>
# 训练题:旋转数组的最小数字
题目:[http://t.cn/A6V4bma7](http://t.cn/A6V4bma7)
```cpp
int minArray(vector<int>& numbers) {
if(numbers.empty()) return INT_MIN;
int left = 0, right = numbers.size() - 1, mid;
for(;left < right;){
mid = left + ((right - left) >> 1);
if(numbers[mid] < numbers[right]) right = mid;
else if(numbers[mid] == numbers[right]) right -= 1;
else left = mid + 1;
}
return numbers[left];
}
训练题:重复的数字
方法一
遍历数组的过程中,让nums[idx]的值在nums[nums[idx]]位置上,即元素值和索引值相同。
时间复杂度:O(n)
空间复杂度:O(1)
int findRepeatNumber(vector<int>& nums) {
const auto size = nums.size();
for( int idx = 0; idx < size; ++idx )
{
if( idx != nums[idx] )
{
if( nums[idx] == nums[nums[idx]] ) { return nums[idx]; }
else { std::swap( nums[nums[idx]], nums[idx] ); }
}
}
return -1;
}
方法二:set集合
时间复杂度:O(n)
空间复杂度:O(n)
int findRepeatNumber(vector<int>& nums) {
set<int> shit;
for( auto &num : nums )
{
if( shit.count( num ) ) { return num; }
else { shit.insert( num ); }
}
return -1;
}
训练题:超过一半的数字
方法一:排序
先排序,然后返回中间那个数,这里我们假设一定存在这样一个数。
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
方法二:哈希
用一个哈希统计每个数出现的次数。
- 时间复杂度:O(n)
- 空间复杂度:O(n)
int MoreThanHalfNum_Solution( vector<int> numbers )
{
map<int, unsigned short> shit;
const auto size = numbers.size() >> 1;
for( const auto &num : numbers ) if( ++shit[num] > size ) { return num; }
return -1;
}
方法三
- 时间复杂度:O(n)
- 空间复杂度:O(n)
int MoreThanHalfNum_Solution( vector<int> numbers )
{
unsigned short size = numbers.size();
// 遍历数组,当target与当前数不同,count-1,count=0,就将target赋值为当前数
// 当target与当前数相同则count+1,遍历到最后一个数,target必定为目标数
unsigned short count = 1; //
int target = numbers[0]; // 当前数目
for( unsigned short idx = 1; idx < size ; ++idx )
{
target == numbers[idx] ? ++count : --count;
if( !count )
{
target = numbers[idx];
count = 1;
}
}
return target;
}
训练题:两数之和
typedef int Value;
typedef int Index;
/**
*
* @param numbers int整型vector
* @param target int整型
* @return int整型vector
*/
vector<int> twoSum( vector<int> &numbers, int target )
{
if( numbers.size() < 2 ) { return {}; }
map<Value, Index> shit;
int index2 = -1;
for( int index1 = 0; index1 < numbers.size(); index1++ )
{
if( shit.find( target - numbers[index1] ) != shit.end() )
{
index2 = shit[target - numbers[index1]];
return {index2 + 1, index1 + 1};
}
shit.insert( make_pair( numbers[index1], index1 ) );
}
return{};
}
训练题:三数之和
题目:https://leetcode-cn.com/problems/3sum/
方法一:降维成两数之和
vector<vector<int>> threeSum(vector<int>& nums) {
if(nums.size() < 3) return {};
sort(nums.begin(), nums.end());
if(nums[0] > 0) return {};
const auto size = static_cast<short>(nums.size());
vector<vector<int>> ret;
for(short first = 0; first < size - 2; ++first){
// 先取第一个数,问题就变成在数组中找两数之和的问题,用双指针搞定。
// 当前取的first和上一轮的相同,那这个first就重复了。
if(first > 0 && nums[first] == nums[first - 1]) continue;
int target = 0 - nums[first]; // 目标和
int left = first + 1, right = size - 1; // 左右指针
while(left < right){
// 这里其实还
if(nums[left] + nums[right] < target) ++left; // 右移左指针
else if(nums[left] + nums[right] > target) --right; // 左移右指针
else{ // 找到目标
ret.push_back({nums[first], nums[left++], nums[right--]});
// 排序之后,相等的数都相邻,找到目标之后,需要做去重操作。
for(; left < right && nums[left] == nums[left-1]; ++left); // 当前的left指针和目标和的left指针相同,跳过
for(; left < right &&nums[right] == nums[right+1]; --right);
}
}
}
return ret;
}
训练题:最接近三数之和
题目:https://leetcode-cn.com/problems/3sum-closest/
同上理。
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
const auto size = nums.size();
int ret = nums[0] + nums[1] + nums[2];
if(ret == target) return ret;
for(int first = 0; first < size - 2; ++first){
if(first > 0 && nums[first] == nums[first - 1]) continue;
for(int left = first+1, right = size - 1, tmpSum; left < right; ) {
tmpSum = nums[first] + nums[left] + nums[right];
if(tmpSum == target) return target;
else {
if(abs(ret - target) > abs(tmpSum - target)) ret = tmpSum;
if(tmpSum > target) for(--right; left < right && nums[right] == nums[right+1]; --right);
else for(++left; left < right && nums[left] == nums[left-1]; ++left);
}
}
}
return ret;
}
训练题:最大累加和
动态规划。
连续子数组的最大累加和肯定是以某个元素结尾。
我们设dp[i]是以i位置元素结尾的最大累加和。按dp思想,我们就要从dp[i-1]来求dp[i]。
int maxSubArray(vector<int>& nums) {
int maxSum = nums[0]; // 当前的最大累加和。
for(int i = 1; i < nums.size(); i++){
// nums[i]存储以nums[i]元素结尾的连续最大累加和。
// 如果nums[i - 1]<=0,则nums[i]的最大累加和就等于nums[i]元素,
// 如果nums[i - 1]>0,则nums[i]的最大累加和=nums[i-1] + nums[i]
nums[i] += max(nums[i - 1], 0);
// 比较当前记录的最大累加和,和nums[i]位置处的最大累加和。
maxSum = max(maxSum, nums[i]);
}
return maxSum;
}
训练题:合并有序数组
- 时间复杂度:O(n)
- 空间复杂度:O(1)
void merge(int A[], int m, int B[], int n) {
// 逆序遍历A、B数组。将最大者保存到tail中
for( int tail = m + n - 1, tailA = m - 1, tailB = n - 1; tail >= 0; --tail )
{
if( tailA >= 0 && ( tailB < 0 || B[tailB] < A[tailA] ) )
{
A[tail] = A[tailA--];
}
else
{
A[tail] = B[tailB--];
}
}
}
训练题:有序数组查找数字
二分查找算法
有序数组查找数字,就不得不想到二分查找法。
二分查找算法的使用心得,将left、right、mid当成二分搜索的动力,nums[mid]与target比较时,mid的赋值决定了搜索的方向(如果找到相同的,是往右边继续搜,还是往左边)。
// nums是升序的,target是需要找的数字
// 返回目标数字在数组nums中的索引,如果没有找到返回-1
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
for(int mid; left <= right;) {
// 可选逻辑:先行判断,减少计算量
if(nums[left] == target) return left;
else if(nums[right] == target) return right;
// 写成mid = (left + right) >> 1有整型溢出风险。
// >>位移运算符的优先级很低,请加括号
mid = left + ((right - left) >> 1);
if(nums[mid] == target) return mid;
else if(nums[mid] > target) right = mid - 1;
else left = mid + 1;
}
// 到这一步,left > right,没有找到。
return -1;
}
算法变形一
// nums是升序的,找到target从索引0开始的第一次出现时的索引,没有返回-1。
// 找区域的左边界。
int search(vector<int>& nums, int target) {
int start = -1;
for(int left = 0, right = nums.size() - 1, mid; left <= right;){
mid = left + ((right - left) >> 1);
if(nums[mid] == target) start = mid;
if(nums[mid] >= target) right = mid - 1; // 找到相同的,继续往左边搜。
else left = mid + 1;
}
return start;
}
算法变形二
// nums是升序的,找到target从末尾往前第一次出现的索引。不存在返回-1
// 找区域的右边界。
int search(vector<int>& nums, int target) {
int end = -1;
for(int left = 0, right = nums.size() - 1, mid; left <= right;){
mid = left + ((right - left) >> 1);
if(nums[mid] == target) end = mid;
if(nums[mid] <= target) left = mid + 1; // 找到相同的继续往右边搜。
else right = mid - 1;
}
return end;
}
场景一:查找数字
int search(vector<int>& nums, int target) {
int targetIdx = -1;
for( int left = 0, right = nums.size() - 1, mid; left <= right; )
{
mid = left + ((right - left) >> 1);
if(nums[mid] == target) ret = mid;
if(nums[mid] >= target) right = mid - 1;
else { left = mid + 1; }
}
return targetIdx;
}
场景二:搜索插入位置
int searchInsert(vector<int>& nums, int target) {
int targetIdx = 0;
for(int left = 0, right = nums.size() - 1, mid; left <= right;){
mid = left + ((right - left) >> 1);
if(nums[mid] == target) return mid;
if(nums[mid] > target) targetIdx = mid;
else targetIdx = mid + 1;
if(nums[mid] > target) right = mid - 1;
else left = mid + 1;
}
return targetIdx;
}
场景三:统计数字个数
int search(vector<int>& nums, int target) {
// 通过二分查找法找到target区域的左右边界
int targetLeftIdx = -1, targetRightIdx = -1;
// 找左边界
for(int left = 0, right = nums.size() - 1, mid; left <= right;){
mid = left + ((right - left) >> 1);
if(nums[mid] == target) targetLeftIdx = mid;
if(nums[mid] >= target) right = mid - 1;
else left = mid + 1;
}
// 找到右边界
for(int left = 0, right = nums.size() - 1, mid; left <= right;){
mid = left + ((right - left) >> 1);
if(nums[mid] == target) targetRightIdx = mid;
if(nums[mid] <= target) left = mid + 1;
else right = mid - 1;
}
// 没有找到target
if(targetLeftIdx == -1) return 0;
// 找到target,索引相减 + 1
return targetRightIdx - targetLeftIdx + 1;
}
训练题:移动零
题目:https://leetcode-cn.com/problems/move-zeroes/
void moveZeroes(vector<int>& nums) {
if(nums.size() < 2) return;
const auto size = nums.size();
// 双指针
// p_0: 指向从头开始的第一个0。
// p_num: 指向p_0之后的第一个非零数字。
int p_0 = 0, p_num = 0;
// 指向第一个0
for(; p_0 < size && nums[p_0] != 0; ++p_0);
p_num = p_0 + 1;
while(true){
// 指向第一个0
for(; p_0 < size && nums[p_0] != 0; ++p_0);
// 指向p_0之后的第一个非0
for(; p_num < size && nums[p_num] == 0; ++p_num);
// 交换了之后,p_0肯定是非0,p_num肯定是0。
// 因此将p_0和p_num右移一位
if(p_0 < size && p_num < size) swap(nums[p_0++], nums[p_num++]);
else break;
}
}
void moveZeroes(vector<int>& nums) {
const auto size = nums.size();
int left = 0, right = 0;
// left: left指向第一个非0
// right: right指向第一个0。
for(int left = 0, right = 0; right < size; ++right){
if(nums[right]) swap(nums[right], nums[left++]);
}
}
训练题:两个正序数组的中位数
题目:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/
方法一:模拟法
遍历中位数的位置。
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.empty() && nums2.empty()) return 0;
const auto size1 = nums1.size();
const auto size2 = nums2.size();
// 设:
// nums1 = [1, 3, 5];
// nums2 = [2, 4, 6];
// remainCount = 4;
// 按从小到大的顺序,移动tmpIdx1、tmpIdx2索引,得:
// tmpIdx1 = 2,即移动到了tmpIdx1-1=1索引处,为3。
// tmpIdx2 = 2,即移动到了tmpIdx2-1=1索引处,为4。
int remainCount = ((size1 + size2) >> 1) + 1;
int tmpIdx1 = 0, tmpIdx2 = 0;
for(; tmpIdx1 < size1 && tmpIdx2 < size2 && remainCount ; --remainCount){
if(nums1[tmpIdx1] < nums2[tmpIdx2]) ++tmpIdx1;
else ++tmpIdx2;
}
if(remainCount && tmpIdx1 < size1) tmpIdx1 += remainCount;
if(remainCount && tmpIdx2 < size2) tmpIdx2 += remainCount;
// 若为奇数,则取tmpIdx1-1和tmpIdx2-1索引处更大者。
if((size1 + size2) & 1) {
if(tmpIdx1 > 0 && tmpIdx2 > 0) return max(nums1[tmpIdx1-1], nums2[tmpIdx2-1]);
if(tmpIdx1 > 0) return nums1[tmpIdx1-1];
return nums2[tmpIdx2-1];
}
else{
// 若为偶数,则取tmpIdx-1、tmpIdx1-2、tmpIdx2-1、tmpIdx2-2中最大的两个。
priority_queue<int> q;
if(tmpIdx1 > 0) q.push(nums1[tmpIdx1-- - 1]);
if(tmpIdx1 > 0) q.push(nums1[tmpIdx1 - 1]);
if(tmpIdx2 > 0) q.push(nums2[tmpIdx2-- - 1]);
if(tmpIdx2 > 0) q.push(nums2[tmpIdx2 - 1]);
int mid1 = q.top();
q.pop();
return (mid1 + q.top()) / 2.0;
}
}
方法二:模拟法
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if(nums1.empty() && nums2.empty()) return 0;
const auto size1 = nums1.size();
const auto size2 = nums2.size();
int remainCount = (size1 + size2) / 2 + 1;
int tmpIdx1 = 0, tmpIdx2 = 0;
for(; tmpIdx1 < size1 && tmpIdx2 < size2 && remainCount ; --remainCount){
if(nums1[tmpIdx1] < nums2[tmpIdx2]) ++tmpIdx1;
else ++tmpIdx2;
}
if(remainCount && tmpIdx1 < size1) tmpIdx1 += remainCount;
if(remainCount && tmpIdx2 < size2) tmpIdx2 += remainCount;
if((size1 + size2) & 1) {
if(tmpIdx1 > 0 && tmpIdx2 > 0) return max(nums1[tmpIdx1-1], nums2[tmpIdx2-1]);
if(tmpIdx1 > 0) return nums1[tmpIdx1-1];
return nums2[tmpIdx2-1];
}
else{
// 与上一种方法的区别主要在于偶数情况时的动作。
// 去tmpIdx1-1和tmpIdx1-2的情况有两种
// 一、tmpIdx2无效
// 二、tmpIdx>1且tmpIdx2-1<tmpIdx1-2
if(tmpIdx1 > 0 && tmpIdx2 == 0 || tmpIdx1 > 1 && tmpIdx2 > 0 && nums2[tmpIdx2-1] <= nums1[tmpIdx1-2])
return (nums1[tmpIdx1-1] + nums1[tmpIdx1-2]) / 2.0;
else if(tmpIdx2 > 0 && tmpIdx1 == 0 || tmpIdx2 > 1 && tmpIdx1 > 0 && nums1[tmpIdx1-1] <= nums2[tmpIdx2-2])
return (nums2[tmpIdx2-1] + nums2[tmpIdx2-2]) / 2.0;
return (nums1[tmpIdx1 - 1] + nums2[tmpIdx2-1]) / 2.0;
}
}
};
方法三:模拟法
double findMedianSortedArrays( vector<int> nums1, vector<int> nums2 )
{
const auto size1 = static_cast<int>( nums1.size() );
const auto size2 = static_cast<int>( nums2.size() );
bool even = ( ( size1 + size2 ) & 1 ) == 0;
if( size1 == 0 && size2 == 0 ) { return 0; }
if( size1 == 1 && size2 == 1 ) { return ( nums1[0] + nums2[0] ) / 2.0; }
if( size1 == 0 ) { return even ? ( nums2[size2 >> 1] + nums2[( size2 - 1 ) >> 1] ) / 2.0 : nums2[( size2 >> 1 )] ; }
if( size2 == 0 ) { return even ? ( nums1[size1 >> 1] + nums1[( size1 - 1 ) >> 1] ) / 2.0 : nums1[( size1 >> 1 )] ; }
int remainCount = ( size1 + size2 ) / 2 + 1;
int val1 = 0, val2 = 0;
int tmpIdx1 = 0, tmpIdx2 = 0;
for( ; tmpIdx1 < size1 && tmpIdx2 < size2 && remainCount; --remainCount )
{
if( even ) { val2 = val1; }
if( nums1[tmpIdx1] < nums2[tmpIdx2] ) { val1 = nums1[tmpIdx1++]; }
else { val1 = nums2[tmpIdx2++]; }
}
for( ; tmpIdx1 < size1 && remainCount; --remainCount, ++tmpIdx1 )
{
if( even ) { val2 = val1; }
val1 = nums1[tmpIdx1];
}
for( ; tmpIdx2 < size2 && remainCount; --remainCount, ++tmpIdx2 )
{
if( even ) { val2 = val1; }
val1 = nums2[tmpIdx2];
}
if( !even ) { return val1; }
else { return ( val1 + val2 ) / 2.0; }
}