This commit is contained in:
Estom
2021-09-18 10:19:59 +08:00
parent 12464c187b
commit c357fb056b
17 changed files with 478 additions and 23 deletions

View File

@@ -0,0 +1,47 @@
#include<bits/stdc++.h>
using namespace std;
/**
* 交换任意两数的本质是改变了元素位置,
* 故建立元素与其目标状态应放置位置的映射关系
*/
int getMinSwaps(vector<int> v)
{
vector<int> v1(v); //将A内元素复制到B。
sort(v1.begin(), v1.end());
map<int,int> m;
int len = v.size();
for (int i = 0; i < len; i++)
{
m[v1[i]] = i; // 建立每个元素与其应放位置的映射关系
}
int loops = 0; // 循环节个数
vector<bool> flag(len, false); //初始化
//找出循环节的个数
for (int i = 0; i < len; i++)
{
if (!flag[i])
{
int j = i;
while (!flag[j]) //对环处理
{
flag[j] = true;
j = m[v[j]]; //原序列中j位置的元素在有序序列中的位置
}
loops++;
}
}
return len - loops;
}
vector<int> v;
int main()
{
int n,k;
cin>>n;
while(n--){
cin>>k;
v.push_back(k);
}
int num = getMinSwaps(v);
cout<<"交换次数:"<<num<<endl;
return 0;
}

View File

View File

@@ -26,6 +26,7 @@
* 流程
* [x] 拒绝了农银金科的笔试(说实话有点蠢,分行能进也不错了)
* [x] 完成了简历投递
* [ ] 农行笔试2021年9月17日18:00-20:30
### 建设银行

View File

@@ -26,7 +26,7 @@
* 8、对隐私计算联邦学习、多方安全计算、机密计算有了解优先。
* 流程
* [x] 简历投递与直通终面协商。https://jobs.bytedance.com/campus/position/application
* [ ] 直通终面时间2021-09-13 15:30面试链接https://people.toutiaocloud.com/hire/bridge/video/interviewee/fe3ffa33-19ab-4be9-aa9e-4bd1af49d05c
* [x] 直通终面时间2021-09-13 15:30面试链接https://people.toutiaocloud.com/hire/bridge/video/interviewee/fe3ffa33-19ab-4be9-aa9e-4bd1af49d05c
### 阿里
@@ -78,7 +78,7 @@
* [x] 百度一面2021-08-30 14:00-15:00https://code.meideng.net/ykl1
* [x] 建立共享当中,应该是面试挂掉了
* [x] 新的笔试都挂了不应该啊2021年-09-07 19:00:00 -- 21:00:00
* [ ] 2021-09-11 16:00百度一面 9-11 16:00-17:00
* [x] 2021-09-11 16:00百度一面 9-11 16:00-17:00
@@ -135,6 +135,8 @@
* 流程
* [x] 官网投递https://campus.163.com/app/personal/apply
* [ ] 网易笔试9月18日周六14:00笔试时长预计3小时
* [ ] 网易雷火2021-09-18 19:00-2021-09-18 21:00
### 搜狐
* 岗位
@@ -207,6 +209,7 @@
* 流程
* [x] 简历投递2021-09-09
* [ ] 快手一面2021-09-18 11:00(GMT+08:00)
----

View File

@@ -13,8 +13,8 @@
* [x] 标准库
* [x] 面向对象
* [x] 设计模式
* [ ] 并发编程
* [ ] 网络编程
* [x] 并发编程
* [x] 网络编程
* [ ] GO
* [x] 重新整理go知识在原先的基础上进行了扩充
* [ ] 基础知识。
@@ -54,7 +54,7 @@
* [ ] 算法编程(力扣刷题)
### 简历准备
* [ ] 对简历上的项目进行介绍
* [x] 对简历上的项目进行介绍
## 计划

View File

@@ -1 +0,0 @@
1. 关于封装的讨论。

View File

