原题
点此链接1
解题思路
参考课本:高等教育出版社 - 陈越主编 - 《数据结构》
参考视频:MOOC - 浙江大学 - 《数据结构与算法》
本题主要考察的是简单插入排序(课本P268)以及归并排序的非递归算法(MOOC 9.4)。两种算法前者较易理解,后者的非递归算法相对更难。
本题的切入点:
- 根据输入数据串判断是插入排序还是归并排序
- 确定插入排序当前的位置
- 确定归并排序当前子串的长度
判断是何排序算法,只需比较两种算法子串的不同即可:
- 插入排序得到的子列必然是一个有序序列 + 剩余原序列
- 归并排序得到的子列必然是一个有序序列 + 无序序列(与原序列不同)
题目中已明确不存在歧义,故而无需考虑其他特殊情况
如果是插入排序,则当前插入位置就是 “有序序列” 的末尾,后续就只需要新增一个插入数据作 “简单插入算法” 并输出结果。
如果是归并排序,则较为繁琐。从这几个角度入手:
- 子序列的长度不会大于目前的有序序列的长度
- 子序列的长度必然是2的幂次
- 每一个子序列都是有序的(注意尾列的长度不一,需要单独处理)
代码
/**
* @file Insert_or_Merge.cpp
* @author your name (you@domain.com)
* @brief https://pintia.cn/problem-sets/16/problems/675
* @version 0.1
* @date 2021-02-20
*
* @copyright Copyright (c) 2021
*
*/
/*
*
* 解题思路
* 判别是归并还是插入
* 读入有序序列
* 后面与原始序列有一个不一样那就是归并排序
* 后面与原始序列完全一样就是插入排序
*
* 如果是插入排序
* 读入下一个数据作插入排序算法
*
* 如果是归并排序
* 确定子串的长度
* 是不是2的幂次,取一个不大于目前有序序列长度的最大的2的幂次数
* 需要简单判断一下现在是不是每一个子串都符合要求,不然就作除2处理再判断
* 作下一次归并运算
*/
#include <iostream>
#include <vector>
using namespace std;
void Merge(vector<int>::iterator beg, vector<int>::iterator lefS,
vector<int>::iterator rigS, const vector<int>::iterator &rigE)
{
auto size = rigE - lefS;
const auto lefE = rigS; // 尾后迭代器!
while (lefS != lefE && rigS != rigE)
{
if (*lefS < *rigS)
*(beg++) = *(lefS++);
else
*(beg++) = *(rigS++);
}
while (lefS != lefE)
*(beg++) = *(lefS++);
while (rigS != rigE)
*(beg++) = *(rigS++);
lefS = rigE - size;
beg = beg - size;
for (auto i = 0; i < size; i++)
*(lefS++) = *(beg++);
}
int main()
{
int n;
cin >> n;
vector<int> d1;
vector<int> d2;
for (auto i = 0; i < n; i++)
{
int tmp;
cin >> tmp;
d1.push_back(tmp);
}
auto res = 0; // 0 -> insertion; 1 -> merge
auto len = 0;
{
auto flag = true;
auto detectFlag = false;
for (auto i = 0; i < n; i++)
{
int tmp;
cin >> tmp;
// 目前还是有序的序列
if (flag)
{
// 发现无序因子
if (!d2.empty() && tmp < d2.back())
{
flag = false;
len = i;
}
}
// 目前已经是无序的因子
else
{
// 发现后续因子与原序列不同,则为归并排序
if (!detectFlag && tmp != d1[i])
{
detectFlag = true;
res = 1;
}
}
d2.push_back(tmp);
}
}
if (res)
{
const auto size = static_cast<int>(d2.size());
cout << "Merge Sort" << endl;
// 选择靠近i的2的次方数
for (auto i = 1;; i *= 2)
if (i >= len)
{
len = i;
break;
}
// 检测片段长度
auto Judge = [&d2, &len, size]() -> bool {
auto iter = d2.begin();
for (; iter < (d2.end() - len); iter += len)
for (auto i = 1; i < len; ++i)
if (*(iter + i - 1) > *(iter + i))
return false;
for (; iter != (d2.end() - 1); ++iter)
if (*iter > *(iter + 1))
return false;
return true;
};
if (len != 1)
while (!Judge())
len /= 2;
auto offset = 0;
for (; offset < (size - 2 * len); offset += (2 * len))
Merge(d1.begin() + offset, d2.begin() + offset, d2.begin() + offset + len, d2.begin() + offset + 2 * len);
// 尾部处理
if (offset + len < size)
Merge(d1.begin() + offset, d2.begin() + offset, d2.begin() + offset + len, d2.end());
}
else
{
cout << "Insertion Sort" << endl;
const auto tmp = d2[len];
auto iter = d2.begin() + len;
for (; iter != d2.begin() && *(iter - 1) > tmp; --iter)
*iter = *(iter - 1);
*iter = tmp;
}
// out the data
{
auto iter = d2.begin();
for (; iter != d2.end() - 1; ++iter)
cout << *iter << " ";
cout << *iter << endl;
}
return 0;
}