46. 全排列
class Solution {
public:
vector<int> vis;//标记数组
vector<int> mid;//中间数组
vector<vector<int>> ans;//答案二维数组
//dfs搜索和回溯求全排列
void dfs(vector<int>& nums,int depth) {
if (depth == nums.size()) {
ans.push_back(mid);//如果搜索深度等于数组大小就说明数组中的全部数字都已经搜索了 直接插入答案数组
}
for (int i = 0; i < nums.size(); i++) {
if (!vis[i]) {
mid.push_back(nums[i]);
vis[i] = 1;//搜索
dfs(nums,depth+1);
mid.pop_back();//回溯
vis[i] = 0;
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<int> vis(nums.size(), 0);
this->vis = vis;
dfs(nums,0);
return ans;
}
};
什么是内存对齐?
内存对齐是一种计算机的内存管理策略,数据存储的基本单位是字节,内存对齐要求数据的存储地址必须是某个值(通常是数据类型自身大小或某个倍数)的整数倍
例如,对于一个 4 字节的整数类型(如int
类型,假设为 32 位系统),其存储地址应该是 4 的整数倍。如果按照自然顺序存储数据,有可能出现数据的存储地址不是其类型大小倍数的情况,而内存对齐就是通过填充字节等方式来保证每个数据的存储地址符合相应的规则。
struct Example {
char a;
int b;
};
在内存中,char
类型通常占 1 个字节,int
类型占 4 个字节。由于内存对齐的要求,编译器可能会在a
和b
之间填充 3 个字节(这取决于编译器和平台的对齐规则),使得b
的存储地址是 4 的整数倍。这样整个结构体的大小可能就不是简单的1 + 4 = 5
字节,而是 8 字节。
为什么需要内存对齐?
硬件方面的原因
提高数据访问速度:现代计算机的硬件体系结构中,处理器访问内存是以字为单位进行的。例如,在 32 位处理器中,字长是 4 字节。如果数据存储的地址是字长的整数倍,那么处理器在读取或写入数据时,可以一次性完整地读取或写入一个数据单元,不需要进行额外的操作来获取完整的数据。如果数据没有对齐,可能会导致处理器需要多次访问内存才能获取完整的数据,从而降低访问速度。
例如,对于一个 32 位处理器访问一个 4 字节的int
数据,如果该int
数据的存储地址是 4 的整数倍,处理器可以一次内存操作读取这个int
数据。但如果存储地址不是 4 的整数倍,处理器可能需要先读取包含该数据起始部分的一个字,再读取包含该数据结束部分的另一个字,然后在内部拼接这些部分来获取完整的int
数据,这会导致额外的内存访问周期和处理时间。
适应硬件设计要求:许多硬件设备(如高速缓存、内存控制器等)的设计是基于内存对齐的假设进行优化的。例如,高速缓存通常按照一定的行(line)大小来存储和读取数据,这些行大小一般是字长的倍数。当数据是内存对齐时,更有利于利用高速缓存,减少缓存未命中(cache miss)的情况,提高数据在高速缓存和主存之间交换的效率。
软件方面的原因
便于编译器优化和代码移植:编译器在生成目标代码时,如果数据是内存对齐的,它可以更好地进行优化。例如,编译器可以利用内存对齐的特性,更有效地安排指令的执行顺序,提高指令级并行性(Instruction - Level Parallelism,ILP),从而生成更高效的机器码。
同时,内存对齐也有助于代码在不同的硬件平台之间的移植。虽然不同平台的对齐规则可能略有差异,但遵循内存对齐原则可以减少因平台差异导致的问题。如果代码在编写时没有考虑内存对齐,在移植到其他对齐要求更严格的平台时,可能会出现性能下降甚至程序错误的情况。