@@ -0,0 +1,6 @@
2021年9月16日
1. 完成算法复习
2. 做完携程的笔试
3. 开始LeetCode刷题每天30个
4. 开始面经复习(每天一门数据库、操作系统、计算机网络)

View File

@@ -82,12 +82,12 @@ $$
* 头递归:递归发生在函数的其他处理代码之前(或理解为,递归发生在函数的头部或顶部)。**需要先解决后续问题,上层依赖下层,最顶层得到结果,直接返回**。
* 需要使用后续递归后的结果,参与本层的计算,然后返回给上一层。这时,需要在下层的递归完成后,才能计算。如归并计算,需要下一层拍好顺序,这一次才会执行归并操作。
* 尾递归:递归发生在函数其他处理代码的后面(或理解为,递归发生在函数的尾部)。**需要先解决当前问题,下层依赖上层,最底层得到结果,返回顶层**。
* 本层内的计算是为了确定递归过程中的条件。且下一层返回的结果,能够作为本层的结果返回。本层不知道改成的结果,结果取决于最底层。如搜索树的最大深度,只有递归到叶节点才知道最大深度是多少。
* 本层内的计算是为了确定递归过程中的条件。且下一层返回的结果,能够作为本层的结果返回。本层不知道改成的结果,结果取决于最底层。如搜索树的最大深度,只有递归到叶节点才知道最大深度是多少。快排也是,先解决本层的分割排序,在解决下一层的。
* 中间部分递归:递归发生在函数体的中间部分。
* 头递归和尾递归只是描述递归发生的相对顺序。实际上在递归过程中,递归可以发生在任何位置。递归前的代码负责处理本层的与下一层无关的内容。递归后的代码负责利用下一层的返回值或其他变量处理内容。
### 单分支递归和多分支递归
* 单分支递归:每一层只是常数级别缩小问题的规模。进行一次递归处理下一个问题。每一层只**调用一次递归函数**。如阶乘问题青蛙跳问题等。
* 单分支递归:每一层只是常数级别缩小问题的规模。进行一次递归处理下一个问题。每一层只**调用一次递归函数**。如阶乘问题青蛙跳问题等。单分支递归往往能写成循环的形式,如二分法的递归和循环都可以。
* 多分支递归:当一个问题可以分为多个相同的子问题的时候。在同一层进行多分支递归。每一层**多次调用递归函数**。如树和图的遍历问题,有多个下层子节点。
## 3 递归的实现
@@ -118,7 +118,7 @@ $$
* 递归算法本质上是一种自顶向下的思考模式。即数学上所说的归纳法。有结果一步一步归纳,得到验证其条件的正确性。问题规模逐渐变小。
* 非递归算法的本质是一种自底向上的思考模式。即数学上所说的推导法。将一些零碎的部分逐渐求解,渐渐得到最终的结果。问题逐渐组装成目标问题。
* 在使用递归的时候,应该从顶层考虑怎么分割。在使用非递归算法的时候,应该考虑怎样从层进行组合。
* 在使用递归的时候,应该从顶层考虑怎么分割。在使用非递归算法的时候,应该考虑怎样从层进行组合。
## 4 迭代法概述

View File

