突然意识到leetcode是不存自己写的代码的,都是临时存在本地而已,如果自己清理垃圾,估计会直接被清除掉。但自己的一些想法会写在代码里,还是把代码搬了过了。有想过弄去github的,但是感觉这里会方便一点,先弄在这里吧。
以下是9.6-9.13做过的题目
目录
1002. 查找常用字符
242. 有效的字母异位词
1160. 拼写单词
203. 移除链表元素
160. 相交链表
1078. Bigram 分词
234. 回文链表
876. 链表的中间结点
922. 按奇偶排序数组 II
1030. 距离顺序排列矩阵单元格
给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。
你可以按任意顺序返回答案。
示例 1:
输入:["bella","label","roller"]
输出:["e","l","l"]
示例 2:输入:["cool","lock","cook"]
输出:["c","o"]
题解:https://leetcode-cn.com/problems/find-common-characters/solution/js-by-blzbanme/
这题的解法三用了filter,不太懂。filter会返回return true的元素组成的数组,但下面这行代码,实在巧妙,我有点暂时接受不了。我的疑问是,temp[index] = 1这个,也算true的表达??
return index !== -1 ? temp[index] = 1 : false;
/**
* @param {string[]} A
* @return {string[]}
*/
var commonChars = function(A) {
let res = A[0].split("");
for(let i = 1; i < A.length; i++){
let temp = A[i].split("");
res = res.filter(e => {
let index = temp.indexOf(e);
return index !== -1 ? temp[index] = 1 : false;
});
}
return res;
};
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:输入: s = "rat", t = "car"
输出: false
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function(s, t) {
let res = s.split("");
let temp = t.split("");
if(res.length !== temp.length)return false;//这句话还是非常有用的。直接为后面不管是用哈希还是其他都省去不少麻烦
return res.every(e => {
let index = temp.indexOf(e);
return index !== -1? temp[index] = 1 : false;
});
};
上面那个是我自己用的方法,跟1002那道题一样,为什么那道题运行速度还行而这个运行速度直接飙2000ms?
其他人的解法:https://leetcode-cn.com/problems/valid-anagram/solution/you-xiao-de-zi-mu-yi-wei-ci-by-chitanda-eru/
1.是用哈希表,记录次数。如果第一次记录则将次数设为1,若本身已有次数,则在其基础上+1。记录完次数时候,比较s和t的哈希表
let char in对象的话,char表示的是属性。
2.用sort排序完之后一一匹配,但有一个类似的更简便的是:(这个甚至不出现for这一个词。用等号就实现了)牛
var isAnagram = function(s, t) {
if(s.length != t.length) return false;
return s.split('').sort().join('') == t.split('').sort().join('');
}
1.get的用法:返回根据键值检索到的特定的值
2.let newMap = new Map(map);//复制一个map
3.这题可多看
给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我们就认为你掌握了这个单词。
注意:每次拼写时,chars 中的每个字母都只能用一次。
返回词汇表 words 中你掌握的所有单词的 长度之和。
示例 1:
输入:words = ["cat","bt","hat","tree"], chars = "atach"
输出:6
解释:
可以形成字符串 "cat" 和 "hat",所以答案是 3 + 3 = 6。
示例 2:输入:words = ["hello","world","leetcode"], chars = "welldonehoneyr"
输出:10
解释:
可以形成字符串 "hello" 和 "world",所以答案是 5 + 5 = 10。
/**
* @param {string[]} words
* @param {string} chars
* @return {number}
*/
var countCharacters = function(words, chars) {
let map = new Map();
for(let c of chars){
if(map.has(c))
map.set(c, map.get(c)+1);//get:返回根据键值检索到的特定的值
else
map.set(c, 1);
}
let res = 0;
for(let w of words){
let temp = w.split('');//将一个单词拆分成一个个字母储存
if(check(temp,map))
res += temp.length;
}
return res;
function check(word, map){//word是存着单词的数组,map是存着单词和它对应的出现次数的哈希表
let newMap = new Map(map);//复制一个map
let back = true;
for(let w of word){
if(newMap.has(w) && newMap.get(w) > 0)
newMap.set(w, newMap.get(w)-1);
else
back = false;
}
return back;
}
};
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6 输出: 1->2->3->4->5
19.9.11
while中的head && head.next是有顺序的。head要在head.next前面。不然,如果head为空,那head.next就会出错。
我!一开始以为这道题很简单的...没想到后面一直改bug,例子能通过这个就通不过那个,为了结果改代码,改到我自己都不认得了...
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @param {number} val
* @return {ListNode}
*/
// 自己写的,放弃了放弃了
var removeElements = function(head, val) {
let apple = head;
while(head){
if(head.next === null){
if(head.val === val){
head = null;
return head;
}
head = head.next;
}
else{
while(head && head.next){
if(head.next.val === val){
head.next = head.next.next;
}
if(head.next === null)break;
head = head.next; //估计我的代码一开始就从这里错掉了
}
}
}
return apple;
};
//方法一
var removeElements = function(head, val) {
while(head && head.val === val){
head = head.next; //这是将头结点直接挪到下一个节点上去
}
if(!head)return head;
let pre = head;
while(pre && pre.next){//需要加pre && 吗。 还是加吧,没影响的情况下自己能更好理解
if(pre.next.val === val){
pre.next = pre.next.next;
}else{
pre = pre.next;// 首先,不可以去掉原先的else,请看这个栗子[1,2,2,1]2,如果去掉else,在删除完第一个2之后,pre去到了下一个2,它看的是谁?它看的是后面的数了,不再看自己是否要被删掉。
}
}
return head;
}
// 方法二 额外加一个虚拟节点
var removeElements = function(head, val) {
let node = new ListNode(val-1);
node.next = head;
let pre = node;
while(pre && pre.next){
if(pre.next.val === val){
pre.next = pre.next.next;
}else{
pre = pre.next;
}
}
return node.next;
}
// 方法三 递归
var removeElements = function(head, val) {
if(!head)return null;
head.next = removeElements(head.next, val);
if(head.val === val){
return head.next;
}else{
return head;
}
}
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
// 方法一
var getIntersectionNode = function(headA, headB) {
let pA = headA;
let pB = headB;
while(pA !== pB){
pA = pA? pA.next : headB;
pB = pB? pB.next : headA;
}
return pA;
};
// 先遍历headA并打上标记,再遍历headB寻找标记。
var getIntersectionNode = function(headA, headB) {
while(headA){
headA.sep = 1;
headA = headA.next;
}
while(headB){
if(headB.sep) return headB
headB = headB.next;
}
};
给出第一个词 first 和第二个词 second,考虑在某些文本 text 中可能以 "first second third" 形式出现的情况,其中 second 紧随 first 出现,third 紧随 second 出现。
对于每种这样的情况,将第三个词 "third" 添加到答案中,并返回答案。
示例 1:
输入:text = "alice is a good girl she is a good student", first = "a", second = "good"
输出:["girl","student"]
/**
* @param {string} text
* @param {string} first
* @param {string} second
* @return {string[]}
*/
// 为什么我的for循环里面的if进不去?!!!明明和题解一样啊?!!! (过了十几分钟终于知道了...开头的split里没有空格
var findOcurrences = function(text, first, second) {
let textArr = text.split('');
// let arr = [];
let result = [];
// arr.push(first, second);
// console.log(arr);
for(let i = 0; i < textArr.length; i++){
if(textArr[i] == first && textArr[i+1] == second && !!textArr[i+2]){
console.log('hello');//这个没显示、、、
result.push(textArr[i+2]);
console.log(result);
}
}
return result;
};
var findOcurrences = function(text, first, second) {
let textArr = text.split(' ');
// let addArr = []
let result = [];
// addArr.push(first, second)
for(let i = 0; i < textArr.length; i++){
if(textArr[i] == first && textArr[i+1] == second && !!textArr[i+2]){
// console.log('hello');
result.push(textArr[i+2]);
}
}
return result;
};
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2 输出: false示例 2:
输入: 1->2->2->1 输出: true
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function(head) {
if(!head || !head.next)return true;
let mid = head,
pre,//这个初始化加不加 null都没关系
reversed = null;
while(head && head.next){
pre = mid;//这个赋值要在 mid被修改之前
mid = mid.next;
head = head.next.next;
pre.next = reversed;
reversed = pre;
}
if(head)mid = mid.next;//①
while(mid){
if(mid.val !== reversed.val)return false;
mid = mid.next;
reversed = reversed.next;
}
return true;
};
①:这个有点厉害。如果链表是奇数个,那退出while循环时,head指向最后一个。但如果是偶数个时,head会指向null。所以用head的指向来判断链表数是奇数还是偶数。如果是奇数,mid要加1,避开那个对称点
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
//自己的解法...慢的要死 这个还是基于链表的
var middleNode = function(head) {
let apple = head,
length = 0,
result = head,
len;
for(; apple != null; apple = apple.next){
length++;
}
if(length % 2 === 0){
len = length/2;
}else{
len = length/2 - 1; //不知道为什么要-1。因为我是根据运行结果来调试的,-1的时候结果对了
}
for(let i = 0; i < len ; i++){
result = result.next;
}
return result;
};
//解法1:将链表输出到数组,数组的话可以按索引找 ???怎么return是undefined??因为要求的是返回链表,你现在只是把数存进数组了,应该把val和next同时存进数组的,所以用push才正确
var middleNode = function(head) {
let result = [];
for(let i = 0, apple = head; apple != null; i++, apple = apple.next){
// result[i] = apple.val;//这句话错了
result.push(apple);
}
// console.log(result);
// console.log(result.length);
// console.log (result.slice(Math.floor(result.length/2)));
// return Array.from(result);
// console.log(5/2);//2.5
return result[Math.floor(result.length/2)];//注意了!下标直接用result.length/2是不行的,会直接返回undefined,因为目前的测试代码的长度为5,5/2=2.5而下标是没有2.5的,一定要用floor或者trunc方法对数据进行处理
}
// var middleNode = function(head) {
// let A = [head];
// while (A[A.length - 1].next != null)
// A.push(A[A.length - 1].next);
// return A[Math.trunc(A.length / 2)];
// };
// 解法2 快慢指针 68ms
var middleNode = function(head) {
let slow = fast = head;
while(fast && fast.next != null){ //为什么去掉fast 就会报错? 因为一定要检测fast.next是否为空,所以也要检测fast,因为如果fast为空,fast.next是会出错的。
// console.log(slow.val +" "+fast.val);
slow = slow.next;
fast = fast.next.next;//用到fast.next.next,所以要看一下fast.next是否为空,因为如果fast.next为空,fast.next.next是会出错的
}
return slow;
}
给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。
对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;当 A[i] 为偶数时, i 也是偶数。
你可以返回任何满足上述条件的数组作为答案。
示例:
输入:[4,2,5,7]
输出:[4,5,2,7]
解释:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。
第一版:我觉得要用到splice
**官方题解:**(有点类似快排的一部分,用了双链表)
一旦所有偶数都放在了正确的位置上,那么所有奇数也一定都在正确的位子上。所以只需要关注 A[0], A[2], A[4], ... 都正确就可以了。
将数组分成两个部分,分别是偶数部分 even = A[0], A[2], A[4], ... 和奇数部分 odd = A[1], A[3], A[5], ...。定义两个指针 i 和 j, 每次循环都需要保证偶数部分中下标 i 之前的位置全是偶数,奇数部分中下标 j 之前的位置全是奇数。
**算法**
让偶数部分下标 i 之前的所有数都是偶数。为了实现这个目标,把奇数部分作为暂存区,不断增加指向奇数部分的指针,直到找到一个偶数,然后交换指针 i,j 所指的数。
第二版:最外层的for循环好奇怪。写不出不想要的感觉。一写就会多访问数组外的值。
算是初步改好了,还剩数组调换那里,不想再补了。知道怎么补,但是补了之后代码看起来就会很臃肿。算了,跳坑写第三版
第三版:写完了
/**
* @param {number[]} A
* @return {number[]}
*/
// 第一版:自己写的时候卡掉了...
// var sortArrayByParityII = function(A) {
// let odd = [];
// A.forEach((item, index) => {
// if(item % 2 !== 0){
// odd.push(item);
// // A.splice(index,1);
// }
// });
// console.log(A);
// console.log(odd);
// };
// 第二版:只想到快排有一部分相似,但是忽略了它的结束条件。在这道题里,如果按快排的思路,很难写结束条件
// var sortArrayByParityII = function(A) {
// // console.log(A.length);
// // console.log(A.length/2);
// let len = A.length/2;
// let i = 0, j = 1;//忘了快排具体的了,我是要用i做外循环,j做内循环吗?
// for(let x = 0; x < A.length/2; x++){
// while(A[i] % 2 == 0 && A[i+2] !== undefined)i += 2;//突然感觉自己在挖坑然后跳了进去...
// while(A[j] % 2 == 1 && A[j+2] !== undefined)j += 2;
// console.log("i:"+i+" "+"j:"+j);
// [A[i],A[j]]=[A[j],A[i]];//要添加多一些条件,这样就会变得冗杂,失去本来想简洁的初衷了
// console.log(x +":"+A);
// }
// return A;
// }
// 第三版
var sortArrayByParityII = function(A) {
let i = 0,
j = 1;
for(; i < A.length; i += 2){
if(A[i] % 2 == 0)continue;
while(A[j] % 2 == 1)j += 2;
[A[i],A[j]]=[A[j],A[i]];
}
return A;
}
给出 R 行 C 列的矩阵,其中的单元格的整数坐标为 (r, c),满足 0 <= r < R 且 0 <= c < C。
另外,我们在该矩阵中给出了一个坐标为 (r0, c0) 的单元格。
返回矩阵中的所有单元格的坐标,并按到 (r0, c0) 的距离从最小到最大的顺序排,其中,两单元格(r1, c1) 和 (r2, c2) 之间的距离是曼哈顿距离,|r1 - r2| + |c1 - c2|。(你可以按任何满足此条件的顺序返回答案。)
示例 1:
输入:R = 1, C = 2, r0 = 0, c0 = 0
输出:[[0,0],[0,1]]
解释:从 (r0, c0) 到其他单元格的距离为:[0,1]
// 这是参考的代码,比较慢 268ms
var allCellsDistOrder = function(R, C, r0, c0) {
let result = [];
for(let r = 0; r < R; r++){
for(let c = 0; c < C; c++){
let temp = [r,c];
result.push(temp);
}
}
result.sort((a,b) => {
let ta = Math.abs(a[0] - r0) + Math.abs(a[1] - c0),
tb = Math.abs(b[0] - r0) + Math.abs(b[1] - c0);
return ta - tb;
});
return result;
};
笔记
1. result是二维数组吗? 怎么声明 **解决:** result为一维数组,定义一个是坐标的变量,然后
2. temp那个是数组来的,可以用temp[0]和temp[1]访问。同理,sort里面的那个函数参数a和b也可以用方括号访问
3. sort()还是不熟:如果想要参数一在参数二之前就返回一个负数。(高程P92