1.买股票和卖股票最佳时机
/**
* 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
var max = 0
var min = prices[0]
for(var i=1;i<prices.length;i++){
var temp = prices[i] - min
max = temp > max ? temp : max
min = min>prices[i]?prices[i]:min
}
return max
};
2. 合并区间
/**
* 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
* @param {number[][]} intervals
* @return {number[][]}
*/
var merge = function(intervals) {
intervals = intervals.sort((a,b)=>a[0]-b[0])
var result = [intervals[0]]
for(var i = 1;i<intervals.length;i++){
var last = result[result.length-1]
var current = intervals[i]
if(current[0] > last[1]){
result.push(current)
}else{
result[result.length-1] = [last[0],Math.max(last[1],current[1])]
}
}
return result
};
3.合并两个有序链表
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var mergeTwoLists = function(l1, l2) {
if (l1 === null) {
return l2;
} else if (l2 === null) {
return l1;
} else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
};
4. 爬楼梯
var climbStairs = function(n) {
// 定义一个数组来保存每一层楼梯的不同爬法数
const dp = new Array(n + 1);
// 初始状态:第0层和第1层的不同爬法数均为1
dp[0] = 1;
dp[1] = 1;
// 状态转移方程:第i层的不同爬法数等于第i-1层和第i-2层的不同爬法数之和
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
// 最终结果为第n层的不同爬法数
return dp[n];
};
假设有0级台阶、1阶台阶、2阶台阶。。。n级台阶,所以设置一个长度为(n+1)的数组。如果最终爬到了n级台阶,那么在这之前的一步,根据题意,
一定是爬了1或2个台阶才上到了n级台阶的,所以爬到n级台阶的爬法数是爬到(n-1)级台阶爬法数和爬到(n-2)级台阶爬法数的和,这就有点像斐波那契数列的公式一样。
- 使用最小花费爬楼梯
var minCostClimbingStairs = function(cost) {
const n = cost.length;
// 定义一个数组来保存到达每个台阶的最小花费
const dp = new Array(n);
// 初始状态:到达第0个和第1个台阶的最小花费均为对应的费用
dp[0] = cost[0];
dp[1] = cost[1];
// 状态转移方程:第i个台阶的最小花费为从第i-1个台阶和第i-2个台阶中选择最小花费加上到达第i个台阶的费用
for (let i = 2; i < n; i++) {
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
}
// 比较从第n-1个台阶上楼顶和从第n-2个台阶上楼顶的花费,选较小的那个
return Math.min(dp[n - 1], dp[n - 2]);
};
5.01背包算法
/**
* @description
* 01背包算法
* @private
* @param {any} dataList 红包列表
* @param {any} all 下单金额
* @returns
*/
function knapsack(dataList, all) {
const returnList = [];
for (let i = 0; i < dataList.length; i++) {
// 构建二维数组
returnList[i] = [];
for (let j = 0; j < all; j++) { // 分割背包
const currentBagWeight = j + 1; // 此时背包重量
const currentWeight = dataList[i].rangeBegin; // 此时物品重量
const currentValue = dataList[i].amount; // 此时的价值
const lastW = currentBagWeight - currentWeight; // 此时背包重量减去此时要添加的物品后的重量
// 求子问题最优解,并记录
let fV = lastW >= 0 ? currentValue : 0;
fV = fV + (i > 0 && returnList[i - 1][lastW - 1] ? returnList[i - 1][lastW - 1] : 0);
const nV = i > 0 && returnList[i - 1][j] ? returnList[i - 1][j] : 0;
returnList[i][j] = Math.max(fV, nV);
}
}
// 回溯算法,算出选择的商品
let y = all - 1;
const selectItem = [];
let i = dataList.length - 1;
while (i > -1) {
if (returnList[i][y] === (returnList[i - 1] && returnList[i - 1][y - dataList[i].rangeBegin] || 0) + dataList[i].amount) {
selectItem.push(dataList[i]);
y -= dataList[i].rangeBegin;
}
i--;
}
return selectItem;
}
6. 三数之和
/**给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
const length = nums.length
var result = []
if(length===3){
var sum = nums.reduce((l,r)=>l+r)
result = sum === 0 ? [nums]: []
}else{
let array = []
nums = nums.sort((a,b)=>a-b)
for(var i =0 ;i<length;i++){
for(var j=i+1;j<length;j++){
for(var k=i+2;k<length;k++){
const threeArr = [nums[i],nums[j],nums[k]]
const sort = threeArr.sort().toString()
if(!array.includes(sort)){
array.push(sort)
if((threeArr[0]+threeArr[1]+threeArr[2]) === 0 && i!=j&& j!=k && i!=k){
result.push(threeArr)
}
}
}
}
}
}
return result
};
console.log(threeSum([-2,0,1,1,2]))
7. 字母异位词分组
/**给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
* @param {string[]} strs
* @return {string[][]}
*/
var groupAnagrams = function(strs) {
if(strs.length === 1){
return [strs]
}else{
var map = {}
for(var i= 0 ; i<strs.length; i++){
var array = Array.from(strs[i]).sort()
var key = array.toString()
if(map.hasOwnProperty(key)){
var temp = map[key]
temp.push(strs[i])
map[key] = temp
}else{
map[key] = [strs[i]]
}
}
return Object.values(map)
}
};
8.最小覆盖子串
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function(s, t) {
let l = 0;
let r = 0;
const need = new Map();
for(let c of t){
need.set(c, need.has(c) ? need.get(c) + 1 : 1);
}
let needType = need.size;
let res = '';
while(r < s.length){
const c = s[r];
if(need.has(c)){
need.set(c, need.get(c) - 1);
if(need.get(c) === 0) needType -= 1;
}
while(needType === 0){
const newRes = s.substring(l, r + 1);
if(!res || newRes.length <res.length) res = newRes;
const c2 = s[l];
if(need.has(c2)){
need.set(c2, need.get(c2) + 1);
if(need.get(c2) === 1) needType += 1;
}
l += 1;
}
r += 1;
}
return res;
}
9. 给定数组转换成二叉树
/**
* 给定数组转换成二叉树
* @param {} val_list
*/
function buildTree(val_list){
//数组为空
if(!val_list || val_list.length === 0){
return
}
//根结点
var root = new TreeNode(val_list.shift());
var nodeQueue = [root];
//对根节点进行操作,更新node
while(val_list.length>0){
var node = nodeQueue.shift();
//构建左孩子节点
var leftVal = val_list.shift();
if(val_list.length === 0) break
if(leftVal){
node.left = new TreeNode(leftVal);
nodeQueue.push(node.left);
}
//构建右孩子节点
var rightVal = val_list.shift();
if(val_list.length === 0) break;
if(rightVal){
node.right = new TreeNode(rightVal);
nodeQueue.push(node.rightVal)
}
}
return root
}
function TreeNode(val,left,right){
this.val = val === undefined ? 0 : val;
this.left = left === undefined ? null : left;
this.right = right === undefined ? null : right;
}
var root = [3,9,20,null,null,15,7];
let tree = buildTree(root)
console.log(tree);
10.数组转换成数,树转换成数组,转来转去的,去实现一下
//为啥要这么做呢,因为arrayList这种格式一般是数据库存储的格式,而treeList一般是前端menu的展示数据格式,所以这个算法场景很常见
const arrayList = [
{id: 1, name: '部门1', pid: 0},
{id: 2, name: '部门2', pid: 1},
{id: 3, name: '部门3', pid: 1},
{id: 4, name: '部门4', pid: 3},
{id: 5, name: '部门5', pid: 4},
]
const treeList = [
{
"id": 1,
"name": "部门1",
"pid": 0,
"children": [
{
"id": 2,
"name": "部门2",
"pid": 1,
"children": []
},
{
"id": 3,
"name": "部门3",
"pid": 1,
"children": [
// 结果 ,,,
]
}
]
}
]
function arrayToTree(array){
//递归查找获取子节点
let tree = []
const getChildren = (array,pid)=>{
const children = []
array.forEach(item => {
if(item.id === pid){
children.push(item)
}
})
}
getChildren(array,pid)
return tree
}
function treeToArray(tree){
let array = []
return array
}
console.log(treeToArray())
11.冒泡排序
const arr = [5, 2, 7, 8, 34, 7, 39, 12, 56, 9, 1]
function bubbleSort(arr) {
const len = arr.length
// 外层循环i控制比较的轮数
for (let i = 0; i < len; i++) {
// 里层循环控制每一轮比较的次数j,arr[i] 只用跟其余的len - i个元素比较
for (let j = 1; j < len - i; j++) {
// 若前一个元素"大于"后一个元素,则两者交换位置
if (arr[j - 1] > arr[j]) {
[arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]
}
}
}
return arr
}
console.log(bubbleSort(arr)) // [1, 2, 5, 7, 7, 8, 9, 12, 34, 39, 56]
12.插入排序
const arr = [5, 2, 7, 8, 34, 7, 39, 12, 56, 9, 1]
function insertSort(arr) {
const handle = [arr[0]], len = arr.length
for (let i = 1; i <= len - 1; i++) {
const current = arr[i]
for (var j = handle.length - 1; j >= 0; j--) {
if (current > handle[j]) {
handle.splice(j + 1, 0, current)
break
}
if (j === 0) {
handle.unshift(current)
}
}
}
return handle
}
console.log(insertSort(arr)) // [1, 2, 5, 7, 7, 8, 9, 12, 34, 39, 56]
13.快速排序
const arr = [5, 2, 7, 8, 34, 7, 39, 12, 56, 9, 1]
function quickSort(arr) {
// 4.结束递归(当ary小于等于一项,则不用处理)
if (arr.length <= 1) {
return arr
}
// 1. 找到数组的中间项,在原有的数组中把它移除
const middleIndex = Math.floor(arr.length / 2)
const middle = arr.splice(middleIndex, 1)[0]
// 2. 准备左右两个数组,循环剩下数组中的每一项,比当前项小的放到左边数组中,反之放到右边数组中
const leftArr = [], rightArr = []
for (let i = 0; i < arr.length; i++) {
const current = arr[i]
current < middle ? leftArr.push(current) : rightArr.push(current)
}
// 3. 递归方式让左右两边的数组持续这样处理,一直到左右两边都排好序为止。
//(最后让左边+中间+右边拼接成最后的结果)
return quickSort(leftArr).concat(middle, quickSort(rightArr))
}
console.log(bubbleSort(arr)) // [1, 2, 5, 7, 7, 8, 9, 12, 34, 39, 56]
14. 01背包问题
// 题目描述:
// 有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.
// 问最多能装入背包的总价值是多大?
// 示例1:
// 输入: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
// 输出: 9
// 解释: 装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9
// 示例2:
// 输入: m = 10, A = [2, 3, 8], V = [2, 5, 8]
// 输出: 10
// 解释: 装入 A[0] 和 A[2] 可以得到最大价值, V[0] + V[2] = 10
function knapSack(w,val,capacity,n){
var T = []
for(let i = 0;i < n;i++){
T[i] = [];
for(let j=0;j <= capacity;j++){
if(j === 0){ //容量为0
T[i][j] = 0;
continue;
}
if(j < w[i]){ //容量小于物品重量,本行hold不住
if(i === 0){
T[i][j] = 0; // i = 0时,不存在i-1,所以T[i][j]取0
}else{
T[i][j] = T[i-1][j]; //容量小于物品重量,参照上一行
}
continue;
}
if(i === 0){
T[i][j] = val[i]; //第0行,不存在 i-1, 最多只能放这一行的那一个物品
}else{
T[i][j] = Math.max(val[i] + T[i-1][j-w[i]],T[i-1][j]);
}
}
}
findValue(w,val,capacity,n,T);
return T;
}
//找到需要的物品
function findValue(w,val,capacity,n,T){
var i = n-1, j = capacity;
while ( i > 0 && j > 0 ){
if(T[i][j] != T[i-1][j]){
console.log('选择物品'+i+',重量:'+ w[i] +',价值:' + values[i]);
j = j- w[i];
i--;
}else{
i--; //如果相等,那么就到 i-1 行
}
}
if(i == 0 ){
if(T[i][j] != 0){ //那么第一行的物品也可以取
console.log('选择物品'+i+',重量:'+ w[i] +',价值:' + values[i]);
}
}
}
// w = [2,3,4]. val = [3,4,5] , n = 3 , capacity = 5
//function knapSack([2,3,4],[3,4,5],5,3);
//
var values = [3,4,5],
weights = [2,3,4],
capacity = 5,
n = values.length;
console.log(knapSack(weights,values,capacity,n));