算法(48)-easy 题目汇总

整理几道常见的 easy 题目。

[toc]

字符串

同构字符串

给定两个字符串 s 和 t ,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

题解

本质是映射的实现。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var isIsomorphic = function(s, t) {
const mapS = {};
const mapT = {};
const lenS = s.length;
const lenT = t.length;
if (lenS !== lenT) {
return false;
}
for (let i = 0; i < lenS; i++) {
const x = s[i];
const y = t[i];
if ((mapS[x] && mapS[x] !== y) || (mapT[y] && mapT[y] !== x)) {
return false;
}
mapS[x] = y;
mapT[y] = x;
}
return true;
};

最长的美好字符子串

题目

当一个字符串 s 包含的每一种字母的大写和小写形式 同时 出现在 s 中,就称这个字符串 s 是 美好 字符串。比方说,”abABB” 是美好字符串,因为 ‘A’ 和 ‘a’ 同时出现了,且 ‘B’ 和 ‘b’ 也同时出现了。然而,”abA” 不是美好字符串因为 ‘b’ 出现了,而 ‘B’ 没有出现。

给你一个字符串 s ,请你返回 s 最长的 美好子字符串 。如果有多个答案,请你返回 最早 出现的一个。如果不存在美好子字符串,请你返回一个空字符串。

题解

本质就是维护大小写两个数组,数组记录每个字符是否出现过,如果两个数组每一位都相同,就是美好字符串。因为字符只有 26 个,所以可以用一个数字的二进制位来替代数组表示是否出现过。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function longestNiceSubstring(s) {
const len = s.length;
let index = -1;
let count = 0;
for (let i = 0; i < len; i++) {
let lower = 0;
let upper = 0;
for (let j = i; j < len; j++) {
const current = s.charAt(j);
if (current >= 'a' && current <= 'z') {
lower |= 1 << (current.charCodeAt(0) - 'a'.charCodeAt(0));
} else {
upper |= 1 << (current.charCodeAt(0) - 'A'.charCodeAt(0));
}
if (lower === upper && j - i + 1 > count) {
index = i;
count = j - i + 1;
}
}
}
return index === -1 ? '' : s.substring(index, index + count);
}

栈与队列

最小栈

题目

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

MinStack() 初始化堆栈对象。

void push(int val) 将元素val推入堆栈。

void pop() 删除堆栈顶部的元素。

int top() 获取堆栈顶部的元素。

int getMin() 获取堆栈中的最小元素。

题解

使用两个栈,一个栈用来存储数据,一个栈用来存储最小值。每次 push 的时候,如果当前值小于等于最小值栈的栈顶值,就将当前值也 push 到最小值栈。pop 的时候也要注意,如果当前值等于最小值栈的栈顶值,就将最小值栈的栈顶值也 pop 出去。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* initialize your data structure here.
*/
const MinStack = function () {
this.a = [];
this.b = [];
};

/**
* @param {number} x
* @return {void}
*/
MinStack.prototype.push = function (x) {
this.a.push(x);
if (!this.b.length || x <= this.b[this.b.length - 1]) {
this.b.push(x);
}
};

/**
* @return {void}
*/
MinStack.prototype.pop = function () {
const x = this.a.pop();
if (x === this.b[this.b.length - 1]) {
this.b.pop();
}
};

/**
* @return {number}
*/
MinStack.prototype.top = function () {
return this.a[this.a.length - 1];
};

/**
* @return {number}
*/
MinStack.prototype.getMin = function () {
return this.b[this.b.length - 1];
};

二叉树

判断二叉树是不是搜索树

给定一个二叉树根节点,请你判断这棵树是不是二叉搜索树。

题解

这题难点反倒在读取,给的是一个数组,用层序遍历的方式,用队列保存结果。然后每次层序遍历,取下两个节点分别就是左右节点,和父节点比较大小即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function check(arr) {
const len = arr.length;
if (len > 0) {
const queue = [arr[0]];
let i = 1;
while (queue.length > 0 && i < len) {
const node = queue.shift();
const left = arr[i];
const right = arr[i + 1];
if (left !== null) {
queue.push(left);
if (left > node) {
return false;
}
}
if (right !== null) {
queue.push(right);
if (right < node) {
return false;
}
}
i += 2;
}
}
return true;
}

贪心

最小硬币个数

题目

假设现在有一堆硬币,其中有足够个1元硬币、足够个2元硬币和足够个5元硬币。现在需要用这些硬币凑出总价值为n元的钱,求最少需要多少枚硬币?

题解

贪心算法,优先使用面值大的硬币。

代码