@@ -15,7 +15,7 @@
如果有向图具有一个有向的回路,该问题是无解的。因此,为了使得拓扑排序成为可能,问题中的图必须是一个无环有向图。
### 减治原理
### 拓扑排序-减治原理
基于减(减一)治技术的一个直接实现:重复以下过程:
@@ -23,4 +23,165 @@
2. 然后把该源和所有从它出发的边都删除。(如果有多个这样的源,可以任意选择一个;如果这样的源不存在,算法停止,因为该问题是无解的)
3. 顶点被删除的次序就是拓扑排序的一个解。
```c++
// 拓扑排序——循环入度为零
vector<int> tuopu1(vector<vector<int>> vec){
vector<int> res;
vector<int> in(vec.size(),0);
vector<int> flag(vec.size(),0);
for(int i=0;i<vec.size();i++){
for(int j=0;j<vec[i].size();j++){
in[vec[i][j]]++;
}
}
for(int i=0;i<vec.size();i++){
int j;
// 查找入度为零的节点,添加到结果中,并标记
for(j=0;j<vec.size();j++){
if(in[j]==0 && flag[j]==0){
flag[j]=1;
res.push_back(j);
break;
}
}
// 将该节点的下一个节点的入度减1
for(auto a:vec[j]){
in[a]--;
}
}
return res;
}
```
### 拓扑排序-深度搜索
基于深度优先搜索的方法实现
深度优先搜索或广度优先搜索整个图将出度为0的顶点入栈中途要判断是否会形成环最后输出栈得到的序列就是该图的一种拓扑排序。
```c++
// 拓扑排序——深度递归+栈
void tuopu2(vector<vector<int>> &vec,stack<int> &st,vector<int> &flag,int node){
for(int i=0;i<vec[node].size();i++){
if(flag[vec[node][i]]==0){
tuopu2(vec,st,flag,vec[node][i]);
}
}
if(flag[node]==0){
st.push(node);
flag[node]=1;
}
}
```
## 2 深度学习算子
### 问题描述
一般深度学习网络有若干算子组成假设一个深度学习网络模型是一个有向无环图。若算子A依赖算子B的输出则晋档算子B执行完成后才能执行算子A,没有依赖关系的算子可以并行执行。已知每个算子的执行时间,请计算运行整个网络所需要的的最小执行时间。
1. 不考虑算子之间的传输时间
2. 第一个算子为输入算子且仅有一个输入算子
3. 算子的索引从0开始
输入描述
N+1行。N为算子的总个数第一行输入N,N<100.第j行输入代表索引为j-2的算子的属性。包括算子的时间和算子的后继算子。
实例
```
4
10 1 2
5
1 3
2
```
### 问题分析
1. 图问题
2. 拓扑排序
3. 深度搜索
### 策略选择
1. 深度优先搜索
2. 记录最长的路径
### 算法设计
### 算法分析
时间复杂度 O(n*v)
空间复杂度 O(n)
### 算法实现
```c++
// 使用深度优先搜索计算
int dfs(vector<vector<int>> &vec,vector<int> &cost,int node,int c){
int max_cost=c+cost[node];
// cout<<max_cost<<endl;
for(int i=0;i<vec[node].size();i++){
max_cost = max(max_cost,dfs(vec,cost,vec[node][i],c+cost[node]));
}
return max_cost;
}
int main(){
string input="4\nsoftmax 10 1 2\nrelu 5\nconv1 1 3 1\nsoftmax 2";
istringstream cin(input);
int N ;
cin>>N;
cout<<N<<endl;
// 表示每个边花费的时间
vector<int> cost(N,0);
// 使用邻接链表表示关系吧。单点触发的所有边
vector<vector<int>> vec(N,vector<int>());
cin.ignore();
// string input2;
// getline(cin,input2);
for(int i=0;i<N;i++){
string line;
getline(cin,line,'\n');
// cout<<line<<endl;
istringstream is(line);
string name;
is>>name;
is>>cost[i];
int next;
while(is>>next){
vec[i].push_back(next);
}
cout<<name<<":"<<cost[i]<<endl;
}
// for(auto a:vec){
// for(auto b:a){
// cout<<b<<" ";
// }
// cout<<endl;
// }
cout<<"result:"<<dfs(vec,cost,0,0)<<endl;
// 进行拓扑排序并查看结果
vector<int> res = tuopu1(vec);
for(auto a:res){
cout<<a<<" ";
}
cout<<endl;
vector<int> flag(vec.size(),0);
stack<int> st;
tuopu2(vec,st,flag,0);
vector<int> res2;
while(!st.empty()){
cout<<st.top()<<" ";
res2.push_back(st.top());
st.pop();
}
cout<<endl;
// 然后根据拓扑排序计算最长时间。没哟必要
}
```

View File

