接雨水
仔细分析下来发现接多少雨水的关键是墙,如果没有墙壁,水就蓄不起来。
所以对于每个柱子它能蓄多少水,要看它的左边最高的柱子和它右边最高的柱子,取这两者的较小值来作为当前柱子的蓄水能力值。
如果两者的较小值小于等于当前柱子的高度,说明当前柱子它蓄不了水,否则它的蓄水能力就是这个较小值减去当前柱子的高度。
var trap = function(height) {
const left = new Array(height.length).fill(0)
const right = new Array(height.length).fill(0)
let max = height[0]
for (let i = 1; i < height.length; i ++) {
left[i] = max
max = Math.max(max, height[i])
}
max = height[height.length - 1]
for (let i = height.length - 2; i >= 0; i --) {
right[i] = max
max = Math.max(max, height[i])
}
let ans = 0
for (let i = 1; i < height.length - 1; i ++) {
const cur = height[i]
const m = Math.min(left[i], right[i])
if (m > cur) {
ans += (m - cur)
}
}
return ans
};
作者:scnu_evan
链接:https://leetcode-cn.com/problems/trapping-rain-water/solution/javascript-42-jie-yu-shui-by-scnu_evan-u0nu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:scnu_evan 链接:leetcode-cn.com/problems/tr… 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
接雨水2
股票买卖
//动态规划
function maxProfit(prices: number[]): number {
let minPrice = Number.MAX_VALUE;
let maxProfit = 0;
//前i天的最大收益 = max{前i-1天的最大收益,第i天的价格 - 前i-1天中的最小价格}
for (let i of prices) {
minPrice = Math.min(minPrice, i);
maxProfit = Math.max(maxProfit, i - minPrice);
}
return maxProfit;
};
股票买卖2
function maxProfit(prices: number[]): number {
//极端情况处理
if (prices === null || prices.length === 1) {
return 0;
}
let benefit = 0;
//只要后一天的价格比前一天高就可以计入总利润
for (let i = 0; i < prices.length - 1; i++) {
let j = i + 1;
if (prices[i] < prices[j]) {
benefit = prices[j] - prices[i] + benefit;
}
}
return benefit;
};
零钱兑换
function coinChange(coins: number[], amount: number): number {
//初始化 amount + 1的数组,使金额amount按照正常数值变化
const dp = new Array(amount + 1).fill(Number.POSITIVE_INFINITY);
dp[0] = 0;//corner case dp[0] 表示金额为0时,与0中符合
for (let currentAmount = 1; currentAmount <= amount; currentAmount++) {
for (let currentCoin of coins) {
//减掉当前硬币值后的剩余总金额leftAmount
const leftAmount = currentAmount - currentCoin;
//剩余金额小于零就不需要计算了,说明凑不出来
if (leftAmount < 0 || dp[leftAmount] === Number.POSITIVE_INFINITY) continue;
//如果剩余金额的dp也凑不出来,说明当前页凑不出来
if (dp[leftAmount] === Number.POSITIVE_INFINITY) continue;
// if (leftAmount < 0 || dp[leftAmount] === Number.POSITIVE_INFINITY) continue;
// dp[leftAmount] + 1;说明能凑出来+1 就是加currentCoin 这个硬币一次
dp[currentAmount] = Math.min(dp[leftAmount] + 1, dp[currentAmount]);
}
}
//有凑不出来的情况所以要拿出来判断下
return dp[amount] === Number.POSITIVE_INFINITY ? -1:dp[amount];
};
零钱兑换2
//动态规划
function change(amount: number, coins: number[]): number {
let dp = new Array(amount + 1).fill(0);
dp[0] = 1;
for (let coin of coins) {
for(let currentAmout = coin; currentAmout <= amount; currentAmout++) {
//剩余总量
const leftAmout = currentAmout - coin;
dp[currentAmout] = dp[currentAmout] + dp[leftAmout];
}
}
return dp[amount];
};
最大子数组和
//动态规划
function maxSubArray(nums: number[]): number {
for (let i = 1; i < nums.length; i++) {
if (nums[i - 1] > 0) {
nums[i] = nums[i] + nums[i - 1];
}
}
return Math.max(...nums);
};
排序数组
- 冒泡
function sortArray(nums: number[]): number[] {
for (let i = 0; i < nums.length - 1; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[i]) {
const temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums;
};
- 插入排序
function sortArray(nums: number[]): number[] {
//从第二个开始排序
for (let i = 1; i < nums.length; i++) {
for (let j = 0; j < i; j++) {
if (nums[j] > nums[i]) {
const temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums;
};
- 快排
function sortArray(nums: number[]): number[] {
quickSort(nums,0, nums.length - 1);
return nums;
};
function quickSort(nums: number[], start: number, end: number) {
if (start >= end) {
return;
}
const pivot = nums[start];
let left = start;
let right = end;
while (left < right) {
while (left < right && nums[right] >= pivot) {
right--;
}
//上面的循环跳出来就说明当前 nums[right] < pivot, 那就交换到左边坑位
if (left < right && nums[right] < pivot) {
nums[left] = nums[right];
}
while (left < right && nums[left] <= pivot) {
left++;
}
//那就交换到右边坑位
if (left < right && nums[left] > pivot) {
nums[right] = nums[left];
}
//结束了pivot插入到中间空位
if (left >= right) {
nums[left] = pivot;
}
}
quickSort(nums, start, left-1);
quickSort(nums, left+1, end);
}
下面这题的题目被勿删了。。。
解法一:利用Map不可重复性,比较map.size和string.lenght
TypeScript
function isUnique(astr: string): boolean {
// const stringArray = astr.split('');
let stringMap = new Map();
for (let i = 0; i < astr.length; i++) {
stringMap.set(astr.substr(i,1), i);
}
if (astr.length === stringMap.size) {
return true;
} else {
return false;
}
};
解法二:遍历判断map是否存在对应的value
TypeScript
function isUnique(astr: string): boolean {
let stringMap = new Map();
for (let i = 0; i < astr.length; i++) {
const p = astr.substr(i,1);
if (stringMap.has(p)) {
return false;
}
stringMap.set(p, i);
}
return true
};
两数之和
解法一:双层遍历相加判断
TypeScript
function twoSum(nums: number[], target: number): number[] {
let p: number[] = [];
for ( let i = 0 ; i < nums.length; i++ ) {
let p1 = nums[i];
for (let j = i + 1; j < nums.length; j++) {
const sum = p1 + nums[j];
if (sum === target) {
p = [i, j];
return p;
}
}
}
return p;
};
解法二:利用hashMap
TypeScript
function twoSum(nums: number[], target: number): number[] {
let myMap = new Map();
//key : num, value: index
myMap.set(nums[0],0);
//从1开始
for (let i = 1; i < nums.length; i++) {
const t = target - nums[i];
if (myMap.has(target - nums[i])) {
return [myMap.get(target - nums[i]), i];
}
myMap.set(nums[i], i);
}
return [];
};
字符翻转
TypeScript
function reserveStr(str: string) {
const tempStrArr = str.split('');
let tmpStr = '';
console.log(tempStrArr);
for (let i = str.length - 1; i >= 0; i--) {
tmpStr = tmpStr + str.substr(i,1);
}
console.log('tmpStr' + tmpStr);
return tmpStr;
}
两数相加 TS
TypeScript
function addTwoNumbers(l1: ListNode | null, l2: ListNode | null): ListNode | null {
let p = 0;
let carry = 0;// 进位
const sum = l1.val + l2.val + carry;
console.log('sum: ' + sum);
p = sum % 10;
console.log('p: ' + p);
carry = Math.floor(sum/10);
const returnNode = new ListNode(p,null);
let tempNode = returnNode;
while (l1.next && l2.next) {
l1 = l1.next;
l2 = l2.next;
const sum = l1.val + l2.val + carry;
console.log('sum: ' + sum);
p = sum % 10;
console.log('p: ' + p);
carry = Math.floor(sum/10);
console.log('carry: ' + carry);
tempNode.next = new ListNode(p,null);
tempNode = tempNode.next;
}
if (l1.next) {// l1.lenght > l2.lenght
while (l1.next) {
l1 = l1.next;
const sum = l1.val + carry;
p = sum % 10;
carry = Math.floor(sum/10);
tempNode.next = new ListNode(p,null);
tempNode = tempNode.next;
}
}
if (l2.next) {// l2.lenght > l1.lenght
while (l2.next) {
l2 = l2.next;
const sum = l2.val + carry;
p = sum % 10;
carry = Math.floor(sum/10);
tempNode.next = new ListNode(p,null);
tempNode = tempNode.next;
}
}
if (carry > 0) {//如果最后相加还有进位,就应该新建这个进位的节点
tempNode.next = new ListNode(carry,null);
}
return returnNode;
};
Stack
export class Stack<T> {
private elements: T[];
size: number;
constructor() {
this.elements = [];
this.size = 0;
}
push(item: T) {
this.elements.push(item);
this.size++;
}
pop() {
if (this.size > 0) {
this.elements.pop();
this.size--;
}
}
top(): T | null {
if (this.size === 0) {
return null;
} else {
return this.elements[this.size - 1];
}
}
}
- 有效括号 时间复杂度 O(n)) 空间复杂度 O(n))
export function isValid(s: string): boolean {
if (s.length === 0) {
return true;
}
const stack = new Stack();
for (let i = 0; i < s.length; i++) {
const p = s.substr(i, 1);
if (p === '(' || p === '{' || p === '[') {
stack.push(p)
} else {
if (p === ')' && stack.top() === '(') {
stack.pop();
continue;
} else if (p === '}' && stack.top() === '{') {
stack.pop();
continue;
} else if (p === ']' && stack.top() === '[') {
stack.pop();
continue;
} else {
stack.push(p);
}
}
}
if (stack.size === 0) {
return true;
} else {
return false;
}
};
Queue
export class Queue<T> {
private elements: T[];
constructor() {
this.elements = [];
}
push(item: T): void {
this.elements.push(item);
}
pop(): T | null {
if (this.elements.length === 0) {
return null;
}
const item = this.elements[0];
this.elements = this.elements.slice(1, this.elements.length);
return item;
}
top(): T | null {
if (this.elements.length === 0) {
return null;
}
const item = this.elements[0];
return item;
}
isEmpty(): boolean {
if (this.elements.length === 0) {
return true;
} else {
return false
}
}
}
export function queueTest() {
const queue = new Queue();
console.log('isEmpty:' + queue.isEmpty()); //isEmpty:true
queue.push(1);
queue.push(2);
console.log('isEmpty:' + queue.isEmpty()); //isEmpty:false
console.log('queueTop:' + queue.top()); //queueTop:1
console.log('queuePop:' + queue.pop()); //queuePop:1
console.log('queuePop:' + queue.pop()); //queuePop:2
console.log('isEmpty:' + queue.isEmpty()); //isEmpty:true
console.log('queuePop:' + queue.pop()); //queuePop:null
}
链表合并
- 暴力 时间复杂度 O(list1.length + list2.length)
空间复杂度 O(1)
function mergeTwoLists(list1: ListNode | null, list2: ListNode | null): ListNode | null {
//使用临时节点
let tempNode = new ListNode(-1);
//最终返回的ListNode
const backNode = tempNode;
while (list1 && list2) {
if (list1.val < list2.val) {
tempNode.next = list1;
list1 = list1.next;
tempNode = tempNode.next;
} else {
tempNode.next = list2;
list2 = list2.next;
tempNode = tempNode.next;
}
}
if (list2) {
tempNode.next = list2
}
if (list1) {
tempNode.next = list1
}
//注意这里返回的是 backNode.next,因为backNode.next才是第一元素的开始
return backNode.next;
};
- 递归 时间复杂度 O(list1.length + list2.length)
空间复杂度 O(list1.length + list2.length)
function mergeTwoLists(list1: ListNode | null, list2: ListNode | null): ListNode | null {
if (list1 === null) {
return list2;
}
if (list2 === null) {
return list1;
}
if (list1.val <= list2.val) {
list1.next = mergeTwoLists(list1.next,list2);
return list1;
} else {
list2.next = mergeTwoLists(list1,list2.next);
return list2;
}
};
罗马数字转整数
时间复杂度 O(n)
空间复杂度 O(1)
function romanToInt(s: string): number {
//先建立map
const vauleMap = new Map();
vauleMap.set('I',1);
vauleMap.set('V',5);
vauleMap.set('X',10);
vauleMap.set('L',50);
vauleMap.set('C',100);
vauleMap.set('D',500);
vauleMap.set('M',1000);
vauleMap.set('IV',4);
vauleMap.set('IX',9);
vauleMap.set('XL',40);
vauleMap.set('XC',90);
vauleMap.set('CD',400);
vauleMap.set('CM',900);
let value: number = 0;
for (let i = 0; i < s.length; i++) {
const p = s.charAt(i);
if (p === 'I' || 'X' || 'C') {
//判断是否到最后了
if (i === s.length - 1) {
value = value + vauleMap.get(p);
} else {
//判断下一个字符是否和当前字符组成复合数字
const nextP = s.substr(i, 2);
if (vauleMap.get(nextP)) {
value = value + vauleMap.get(nextP);
i++; //位移两位
} else {
value = value + vauleMap.get(p);
}
}
} else {
value = value + vauleMap.get(p);
}
}
return value;
};
无重复字符的最长子串
- 暴力解法: 遍历取出子串(O(n^2)),判断是否存在相同子串(O(n)); 时间复杂度 O(n^3)
//是否包含重复字符串
function isContainSameChar(s: string): boolean {
const tempMap = new Map();
for (let i = 0; i < s.length; i++) {
const c = s.charAt(i);
if (tempMap.get(c)) {
return true;
} else {
tempMap.set(c, 1);
}
}
return false;
}
function lengthOfLongestSubstring(s: string): number {
const strMap = new Map();
let maxStr = '';
for (let i = 0; i < s.length; i++) {
for (let j = i + 1 + maxStr.length; j < s.length + 1; j ++) {
const p = s.substring(i,j);
if (p.length > maxStr.length) {
const r = isContainSameChar(p);
console.log('是否有重复字母' + p + ':' + r )
if (!r) {
if (maxStr.length < p.length) {
maxStr = p;
}
}
}
}
}
return maxStr.length;
};
- 滑动窗口: hashmap + 滑动窗口法 时间复杂度 O(n)
function lengthOfLongestSubstring(s: string): number {
const strMap = new Map();
let maxLength = 0;
let i = 0;//滑动窗口右边界
let j = 0;//滑动窗口左边界
for (i; i < s.length; i++) {
//当前元素不在set中 就加入set 然后更新最大长度,i++继续下一轮循环
if (!strMap.has(s[i])) {
strMap.set(s[i], true);
if (maxLength < strMap.size) {
maxLength = strMap.size;
}
} else {
//set中有重复元素不断让j++ 并删除窗口之外的元素 直到滑动窗口内没有重复的元素
while (strMap.has(s[i])) {
strMap.delete(s[j]);
j++;
}
strMap.set(s[i],true);
}
}
return maxLength;
};
寻找两个正序数组的中位数
- 暴力解法 时间复杂度O(m + n)
function findMedianSortedArrays(nums1: number[], nums2: number[]): number {
//合并成一个新的数组
const tArray: number[] = [];
while (nums1.length > 0 || nums2.length > 0) {
if (nums1.length === 0 && nums2.length > 0) {
tArray.push(nums2[0]);
const p = nums2.shift();
console.log('popZ' + p);
}
if (nums2.length === 0 && nums1.length > 0) {
tArray.push(nums1[0]);
const p = nums1.shift();
console.log('popK' + p);
}
if (nums1.length > 0 && nums2.length > 0) {
if (nums1[0] > nums2[0]) {
tArray.push(nums2[0]);
const p = nums2.shift();
console.log('popY' + p);
} else {
tArray.push(nums1[0]);
const p = nums1.shift();
console.log('popX' + p);
}
}
}
console.log(tArray);
//是数组是奇数
if (tArray.length % 2 > 0) {
// 1 2 3 4 5
// 0 1 2 3 4
const index = Math.floor(tArray.length/2);//向下取整
console.log('index' + index);
return tArray[index];
} else {//是偶数
// 1 2 3 4 5 6
// 0 1 2 3 4 5
const p1 = tArray[tArray.length/2 -1];
const p2 = tArray[tArray.length/2];
return (p1 + p2)/2;
}
};
- 二分查找法 。。。
动态规划
青蛙跳台阶
问题分解 0 -> 0
1 -> 1
2 -> 11 2
3 -> 111 12 21
4 -> 1111 22 121 112 211
5 -> 11111 2111 1211 1121 1112 221 212 122
- 时间复杂度 O(n)
- 空间复杂度 O(1);
function levelWays(level: number) : number {
let ways = 0;
if (level === 0) {
return 0;
}
if (level === 1) {
return 1;
}
if (level === 2) {
return 2;
}
const myMap = new Map();
myMap.set(1, 1);
myMap.set(2, 2);
for (let i = 3; i < level + 1; i ++) {
const pWays = myMap.get(i - 1) + myMap.get(i - 2);
myMap.set(i, pWays);
}
return myMap.get(level);
}
console.log(levelWays(1)); // 1
console.log(levelWays(2)); // 2
console.log(levelWays(3)); // 3
console.log(levelWays(4)); // 5
console.log(levelWays(5)); // 8
不同路径
-
时间复杂度 O(m*n)
-
空间复杂度 O(1);
-
使用map实现
function uniquePaths(m: number, n: number): number {
//边界判断
if (m === 0 && n === 0) {
return 0;
}
if (n === 0 && m > 0) {
return 1;
}
if (m === 0 && n > 0) {
return 1;
}
const dpMap = new Map();
dpMap.set('0-0',0);
//某项为0,那么永远只有一种走法
for (let i = 0; i <= n; i++) {
dpMap.set(`0-${i}`,1);
}
for (let i = 0; i <= m; i++) {
dpMap.set(`${i}-0`,1);
}
console.log(dpMap);
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
//最小重复结构
//(x,y) => 上一步要么来自(x-1,y),要么来自(x,y-1)
//即 dpPath[x][y] = dpPath[x-1][y] + dpPath[x][y-1];
console.log(`${i-1}-${j}: ` + dpMap.get(`${i-1}-${j}`));
console.log(`${i}-${j-1}: ` + dpMap.get(`${i}-${j-1}`));
dpMap.set(`${i}-${j}`, dpMap.get(`${i-1}-${j}`) + dpMap.get(`${i}-${j-1}`));
console.log(`${i}-${j}: ` + dpMap.get(`${i}-${j}`));
}
}
return dpMap.get(`${m-1}-${n-1}`);
};
- 使用二维数组实现
function uniquePath(pointX: number, pointY: number): number {
//边界判断
if (pointX === 0 && pointY === 0) {
return 0;
}
if (pointY === 0 && pointX > 0) {
return 1;
}
if (pointX === 0 && pointY > 0) {
return 1;
}
//创建内部全为0的数组
const dpPath = create2DArray(pointX, pointY);
console.log(dpPath);
//边界点
dpPath[0][0] = 0;
//如果在
for (let i = 1; i < pointY; i++) {
dpPath[0][i] = 1;
}
for (let i = 1; i < pointX; i++) {
dpPath[i][0] = 1;
}
console.log(dpPath);
//最小重复结构
//(x,y) => 上一步要么来自(x-1,y),要么来自(x,y-1)
//即 dpPath[x][y] = dpPath[x-1][y] + dpPath[x][y-1];
for (let i = 1; i < pointX; i++) {
for (let j = 1; j < pointY; j++) {
dpPath[i][j] = dpPath[i - 1][j] + dpPath[i][j - 1];
}
}
console.log(dpPath);
return dpPath[pointX - 1][pointY - 1];
}
function create2DArray(bigNum: number, subNum: number) {
const pArray: number[][] = [];
for (let i = 0; i < bigNum; i++) {
const childArray: number[] = [];
for (let j = 0; j < subNum; j++) {
childArray.push(0);
}
pArray.push(childArray);
}
return pArray;
}
最小路径和
复杂度分析
时间复杂度:O(mn)其中 m 和 n 分别是网格的行数和列数。需要对整个网格遍历一次,计算 dp 的每个元素的值。 空间复杂度:O(mn),其中 m 和 n 分别是网格的行数和列数。创建一个二维数组 dp,和网格大小相同。
//最小路径和
function minPathSum(grid: number[][]): number {
//边界情况判断
if (grid === null || grid.length === 0 || grid[0].length === 0) {
return 0;
}
//根据原始数据深拷贝一个新的数创建一个空的二维数组
const dp = deepCopyArray(grid);
//第一个点肯定是
dp[0][0] = grid[0][0];
//最左一列没有最小判断
for (let i = 1;i < grid.length; i++) {
dp[i][0] = dp[i-1][0] + grid[i][0];
}
//最上面一列没有最小判断
for (let j = 1;j < grid[0].length; j++) {
dp[0][j] = dp[0][j-1] + grid[0][j];
}
for (let i = 1;i < grid.length; i++) {
for (let j = 1; j < grid[i].length; j++) {
//当前点位和值来自上一个或者左边一个
dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
}
}
return dp[grid.length - 1][grid[0].length -1];
};
function deepCopyArray(grid:number[][]) {
const temp: number[][] = [];
for (let i of grid) {
const pTemp: number[] = [];
for (let j of i) {
pTemp.push(j);
}
temp.push(pTemp);
}
return temp;
}