1
2
3
4
5
6
7
8
9
function minCoins(n) {
let count = 0;
count += Math.floor(n / 5);
n %= 5;
count += Math.floor(n / 2);
n %= 2;
count += n;
return count;
}

暴力法

数字拆分求和

题目

对于给定的正整数k,将其表示为一个正整数序列之和,且该序列中相邻元素的差值为等差分布(等差分布从1开始)。注意,请打印出所有可能的序列(打印多个序列时,按照首个数字从小到大依次打印)。

题解

差值是等差分布,可以得知每次加的数都比原来的数多一个等差数列之和。所以可以从两个数开始遍历,把加的数移到外部计算,每次根据公式算出第一个数,如果是整数就满足条件。之后在根据第一个数计算出剩下的数生成结果。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function find(k) {
let rest = 0;
const res = [];
for(let i = 2; i < k; i++) {
rest = rest + i * (i - 1) / 2;
const first = (k - rest) / i;
if (first <= 0) {
break;
}
if (first % 1 === 0) {
let str = [first];
let temp = 0;
for (let j = 1; j < i; j++) {
temp += j;
str.push(first + temp);
}
res.push(str.join(','));
}
}
return res.reverse().join('\n');
}

买票需要的时间

题目

有 n 个人前来排队买票,其中第 0 人站在队伍 最前方 ,第 (n - 1) 人站在队伍 最后方 。

给你一个下标从 0 开始的整数数组 tickets ,数组长度为 n ,其中第 i 人想要购买的票数为 tickets[i] 。

每个人买票都需要用掉 恰好 1 秒 。一个人 一次只能买一张票 ,如果需要购买更多票,他必须走到 队尾 重新排队(瞬间 发生,不计时间)。如果一个人没有剩下需要买的票,那他将会 离开 队伍。

返回位于位置 k(下标从 0 开始)的人完成买票需要的时间(以秒为单位)。

题解

实际就是每个人减掉目标的票数,单独区分下标排在目标前和排在目标后的情况。

代码

1
2
3
4
5
6
7
8
9
function needTime(tickets, num) {
let result = 0;
const target = tickets[num];
for (let i = 0; i < tickets.length; i++) {
const current = tickets[i];
result += Math.min(current, i <= num ? target : target - 1);
}
return result;
}

无法吃午餐的学生数量

题目

学校的自助午餐提供圆形和方形的三明治,分别用数字 0 和 1 表示。所有学生站在一个队列里,每个学生要么喜欢圆形的要么喜欢方形的。
餐厅里三明治的数量与学生的数量相同。所有三明治都放在一个 栈 里,每一轮:

如果队列最前面的学生 喜欢 栈顶的三明治,那么会 拿走它 并离开队列。

否则,这名学生会 放弃这个三明治 并回到队列的尾部。

这个过程会一直持续到队列里所有学生都不喜欢栈顶的三明治为止。

题解

考虑停止的条件,也就是出现一个三明治所有剩余的学生都不喜欢,所以其实学生的顺序不重要,三明治给任意一个需要的学生都可以。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
function getNum(students, sandwiches) {
let result = students.length;
while (sandwiches.length) {
const s = sandwiches.shift();
const index = students.indexOf(s);
if (index === -1) {
break;
}
result--;
students.splice(index, 1);
}
return result;
}

求最后一块石头的重量

题目

有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;

如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。

最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

题解

直接模拟题目的过程:从后往前遍历,每次取最后两个元素碰撞更新,作为前一个元素的新值。每次遍历都重新排序一下。最后遍历到第一个元素就是结果。

代码

1
2
3
4
5
6
7
function lastStoneWeight(stones) {
for (let i = stones.length - 1; i >= 1; i--) {
stones.sort((a, b) => a - b);
stones[i - 1] = stones[i] - stones[i - 1];
}
return stones[0];
}

最小数

题目

给定一组非0整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最小的整数。

注意:

输入整数数组中,可能存在负数,但最多只会有一个负数

输出结果可能非常小,所以你需要返回一个字符串而不是整数。

题解

本质就是拼接完两个数字再作比较。单独考虑有负数的情况,有负数就变成了移除负数元素后求最大值。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
function min(arr) {
const negativeIndex = arr.findIndex((num) => num < 0);
const negativeValue = arr[negativeIndex] || '';
if (negativeValue) {
arr.splice(negativeIndex, 1);
}
if (negativeIndex === -1) {
arr.sort((a, b) => `${a}${b}` - (`${b}${a}`));
} else {
arr.sort((a, b) => `${b}${a}` - (`${a}${b}`));
}
return `${negativeValue}${arr.join('')}`;
}