@@ -0,0 +1,115 @@
#include<iostream>
#include<vector>
#include<algorithm>
#include<sstream>
#include<stack>
using namespace std;
// 拓扑排序——循环入度为零
vector<int> tuopu1(vector<vector<int>> vec){
vector<int> res;
vector<int> in(vec.size(),0);
vector<int> flag(vec.size(),0);
for(int i=0;i<vec.size();i++){
for(int j=0;j<vec[i].size();j++){
in[vec[i][j]]++;
}
}
for(int i=0;i<vec.size();i++){
int j;
// 查找入度为零的节点,添加到结果中,并标记
for(j=0;j<vec.size();j++){
if(in[j]==0 && flag[j]==0){
flag[j]=1;
res.push_back(j);
break;
}
}
// 将该节点的下一个节点的入度减1
for(auto a:vec[j]){
in[a]--;
}
}
return res;
}
// 拓扑排序——深度递归+栈
void tuopu2(vector<vector<int>> &vec,stack<int> &st,vector<int> &flag,int node){
for(int i=0;i<vec[node].size();i++){
if(flag[vec[node][i]]==0){
tuopu2(vec,st,flag,vec[node][i]);
}
}
if(flag[node]==0){
st.push(node);
flag[node]=1;
}
}
// 使用深度优先搜索计算
int dfs(vector<vector<int>> &vec,vector<int> &cost,int node,int c){
int max_cost=c+cost[node];
// cout<<max_cost<<endl;
for(int i=0;i<vec[node].size();i++){
max_cost = max(max_cost,dfs(vec,cost,vec[node][i],c+cost[node]));
}
return max_cost;
}
int main(){
string input="4\nsoftmax 10 1 2\nrelu 5\nconv1 1 3 1\nsoftmax 2";
istringstream cin(input);
int N ;
cin>>N;
cout<<N<<endl;
// 表示每个边花费的时间
vector<int> cost(N,0);
// 使用邻接链表表示关系吧。单点触发的所有边
vector<vector<int>> vec(N,vector<int>());
cin.ignore();
// string input2;
// getline(cin,input2);
for(int i=0;i<N;i++){
string line;
getline(cin,line,'\n');
// cout<<line<<endl;
istringstream is(line);
string name;
is>>name;
is>>cost[i];
int next;
while(is>>next){
vec[i].push_back(next);
}
cout<<name<<":"<<cost[i]<<endl;
}
// for(auto a:vec){
// for(auto b:a){
// cout<<b<<" ";
// }
// cout<<endl;
// }
cout<<"result:"<<dfs(vec,cost,0,0)<<endl;
// 进行拓扑排序并查看结果
vector<int> res = tuopu1(vec);
for(auto a:res){
cout<<a<<" ";
}
cout<<endl;
vector<int> flag(vec.size(),0);
stack<int> st;
tuopu2(vec,st,flag,0);
vector<int> res2;
while(!st.empty()){
cout<<st.top()<<" ";
res2.push_back(st.top());
st.pop();
}
cout<<endl;
// 然后根据拓扑排序计算最长时间。没哟必要
}

View File

@@ -16,7 +16,7 @@
### 算法设计
* 归并排序」是分治思想的典型应用,它包含这样三个步骤:
* 分解: 待排序的区间为 [l, r][l,r],令 m = \lfloor \frac{l + r}{2} \rfloorm=⌊2l+r⌋,我们把 [l, r][l,r] 分成 [l, m][l,m] 和 [m + 1, r][m+1,r]
* 分解: 待排序的区间为 [l, r],令 $m = \lfloor \frac{l + r}{2} \rfloor$,我们把 [l, r] 分成 [l,m] 和 [m+1,r]
* 解决: 使用归并排序递归地排序两个子序列
* 合并: 把两个已经排好序的子序列 [l, m][l,m] 和 [m + 1, r][m+1,r] 合并起来
@@ -24,8 +24,8 @@
### 算法分析
* 时间复杂度:同归并排序 O(n \log n)O(nlogn)。
* 空间复杂度:同归并排序 O(n)O(n)
* 时间复杂度:同归并排序 O(nlogn)。
* 空间复杂度:同归并排序 O(n)
### 算法实现

