1.分治法
工作原理:
找到基线条件 => 不断分解问题 => 直到符合基线条件得出题干解
思路:
1.基线分割
2.完成单个模块功能
3.递归
例题:
将数组元素由小到大排序
//分割之后分别比较,再递归
const quickSort = function(arr) {
// 检测,停止递归
if (arr.length <= 1) {
return arr;
}
// 绝对的二分之一
let pivotIndex = Math.floor(arr.length / 2);
let pivot = arr.splice(pivotIndex, 1)[0];
let left = [];
let right = [];
for(let i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat([pivot], quickSort(right));
}
2.贪婪
利益最大化,始终查找最大项目,尽可能快的去满足
思路:
- 最大子序列和为sum,结果是ans
- 遍历 => 判断sum是否有buff => 如果没有直接抛弃 => 比较sum和ans
- nums => ans
面试题:
给定一个整数数组nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和
[1, 2, 3, 4, -4, 3, 6] => 10
// 一直加,加之后比较加之前,大的保存,如果小于0了,从下一个数组元素开始累加
const maxSubArray = function(nums) {
let ans = nums[0];
let sum = 0;
for(const num of nums) {
if(sum > 0) {
sum += num;
} else {
sum = num;
}
ans = Math.max(ans, sum);
}
return ans;
}
3.动态规则
走一步看一步
思路:
- 找到落脚点
- 通过落脚点之外的内容,找到关联
- 关联统一递归or循环
面试题:
实现当前算法求和
数组,每一项等于前两项之和
[0,1,1,2,3,5,8]
F(0) = 0, F(1) = 1, F(3) = 1, F(4) = 2, ……, F(n) = F(n-1) + F(n-2) 其中n>1
const fib = function(n) {
if (n < 2) {
return n;
}
let pre = 0;
let next = 0;
let result = 1;
for(let i = 2; i <= n; i++) {
pre = next;
next = result;
result = pre + next;
}
return result;
}
4.图与 图算法
构成:边集合 和 顶点集合
分类:有向图、无向图、构造图
// 试题 实现图类
class Graph {
constructor(v) {
this.vertices = v; // 顶点数
this.edges = 0; // 边集合
this.arr = [];
// 初始化元素集合 => 多少个顶点就有多少个元素
for(let i = 0; i < this.vertices; i++) {
this.arr[i] = [];
}
}
showGraph() {
for (let i = 0; i < this.vertices; i ++) {
let str = i + '->';
for(let j = 0; j < this.vertices; j++) {
if(this.arr[i][j] !== undefined) {
str += this.arr[i][j] + '';
}
}
}
console.log(str);
}
addEdge(v, w) {
this.arr[v].push(w);
this.arr[w].push(v);
this.edges++;
}
// traverse() {}
}
// 图解决深度优先问题
// 起始点开始搜索,直到最终顶点 => 再返回追溯 => 没有路径
// 1. 结构化补充
constructor() {
// super()
this.marked = []; // 已经访问过的节点
for(let i = 0; i < this.vertices; i++) {
this.marked[i] = false;
}
}
// 2. 深度优先开始
dfs(v) {
this.marked[v] = true;
if (this.arr[v] !== undefined) {
console.log('visited' + v);
}
this.arr[v].forEach(w => {
if(!this.marked[w]) {
this.dfs(w);
}
})
}
// 3. 广度优先
// 1. 查找当前节点相邻的顶点,依次访问,由近及远
// 2. 取出下一个顶点,加入到marked已访问列表
// 3. 相邻未访问顶点添加到组织队列
bfs(s) {
let queue = [];
this.marked[s] = true;
queue.push(s);
while(queue.length > 0) {
let v = queue.shift();
if(v !== undefined) {
console.log('visited' + v);
}
this.arr[v].forEach(w => {
if (!this.marked[w]) {
this.marked[w] = ture;
queue.push(w);
}
})
}
}
// 面试题 最短路径方法
// 利用广度优先天然查找路径的方式
// 1. 保存记录一个顶点到另一个顶点的所有边 - edgeTo
// 2. 每遇到一个未标记的顶点,除了标记,还需要从列表中添加边,从顶点连到当前顶点
constructor() {
// super()
this.edgeTo = [];
}
bfs(s) {
let queue = [];
this.marked[s] = true;
queue.push(s);
while(queue.length > 0) {
let v = queue.shift();
if(v !== undefined) {
console.log('visited' + v);
}
this.arr[v].forEach(w => {
if (!this.marked[w]) {
this.marked[w] = ture;
this.edgeTo[w] = v; // 做一个顶点连接记录
queue.push(w);
}
})
}
}
pathTo(t, v) {
let source = t;
// 检测
for (let i = 0; i < this.vertices; i++) {
this.marked[i] = false;
}
this.bfs(source);
if (!this.marked[v]) {
return undefined;
}
let path = [];
let str = '';
for(let i = v; i !== source; i = this.edgeTo[i]) {
path.unshift(i);
}
path.unshift(source);
for (let i in path) {
if(i < path.length - 1) {
str += path[i] + '->';
} else {
str += path[i];
}
}
console.log(str);
return path;
}
// %%%%%%%%%%%%%%%%%%%%%%%%
const g = new Graph(5);
g.addEdge(0, 1);
g.addEdge(0, 2);
// ……
g.pathTo(0, 4);
// 1. 图类 - 路径问题
// 2. 实现图类 - 边 + 顶点
// 3. 处理路径问题