力扣 200-299
200.岛屿数量(dfs)
/**
* @param {character[][]} grid
* @return {number}
*/
// count = 0
// 遍历二维数组,如果当前位置为陆地,count++,开始变0操作
// 变0函数(i,j,grid),如果不符合范围或不为0了,直接return
// 否则变为0,且向四个方向变0
const numIslands = (grid) => {
let count = 0;
let m = grid.length;
let n = grid[0].length;
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
//循环网格
if (grid[i][j] === "1") {
//如果为陆地,count++,
count++;
turnZero(i, j, grid);
}
}
}
function turnZero(i, j) {
//沉没四周的陆地
if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] === "0") return; //检查坐标的合法性
grid[i][j] = "0"; //让四周的陆地变为海水, 整个grid作为参数传递
turnZero(i, j + 1);
turnZero(i, j - 1);
turnZero(i + 1, j);
turnZero(i - 1, j);
}
return count;
};
202.快乐数(类似快慢指针,链表环)
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 1,那么这个数就是快乐数。 如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
/**
* @param {number} n
* @return {boolean}
*/
var isHappy = function (n) {
let slow = n;
// 有可能第一步就判断出是快乐数
let fast = getNext(n);
// 无限循环就是环
while (fast !== 1) {
slow = getNext(slow);
fast = getNext(getNext(fast));
if (slow === fast) {
return false;
}
}
return fast === 1;
};
let getNext = function (n) {
let sum = 0;
while (n > 0) {
sum += Math.pow(n % 10, 2);
n = Math.floor(n / 10);
}
return sum;
};
204.计数质数
给定整数 n ,返回 所有小于非负整数 n 的质数的数量 。
/**
* @param {number} n
* @return {number}
*/
var countPrimes = function (n) {
const isPrime = new Array(n).fill(1);
let ans = 0;
for (let i = 2; i < n; ++i) {
if (isPrime[i]) {
ans += 1;
// i=2,可以确定4,6,8不是
// i=3,可以确定9,12,15不是
for (let j = i * i; j < n; j += i) {
// j=i2 i2+i i2+2i
isPrime[j] = 0;
}
}
}
return ans;
};
206.反转链表/翻转链表/链表反转
const reverseList = function (head) {
if (!head || !head.next) {
return head;
}
let curr = head;
let prev = null;
// n 2 3 4 n
// 2 n 3 4 n
// 3 2 n 4 n
// 4 3 2 n n
// n 4 3 2 n
while (curr) {
let next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
};
// 递归法
var reverseList = function (head) {
if (!head || !head.next) {
return head;
}
const newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
};
208.实现 Trie(前缀树)
前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中 ,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false
const Trie = function () {
this.root = new TrieNode();
};
const TrieNode = function () {
// 当前节点的子节点
this.next = {};
// 当前是否是结束节点
this.isEnd = false;
};
// 插入
Trie.prototype.insert = function (word) {
if (!word) return false;
// 每次都从root开始搜索
let node = this.root;
// 遍历word
for (let i = 0; i < word.length; i++) {
// 没有则创建
node.next[word[i]] = node.next[word[i]] || new TrieNode();
// 进入到下一个
node = node.next[word[i]];
}
node.isEnd = true;
return true;
};
// 搜索
Trie.prototype.search = function (word) {
if (!word) return false;
let node = this.root;
for (let i = 0; i < word.length; i++) {
if (node.next[word[i]]) {
node = node.next[word[i]];
} else {
return false;
}
}
// 树里有appleTree,搜索apple应该返回false,因为e不是end
return node.isEnd;
};
// 前缀匹配
Trie.prototype.startsWith = function (prefix) {
if (!prefix) return false;
let node = this.root;
for (let i = 0; i < prefix.length; i++) {
if (node.next[prefix[i]]) {
node = node.next[prefix[i]];
} else {
return false;
}
}
return true;
};
209.长度最小的子数组(滑动窗口)
出该数组中满足其总和大于等于 target 的长度最小的 连续 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
var minSubArrayLen = function (target, nums) {
let l = 0;
let r = 0;
let sum = 0;
let min = Infinity;
sum = 0;
while (l <= r && r < nums.length) {
sum += nums[r];
r++;
while (sum >= target) {
min = Math.min(min, r - l);
sum -= nums[l];
l++;
}
}
return min === Infinity ? 0 : min;
};
213.打家劫舍 II
地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function (nums) {
let n = nums.length;
if (n == 1) {
return nums[0];
}
let dp = new Array(n).fill(0);
dp[0] = nums[0];
// [2,3] 初始值
dp[1] = nums[0] > nums[1] ? nums[0] : nums[1];
// 第一种情况:只遍历1~n-1的房屋,最后一个房屋不遍历
for (let i = 2; i < n - 1; i++) {
dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
}
let max1 = dp[n - 2];
// 第二种情况:只遍历2~n的房屋,第一个房屋不遍历.
dp[1] = nums[1];
dp[2] = nums[1] > nums[2] ? nums[1] : nums[2];
for (let i = 3; i < n; i++) {
dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
}
let max2 = dp[n - 1];
max1 = max1 > max2 ? max1 : max2;
return max1;
};
214.*最短回文串(kmp)
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为 回文串 。找到并返回可以用这种方式转换的最短回文串。
/**
* @param {string} s
* @return {string}
*/
const shortestPalindrome = (s) => {
const rev_s = s.split('').reverse().join('');
const str = s + "#" + rev_s;
const next = new Array(str.length).fill(0);
const kmp = (next, str) => {
next[0] = 0;
let len = 0;
let i = 1;
// 扩展前缀
for (let i = 1, j = 0; i < str.length; i++) {
while (j > 0 && str[i] !== str[j]) {
j = next[j - 1];
}
if (str[i] == str[j]) {
j++;
}
next[i] = j;
}
kmp(next, str);
const maxLen = next[str.length - 1]; // 最长回文前缀的长度
const add = s.substring(maxLen).split('').reverse().join('');
return add + s;
};
215.topK(快排)
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
O(n)
1.构建一个最小堆,并依次把数组的值插入堆中 2.当堆的容量超过 k 时,就删除堆顶 3.插入结束后,堆顶就是第 k 个最大元素
时间复杂度:O(nlogk) 空间复杂度:O(k)
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
// 整个流程就是上浮下沉
var findKthLargest = function (nums, k) {
let heapSize = nums.length;
buildMaxHeap(nums, heapSize); //构建大顶堆 大小为heapSize
//大顶堆 前k-1个堆顶元素不断和数组的末尾元素交换 然后重新heapify堆顶元素
//这个操作就是之前小顶堆出堆顶的操作,只不过现在是原地排序
for (let i = nums.length - 1; i >= nums.length - k + 1; i--) {
[nums[i], nums[0]] = [nums[0], nums[i]];
//交换堆顶和数组末尾元素
--heapSize; //堆大小减1
maxHeapify(nums, 0, heapSize); //重新heapify
}
return nums[0]; //返回堆顶元素,就是第k大的元素
function buildMaxHeap(nums, heapSize) {
for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
//从第一个非叶子节点开始构建
maxHeapify(nums, i, heapSize);
}
}
// 从左向右,自上而下的调整节点
function maxHeapify(heap, i, len) {
let max = i;
let left = 2 * i + 1;
let right = 2 * i + 2;
max = left < len && heap[left] > heap[max] ? left : max;
max = right < len && heap[right] > heap[max] ? right : max;
if (max !== i) {
[heap[i], heap[max]] = [heap[max], heap[i]];
maxHeapify(heap, max, len);
}
}
};
2.死的人
时间复杂度 O(n) 空间复杂度常数
我们仅仅需要在每执行一次快排的时候,比较基准值位置是否在 n-k 位置上,
// 升序,用 n-k 如果小于 n-k ,则第 k 个最大值在基准值的右边,我们只需递归快排基准值右边的子序列即可; 如果大于 n-k ,则第 k 个最大值在基准值的左边,我们只需递归快排基准值左边的子序列即可; 如果等于 n-k ,则第 k 个最大值就是基准值
let findKthLargest = function (nums, k) {
// 因为是升序,所以把k处理成length-k
return quickSelect(nums, nums.length - k);
};
let quickSelect = (arr, k) => {
return quick(arr, 0, arr.length - 1, k);
};
let quick = (arr, left, right, k) => {
let index;
if (left < right) {
// 划分数组
index = partition(arr, left, right);
// Top k, 最后缩到一位时,必然能确定第topk的位置,
// partition后 该位置左边全是小于它的,右边全是大于它的!
if (k === index) {
return arr[index];
} else if (k < index) {
// Top k 在左边
return quick(arr, left, index - 1, k);
} else {
// Top k 在右边
return quick(arr, index + 1, right, k);
}
}
return arr[left];
};
let partition = (arr, l, r) => {
// 取中间项为基准,因为有random,所以加一
const pivot = arr[Math.floor(Math.random() * (r - l + 1)) + l],
// 开始调整
while (l < r) {
// 左指针右移
while (arr[l] < pivot) l++;
// 右指针左移
while (arr[r] > pivot) r--;
if (arr[l] === arr[r] && l < r) {
l++;
}
// 交换
if (l < r) {
[array[l], array[r]] = [array[r], array[l]];
};
}
return l;
};
221.最大正方形(dp,min)
在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。
100 010
/**
* @param {character[][]} matrix
* @return {number}
*/
var maximalSquare = function (matrix) {
// 用ma[i][j]表示以该点为右下角正方形的最大边长
// 且ma[i][j]=min(ma[i-1][j], ma[i][j-1], ma[i-1][j-1]) + 1;
// 只能找min,短板效应确定可以加的
const long = matrix.length;
if (long < 1) {
return 0;
}
const width = matrix[0].length;
let max = 0;
for (let i = 0; i < long; i++) {
for (let j = 0; j < width; j++) {
if (matrix[i][j] === "1") {
matrix[i][j] =
i !== 0 && j !== 0
? Math.min(
matrix[i - 1][j],
matrix[i][j - 1],
matrix[i - 1][j - 1]
) + 1
: 1;
max = Math.max(max, matrix[i][j]);
} else {
matrix[i][j] = 0;
}
}
}
return max * max;
};
226.翻转二叉树(后序)
var invertTree = function (root) {
if (!root) {
return null;
}
const left = invertTree(root.left);
const right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
};