View File

@@ -108,7 +108,7 @@ $$
* 树形动态规划。非线性规模增长。非线性决策过程。第二个规模增长方向构成该阶段的状态集合每个阶段的状态并非一个但是每个阶段的状态数量不确定。例如三角形数组最小路径和、单向最短路径问题。时间复杂度为O(n*m)//m表示某一阶段最大的状态数量。
* 矩阵动态规划。非线性规模增长。非线性决策。第二个规模增长方向与第一个规模增长方向独立不受第一个规模增长影响每个阶段的状态数量是固定的。例如背包问题、n个骰子的点数问题。O(n*m)。由于矩阵可以用来表示图。也可以定义为图型动态规划
* 序列动态规划。非线性规模增长。非线性决策。第二个规模增长的方向不受第一个规模增长的影响矩阵动态规划的特殊形式针对序列的动态规划。例如最长公共子序列、正则表达式匹配。O(n*m)
* 二维规模增长的冬天规划,第一维表示的行,第二维表示列。第一维为主要的规模增长方向,第二维是第一维某一阶段的状态集合。
* 二维规模增长的动态规划,第一维表示的行,第二维表示列。第一维为主要的规模增长方向,第二维是第一维某一阶段的状态集合。
## 4.1 线性动态规划
## 4.2 树型动态规划

View File

