声明一个指向含有10个元素的数组的指针,其中每一个元素是一个函数指针,函数的返回值是int,参数是int*。
1. 首先是 (*p)[10]
2. int (*p)(int *)
3. int (*(*p)[10])(int *);
(*p)(int, int)
int (*p)(int)
关键路径
关键路径是项目管理中重要的概念,特别是在进行项目进度计划和控制时经常使用。
关键路径是指在项目网络计划中具有最长持续时间的路径。这条路径上的每个活动都对项目的总工期有直接影响,如果其中一个活动延误,整个项目的完成时间都会延长。
关键路径上的活动不能延误,否则会影响整个项目的工期,关键路径上的活动都是关键活动,它们的延误会影响完成时间。
关键路径是指在带权有向图中从源点到汇点的所有路径中具有最大路径长度的路径;
关键路径上的所有活动都是关键活动,可通过加快关键活动来缩短整个工程的工期;
AOE网中,关键路径上活动的时间延长多少,整个工程的时间也就随之延长多少;
O(1)时间插入、删除和获取随机元素
变长数组+哈希表
变长数组可以在O(1)的时间内完成获取随机元素操作,哈希表可以在O(1)时间内完成插入和删除操作。
变长数组存储元素+哈希表存储元素对应的下标。
class RandomizedSet {
private:
vector<int> nums;
unordered_map<int, int> indices;
public:
RandomizedSet() {
srand((unsigned)time(NULL));
}
bool insert(int val) {
if(indices.count(val)){
return false;
}
int index = nums.size();
nums.emplace_back(val);
indices[val] = index;
return true;
}
bool remove(int val) {
if(!indices.count(val)){
return false;
}
int index = indices[val];
int last = nums.back();
nums[index] = last;
indices[last] = index;
nums.pop_back();
indices.erase(val);
return true;
}
int getRandom() {
int randomIndex = rand()%nums.size();
return nums[randomIndex];
}
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet* obj = new RandomizedSet();
* bool param_1 = obj->insert(val);
* bool param_2 = obj->remove(val);
* int param_3 = obj->getRandom();
*/
无重复字符的最长子串
给一个字符串s,请找出其中不含有重复字符的最长子串的长度。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left = 0, right = 1, n = s.size(), ret = 0;
if(n <= 1){
return n;
}
unordered_set<char> set1;
set1.insert(s[left]);
while(right < n){
if(!set1.count(s[right])){
set1.insert(s[right]);
ret = max(ret, (int)set1.size());
right++;
}else{
set1.erase(s[left++]);
}
}
return ret;
}
};
有效的数独
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int rows[9][9];
int columns[9][9];
int subboesx[3][3][9];
memset(rows, 0, sizeof(rows));
memset(columns, 0, sizeof(columns));
memset(subboesx, 0, sizeof(subboesx));
for(int i=0; i<9; i++){
for(int j=0; j<9; j++){
char c = board[i][j];
if(c != '.'){
int index = c - '0' - 1;
rows[i][index]++;
columns[j][index]++;
subboesx[i/3][j/3][index]++;
if(rows[i][index] > 1 || columns[j][index] > 1 || subboesx[i/3][j/3][index] > 1){
return false;
}
}
}
}
return true;
}
};
螺旋矩阵
模拟
可以模拟螺旋矩阵的路径。初始位置是矩阵的左上角,初始方向是向右,当路径超出界限或进入之前访问过的位置时,就逆时针旋转,进入下一个方向。
判断路径是否进入之前访问过的位置需要使用一个与输入矩阵大小相同的辅助矩阵visited,其中的每个元素表示该位置是否被访问过。
class Solution {
private:
static constexpr int directions[4][2] = {{0,1}, {1,0}, {0,-1}, {-1,0}};
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.size() == 0 || matrix[0].size() == 0){
return {};
}
int rows = matrix.size(), columns = matrix[0].size();
vector<vector<bool>> visited(rows,vector<bool>(columns));
int total = rows * columns;
vector<int> order(total);
int row = 0, column = 0;
int directionIndex = 0;
for(int i=0; i<total; i++){
order[i] = matrix[row][column];
visited[row][column] = true;
int nextRow = row + directions[directionIndex][0], nextCol = column + directions[directionIndex][1];
if(nextRow < 0 || nextRow >= rows || nextCol < 0 || nextCol >= columns || visited[nextRow][nextCol]){
directionIndex = (directionIndex + 1)%4;
}
row = row + directions[directionIndex][0];
column = column + directions[directionIndex][1];
}
return order;
return order;
}
};
旋转图像
给定一个nxn的二维矩阵,顺时针旋转90度。
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
//矩阵转置
for(int i=0; i<n; i++){
for(int j=i+1; j<n; j++){
swap(matrix[i][j], matrix[j][i]);
}
}
for(int j=0; j<n/2; j++){
for(int i=0; i< n; i++){
swap(matrix[i][j], matrix[i][n-1-j]);
}
}
}
};
先矩阵转置,在左右对称的两列互换。
矩阵置零
给点一个mxn的矩阵,如果一个元素为0,则其所在行和列的所有元素都设为0。
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
vector<bool> row(m), col(n);
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
if(!matrix[i][j]){
row[i] = col[j] = true;
}
}
}
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
if(row[i] || col[j]){
matrix[i][j] = 0;
}
}
}
}
};
生命游戏
复制原数组进行模拟
这个问题看起来很简单,但有一个陷阱,如果你直接根据规则更新原始数组,那么就做不到题目中说的 同步 更新。假设你直接将更新后的细胞状态填入原始数组,那么当前轮次其他细胞状态的更新就会引用到当前轮已更新细胞的状态,但实际上每一轮更新需要依赖上一轮细胞的状态,是不能用这一轮的细胞状态来更新的。
Linux系统中所有进程都是由器父进程创建出来的,譬如我们在终端下执行某个应用程序。
这个程序启动之后就是一个进程,这个进程就是由它的父进程(也就是shell进程)所创建出来的。
shell进程就是shell解析器(shell解析器有很多种,譬如bash,sh等),所谓解析器就是解析用户输入的各种命令,然后做出相应的响应,执行相应的程序。
那既然所有的进程都是由器父进程创建出来的,那么总会存在一个最原始的父进程(所有进程的祖先),这个祖先进程就是init进程。
init进程的PID是1,它是所有子进程的父进程。
获取父进程的id:getppid()。
进程空间
在Linux系统中,进程与进程之间、进程与内核之间是相互隔离的,各自在自己的进程空间中运行(内核就是在自己的内核空间中运行);一个进程不能读取或修改另一个进程或内核的内存数据,这样提高了系统的安全性与稳定性。
新进程被创建出来之后,便是一个独立的进程,拥有自己独立的进程空间,拥有自己唯一的进程号(PID),拥有自己独立的PCB,新进程会被内核调度执行,参与到系统调用中。
一次for调用会产生两次返回值。
fork创建了一个与原来进程几乎完全相同的进程
理解fork()系统调用的关键在于,完成对其调用后将存在两个进程,一个是原进程,另一个是创建出来的子进程,并且每个进程都会从fork()函数的返回出继续执行,会导致调用fork()返回两次值,子进程返回一个值,父进程返回一个值。
fork()调用成功后,子进程和父进程会继续执行fork()调用之后的指令,子进程、父进程各自在自己的进程空间中运行。
事实上,子进程是父进程的一个副本,子进程拷贝了父进程的数据段、堆、栈以及继承了父进程打开的文件描述符,父进程与子进程并不共享这些存储空间,这是子进程对父进程相应部分存储空间的完全复制,执行fork()之后,每个进程均可修改各自的栈数据以及堆段中的变量,而不影响另一个进程。
但对于程序代码段是共享的,内存中只存在一份。