1、最大子序和
题目
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
思路
-
动态规划的是首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 ans
-
如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字
-
如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字
-
每次比较 sum 和 ans的大小,将最大值置为ans,遍历结束返回结果
-
时间复杂度:O(n)O(n)
题解
var 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;
};
2、Excel表列名称
题目
给你一个整数 columnNumber
,返回它在 Excel 表中相对应的列名称。
思路
这是一道从 11 开始的的 2626 进制转换题。
对于一般性的进制转换题目,只需要不断地对 columnNumbercolumnNumber 进行 % 运算取得最后一位,然后对 columnNumbercolumnNumber 进行 / 运算,将已经取得的位数去掉,直到 columnNumbercolumnNumber 为 00 即可。
一般性的进制转换题目无须进行额外操作,是因为我们是在「每一位数值范围在 [0,x)[0,x)」的前提下进行「逢 xx 进一」。
但本题需要我们将从 11 开始,因此在执行「进制转换」操作前,我们需要先对 columnNumbercolumnNumber 执行减一操作,从而实现整体偏移。
题解
var convertToTitle = function(columnNumber) {
let sb = '';
while(columnNumber > 0){
columnNumber --;
sb = sb + String.fromCharCode(columnNumber%26 + 'A'.charCodeAt(0));
columnNumber = Math.floor(columnNumber/26);
}
sb = sb.split('').reverse().join('');
return sb;
};
3、实现Trie(前缀树)
题目
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。void insert(String word)
向前缀树中插入字符串word
。boolean search(String word)
如果字符串word
在前缀树中,返回true
(即,在检索之前已经插入);否则,返回false
。boolean startsWith(String prefix)
如果之前已经插入的字符串word
的前缀之一为prefix
,返回true
;否则,返回false
。
思路
前缀树
(只保存小写字符的)「前缀树」是一种特殊的多叉树,它的 TrieNode 中 chidren 是一个大小为 26 的一维数组,分别对应了26个英文字符 'a' ~ 'z',也就是说形成了一棵 26叉树。
前缀树的结构可以定义为下面这样。 里面存储了三个信息:
value 表示当前的单词就是当前的value值。 children 是该节点的所有子节点。 end 表示当前节点是否是单词的结束。
class Node {
constructor(value){
this.value = value;
this.children = {};
this.end = false;
}
}
构建
在构建前缀树的时候,按照下面的方法:
根节点不保存任何信息; 关键词放到「前缀树」时,需要把它拆成各个字符,每个字符按照其在 'a' ~ 'z' 的序号,放在对应的 chidren 里面。下一个字符是当前字符的子节点。 一个输入字符串构建「前缀树」结束的时候,需要把该节点的 isWord 标记为 true,说明从根节点到当前节点的路径,构成了一个关键词。 下面是一棵「前缀树」,其中保存了 {"am", "an", "as", "b", "c", "cv"} 这些关键词。图中红色表示 isWord 为 true。 看下面这个图的时候需要注意:
所有以相同字符开头的字符串,会聚合到同一个子树上。比如 {"am", "an", "as"} ; 并不一定是到达叶子节点才形成了一个关键词,只要 isWord 为true,那么从根节点到当前节点的路径就是关键词。比如 {"c", "cv"} ;
有些题解把字符画在了节点中,我认为是不准确的。因为前缀树是根据 字符在 children 中的位置确定子树,而不真正在树中存储了 'a' ~ 'z' 这些字符。树中每个节点存储的 isWord,表示从根节点到当前节点的路径是否构成了一个关键词。
查询
在判断一个关键词是否在「前缀树」中时,需要依次遍历该关键词所有字符,在前缀树中找出这条路径。可能出现三种情况:
在寻找路径的过程中,发现到某个位置路径断了。比如在上面的前缀树图中寻找 "d" 或者 "ar" 或者 "any" ,由于树中没有构建对应的节点,那么就查找不到这些关键词; 找到了这条路径,但是最后一个节点的 isWord 为 false。这也说明没有改关键词。比如在上面的前缀树图中寻找 "a" ; 找到了这条路径,并且最后一个节点的 isWord 为 true。这说明前缀树存储了这个关键词,比如上面前缀树图中的 "am" , "cv" 等。 应用 上面说了这么多前缀树,那前缀树有什么用呢?
其实我们生活中就有应用。比如我们常见的电话拨号键盘,当我们输入一些数字的时候,后面会自动提示以我们的输入数字为开头的所有号码。
比如我们的英文输入法,当我们输入半个单词的时候,输入法上面会自动联想和补全后面可能的单词。
题解
class Trie {
constructor(){
this.root = new Node()
}
insert(word){
let currentNode = this.root;
for (const c of word) {
if (!currentNode.children[c]) {
currentNode.children[c] = new Node(c);
}
currentNode = currentNode.children[c];
}
currentNode.end = true;
}
startsWith(word){
let currentNode = this.root;
for(const c of word) {
if(!currentNode.children[c]){
return false;
}
currentNode = currentNode.children[c];
}
return true;
}
search(word){
let currentNode = this.root;
for(const c of word) {
if(!currentNode.children[c]){
return false;
}
currentNode = currentNode.children[c];
}
return currentNode.end;
}
}
4、删除排序列表中的重复元素
题目
存在一个按升序排列的链表,给你这个链表的头节点 head
,请你删除所有重复的元素,使每个元素 只出现一次 。
返回同样按升序排列的结果链表。
思路
- 标签:链表
- 指定 cur 指针指向头部 head
- 当 cur 和 cur.next 的存在为循环结束条件,当二者有一个不存在时说明链表没有去重复的必要了
- 当 cur.val 和 cur.next.val 相等时说明需要去重,则将 cur 的下一个指针指向下一个的下一个,这样就能* 达到去重复的效果
- 如果不相等则 cur 移动到下一个位置继续循环
- 时间复杂度:O(n)O(n)
题解
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var deleteDuplicates = function(head) {
let cur = head;
while(cur!= null && cur.next != null){
if(cur.val === cur.next.val){
cur.next = cur.next.next;
}else{
cur = cur.next
}
}
return head;
};
5、回文对
题目
给定一组 互不相同 的单词, 找出所有 不同** 的索引对 (i, j)
,使得列表中的两个单词, words[i] + words[j]
,可拼接成回文串。
思路
这道题的暴力解非常简单,两层for循环,把字符串拼接一起判断是不是回文即可,一些写C类语言的胖友说自己的暴力拆解会导致超时,答题不通过,但是我用JS写了之后发现还可以,击败了16%的人,官方的其他前缀树和哈希表解法有些烧脑,就暂时先不展示啦
题解
/**
* @param {string[]} words
* @return {number[][]}
*/
var palindromePairs = function(words) {
let result = [];
for(let i = 0;i<words.length;i++){
for(let j = 0;j<words.length;j++){
if(i===j){
continue
}
if(is_palindromePairs(words[i]+words[j])){
result.push([i,j])
}
}
}
return result;
};
const is_palindromePairs = function(s){
for(let i = 0;i<s.length/2;i++){
if(s[i]!= s[s.length-1-i]){
return false
}
}
return true;
}