@@ -47,7 +47,7 @@ $$
### 算法实现
```
//@五分钟学算法//www.cxyxiaowu.compublic int lengthOfLIS(int[] nums) {
int lengthOfLIS(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}

View File

@@ -172,7 +172,7 @@ $$
### 算法实现
```
//@五分钟学算法//www.cxyxiaowu.compublic int minCost(int[][] costs) {
int minCost(int[][] costs) {
if (costs == null || costs.length == 0) {
return 0;
}

View File

@@ -28,13 +28,13 @@ F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
### 选择策略
* 数组
* 动态规划
* 原理: 以斐波那契数列性质 f(n + 1) = f(n) + f(n - 1)f(n+1)=f(n)+f(n1) 为转移方程。
* 原理: 以斐波那契数列性质f(n+1)=f(n)+f(n1) 为转移方程。
### 算法设计
* 状态定义: 设$dp$为一维数组,其中 $dp[i]$ 的值代表 斐波那契数列第 $i$个数字 。
* 转移方程: $dp[i + 1] = dp[i] + dp[i - 1]$,即对应数列定义 $f(n + 1) = f(n) + f(n - 1)$
* 初始状态: $dp[0] = 0 dp[1] = 1$,即初始化前两个数字;
* 初始状态: $dp[0] = 0,dp[1] = 1$,即初始化前两个数字;
* 返回值:$dp[n]$ ,即斐波那契数列的第 n 个数字。
@@ -122,12 +122,12 @@ public int climbStairs(int n) {
### 算法设计
* **状态定义** 设动态规划列表 dp dp[i]dp[i] 代表以元素 nums[i]nums[i] 为结尾的连续子数组最大和。
* 为何定义最大和 dp[i]dp[i] 中必须包含元素 nums[i]nums[i] :保证 dp[i]dp[i] 递推到 dp[i+1]dp[i+1] 的正确性;如果不包含 nums[i]nums[i] ,递推时则不满足题目的 连续子数组 要求。
* **转移方程** dp[i-1] \leq 0dp[i1]≤0 ,说明 dp[i - 1]dp[i1] 对 dp[i]dp[i] 产生负贡献,即 dp[i-1] + nums[i]dp[i1]+nums[i] 还不如 nums[i]nums[i] 本身大。
* 当 dp[i - 1] > 0dp[i1]>0 时:执行 dp[i] = dp[i-1] + nums[i]dp[i]=dp[i1]+nums[i]
* 当 dp[i - 1] \leq 0dp[i1]≤0 时:执行 dp[i] = nums[i]dp[i]=nums[i]
* 为何定义最大和 dp[i] 中必须包含元素 nums[i] :保证dp[i] 递推到 dp[i+1]的正确性如果不包含nums[i] ,递推时则不满足题目的 连续子数组 要求。
* **转移方程** 若dp[i1]≤0 ,说明 dp[i - 1]对 dp[i] 产生负贡献,即 dp[i-1] + nums[i]还不如 nums[i]本身大。
* 当 dp[i - 1] > 0时执行 dp[i] = dp[i-1] + nums[i]
* 当 dp[i1]≤0 时:执行 dp[i] = nums[i]
* **初始状态** dp[0] = nums[0]dp[0]=nums[0],即以 nums[0]nums[0] 结尾的连续子数组最大和为 nums[0]nums[0] 。
* **返回值** 返回 dpdp 列表中的最大值,代表全局最大值。
* **返回值** 返回 dp 列表中的最大值,代表全局最大值。
![](image/2021-03-29-09-50-50.png)

View File

@@ -0,0 +1,83 @@
## 零钱兑换
### 问题描述
给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
假设每一种面额的硬币有无限个。 
```
输入amount = 5, coins = [1, 2, 5]
输出4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
```
### 问题分析
* 典型的动态规划
* 典型的完全背包问题
### 策略选择
* 动态规划
* 采取两个方向上的动态规划。两个方向上的动态规划是非等价独立的。
* 数组矩阵线性数据结构
### 算法设计
* 阶段划分:规模增长的方向有两个。第一个规模增长的方向是硬币的种类。第二个规模增长的方向是金钱的总量。
* 状态变量dp[i][j]表示使用第i个硬币达到j分钱所有的可能。
* 状态转移方程
* if k*coins[i]== amount dp[i][j]++;
* if k*coins[i]<amount dp[i][j]+=dp[i-1][j-k*conis[i]] foreach k
* 边界条件
* 当amount=0应该只有一种策略一个不选。当amount=n、i=coins.size()终止
* 补充:可以对状态空间进行压缩,提出了一种改进方案。直接以硬币的数目作为第二层遍历。
### 算法分析
时间复杂度O(m*n)
空间复杂度O(m*n)
### 算法实现
```C++
class Solution {
public:
// 其实就是个很简单的完全背包问题。完全可以设置两个规模增长方向
int change2(int amount, vector<int>& coins) {
vector<int> dp(amount + 1);
dp[0] = 1;
for (int& coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
// 使用两个规模增长方向
int change(int amount, vector<int>& coins) {
if(amount==0)return 1;
vector<vector<int>> dp(coins.size()+1,vector<int>(amount + 1,0));
for (int k=1;k<=coins.size();k++) {
int coin=coins[k-1];
for(int i=1;i<=amount;i++){
for(int j=0;j*coin<=i;j++){
if(j*coin==i){
dp[k][i]++;
}
dp[k][i]+=dp[k-1][i-j*coin];
}
}
}
return dp[coins.size()][amount];
}
};
```

View File

@@ -72,4 +72,44 @@ public:
return dp[m - 1][n - 1];
}
};
```
## 递归法
### 代码实现
```
class Solution {
public:
bool isMatch(string s, string p) {
return isMatchR(s,p,0,0);
}
// 递归和循环混搭,果然很恶心。直接使用递归好了。
bool isMatchR(string &s,string &p,int i,int j){
//递归终止的条件
if(i>=s.size() && j>=p.size()){
return true;
}
// cout<<i<<j<<endl;
// *判断。
if(j+1<p.size() && p[j+1]=='*'){
// 匹配零次 || 匹配一次
return isMatchR(s,p,i,j+2) || ((i<s.size()) && (p[j]==s[i]||p[j]=='.')&&isMatchR(s,p,i+1,j));
}
// 如果下一个不是*
if(i<s.size() && j<p.size() && p[j]==s[i]){
return isMatchR(s,p,i+1,j+1);
}
if(i<s.size() && j<p.size() && p[j]=='.'){
return isMatchR(s,p,i+1,j+1);
}
// 不匹配
return false;
}
};
```