c++ 新手。因此,对于一项作业,我必须将向量中的所有元素旋转到左侧。因此,例如,元素 {1,2,3} 应旋转到 {2,3,1}。
我正在研究如何做到这一点,并且我看到了rotate()函数,但我认为这在我的代码中不起作用。然后我看到一个 for 循环可以做到这一点,但我不确定如何将其转换为 return 语句。 (我尝试调整但失败)
这是我到目前为止所得到的,但它是非常错误的(我还没有得到一个没有以错误结束的结果)
编辑:我必须处理的向量大小只有三个,因此不需要考虑任何大小的向量
#include <vector>
using namespace std;
vector<int> rotate(const vector<int>& v)
{
// PUT CODE BELOW THIS LINE. DON'T CHANGE ANYTHING ABOVE.
vector<int> result;
int size = 3;
for (auto i = 0; i < size - 1; ++i)
{
v.at(i) = v.at(i + 1);
result.at(i) = v.at(i);
}
return result;
// PUT CODE ABOVE THIS LINE. DON'T CHANGE ANYTHING BELOW.
}
我所有的老师都会上传教科书页面,解释代码的某些部分应该做什么,但教科书页面没有提供任何帮助来尝试弄清楚如何实际应用这些东西。
有人可以给我一些指点吗?
由于您确切地知道有多少个元素,并且这是有意义的旋转的最小数字,因此您不需要做任何花哨的事情 - 只需按照您需要的顺序放置项目,然后返回结果即可:
vector<int> rotate3(const vector<int>& x) {
return vector<int> { x[1], x[2], x[0] };
}
请注意,如果您的集合始终包含三个元素,您可以使用
std::array
代替:
std::array<int,3>
首先,请注意您已将 v 作为 const 引用传递(
const vector<int>&
),因此禁止在 v.at(i) = v.at(i + 1);
中修改 v 的状态
虽然 Sergey 已经回答了一个简单的解决方案,但您可以像这样更正您的代码:
#include <vector>
using namespace std;
vector<int> left_rotate(const vector<int>& v)
{
vector<int> result;
int size = v.size(); // this way you are able to rotate vectors of any size
for (auto i = 1; i < size; ++i)
result.push_back(v.at(i));
// adding first element of v at end of result
result.push_back(v.front());
return result;
}
使用谢尔盖的答案。这个答案涉及为什么提问者尝试的方法不起作用。它们非常接近,所以值得仔细研究,解释问题,并展示如何解决它。
在
v.at(i) = v.at(i + 1);
v
是const
蚂蚁。你不能写它。天真的解决方案(行不通)是去掉中间人并直接写入 result
vector
,因为它不是 const
result.at(i) = v.at(i + 1);
这不起作用,因为
vector<int> result;
定义一个空的
vector
。没有可写入的 at(i)
,因此 at
会引发异常并终止程序。
顺便说一句,
[]
运算符不会像at
那样检查边界,也不会抛出异常。这可能会让您认为程序“正常工作”,而实际上它正在写入不属于“vector
”的内存。这会可能使程序崩溃,但不一定会1。 此处的快速解决方法是确保可用存储空间
vector<int> result(v.size());
生成的代码
vector<int> rotate(const vector<int>& v)
{
// PUT CODE BELOW THIS LINE. DON'T CHANGE ANYTHING ABOVE.
vector<int> result(v.size()); // change here to size the vector
int size = 3;
for (auto i = 0; i < size - 1; ++i)
{
result.at(i) = v.at(i + 1); // change here to directly assign to result
}
return result;
// PUT CODE ABOVE THIS LINE. DON'T CHANGE ANYTHING BELOW.
}
几乎可以工作了。但是当我们在 {1, 2, 3} 上运行时,
result
在末尾保留 {2, 3, 0}。我们丢失了 1。那是因为
v.at(i + 1)
从未触及 v
的第一个元素。我们可以增加 for 循环迭代的次数并使用模运算符vector<int> rotate(const vector<int>& v)
{
// PUT CODE BELOW THIS LINE. DON'T CHANGE ANYTHING ABOVE.
vector<int> result(v.size());
int size = 3;
for (auto i = 0; i < size; ++i) // change here to iterate size times
{
result.at(i) = v.at((i + 1) % size); // change here to make i + 1 wrap
}
return result;
// PUT CODE ABOVE THIS LINE. DON'T CHANGE ANYTHING BELOW.
}
现在输出是 {2, 3, 1}。但做我们正在做的事情并在循环后添加丢失的元素同样简单,而且可能更快一些。
vector<int> rotate(const vector<int>& v)
{
// PUT CODE BELOW THIS LINE. DON'T CHANGE ANYTHING ABOVE.
vector<int> result(v.size());
int size = 3;
for (auto i = 0; i < size - 1; ++i)
{
result.at(i) = v.at(i + 1);
}
result.at(size - 1) = v.at(0); // change here to store first element
return result;
// PUT CODE ABOVE THIS LINE. DON'T CHANGE ANYTHING BELOW.
}
更进一步,三的大小对于这个函数来说是一个不必要的限制,我会摆脱它,并且因为我们保证我们永远不会在 for 循环中超出范围,所以我们不需要额外的测试在
at
vector<int> rotate(const vector<int>& v)
{
// PUT CODE BELOW THIS LINE. DON'T CHANGE ANYTHING ABOVE.
if (v.empty()) // nothing to rotate.
{
return vector<int>{}; // return empty result
}
vector<int> result(v.size());
for (size_t i = 0; i < v.size() - 1; ++i) // Explicitly using size_t because
// 0 is an int, and v.size() is an
// unsigned integer of implementation-
// defined size but cannot be larger
// than size_t
// note v.size() - 1 is only safe because
// we made sure v is not empty above
// otherwise 0 - 1 in unsigned math
// Becomes a very, very large positive
// number
{
result[i] = v[i + 1];
}
result.back() = v.front(); // using direct calls to front and back because it's
// a little easier on my mind than math and []
return result;
// PUT CODE ABOVE THIS LINE. DON'T CHANGE ANYTHING BELOW.
}
我们可以更进一步,使用迭代器和基于范围的
for
循环,但我认为现在这已经足够了。此外,在一天结束时,您完全扔掉该函数并使用
std::rotate
库中的 <algorithm>
。1这被称为未定义行为(UB),关于 UB 最可怕的事情之一是任何事情都可能发生,包括给你预期的结果。我们容忍 UB 因为它可以实现非常快速、多功能的程序。有效性检查不会在您不需要的地方(以及您需要的地方)进行,除非编译器和库编写者决定进行这些检查并提供有保证的行为,例如错误消息和崩溃。例如,Microsoft 在进行调试构建时使用的实现中的 vector
实现中正是这样做的。 Microsoft
vector
的发行版本不进行任何检查,并假设您正确编写了代码,并且希望可执行文件尽可能快。是的,它会起作用。
学习时,“重新发明轮子”(例如自己实现旋转)会有所收获,学习如何使用现有的部分(例如使用标准库算法函数)也会有所收获。
以下是如何使用标准库中的
:
std::vector<int> rotate_1(const std::vector<int>& v)
{
std::vector<int> result = v;
std::rotate(result.begin(), result.begin() + 1, result.end());
return result;
}
#include <array>
#include <bit>
#include <ranges>
template<typename T>
concept scalar = std::integral<T> || std::floating_point<T>;
namespace vec {
namespace storage {
template<scalar T, size_t N, size_t POW2 = std::bit_ceil<size_t>(N)>
using vector_aligned __attribute__((vector_size(POW2 * sizeof(T)))) = T;
template<scalar T, size_t N>
using element_aligned = std::array<T,N>;
};
template<scalar T, size_t N, size_t POW2 = std::bit_ceil<size_t>(N)>
struct type
{
using storage_type = std::conditional_t<N==POW2, storage::vector_aligned<T,N>, storage::element_aligned<T,N>>;
union {
storage_type data;
};
T& operator[](size_t i) { return data[i]; }
template<size_t pos, size_t count>
type<T,N>& rotate(ssize_t i = 1)
{
std::rotate(&data[0] + (pos % N), &data[0] + pos + i, &data[0] + pos + count);
return (*this);
}
template<size_t pos, size_t count>
type<T,N> rotated(ssize_t i = 1)
{
type<T,N> dst = (*this);
dst.template rotate<pos,count>(i);
return dst;
}
};
};
示例:
#include <cstdlib>
#include <cstdio>
#include "vec.hpp"
int main(int argc, char** argv)
{
vec::type<float, 7> v = {1,2,3,4,5,6,7};
vec::type<float, 4> u = {1,2,3,4};
/* rotate 3 elements at pos 1 to the left by 1 */
printf("vector_aligned: %zu/%zu\n", alignof(u), sizeof(u));
printf("[%f %f %f %f]\n", u[0], u[1], u[2], u[3]);
u.rotate<1,3>();
printf("[%f %f %f %f]\n", u[0], u[1], u[2], u[3]);
/* rotate 3 elements at pos 1 to the left by 1 */
printf("element_aligned: %zu/%zu\n", alignof(v), sizeof(v));
printf("[%f %f %f %f %f %f]\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6]);
v.rotate<1,3>();
printf("[%f %f %f %f %f %f]\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6]);
}
输出:
vector_aligned: 16/16
[1.000000 2.000000 3.000000 4.000000]
[1.000000 3.000000 4.000000 2.000000]
element_aligned: 4/28
[1.000000 2.000000 3.000000 4.000000 5.000000 6.000000]
[1.000000 3.000000 4.000000 2.000000 5.000000 6.000000]