第一题-从尾到头打印链表
题目
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)
思路
1、暴力遍历-遍历链表存入数组,然后再从数组中一个个pop打印
2、递归-直接递归,一直递归到最后,然后push到数组中
代码
1、遍历
var reversePrint = function(head) {
if(head == null) return [];
let arr = [];
while(head != null) {
arr.push(head.val);
head = head.next;
}
let res = [];
while(arr.length != 0) {
res.push(arr.pop());
}
return res;
};
2、递归
let arr = [];
var reversePrint = function(head) {
arr = [];
getArr(head);
return arr;
};
var getArr = function(head) {
if(head == null) return;
getArr(head.next);
arr.push(head.val);
}
第二题-反转链表
题目
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点
思路
1、递归-一直递归到最后两个节点,直接返回节点,然后直接反转
head.next.next = head; head.next = null;;
2、遍历每个节点
- 设置第一个pre节点为null
- 存储当前节点的next
- 把当前节点的next改为pre
- 把pre节点设为当前节点
- 当前节点设置为第二步存储的节点
代码
1、递归
var reverseList = function(head) {
if(head == null || head.next == null) return head;
let res = reverseList(head.next);
head.next.next = head;
head.next = null;
return res;
};
2、遍历
var reverseList = function(head) {
if(head == null || head.next == null) return head;
let newHead = null;
let curr = head;
while(curr != null) {
let next = curr.next;
curr.next = newHead;
newHead = curr;
curr = next;
}
return newHead;
};
第三题-复杂链表的复制
题目
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null
思路-官方思路
1、回溯+哈希
2、迭代+节点拆分
首先将该链表中每一个节点拆分为两个相连的节点,例如对于链表 A→B→CA,我们可以将其拆分为 A→A′→B→B′→C→C′A 。对于任意一个原节点 s,其拷贝节点 S′ 即为其后继节点。
找到每一个拷贝节点 S′ 的随机指针应当指向的节点,即为其原节点 S 的随机指针指向的节点 T 的后继节点 T′
注意原节点的随机指针可能为空,我们需要特别判断这种情况。
完成拷贝节点的随机指针的赋值,将链表按照原节点与拷贝节点的种类进行拆分,只需要遍历一次。
同样需要注意最后一个拷贝节点的后继节点为空,特别判断这种情况
代码
1、回溯+哈希
var copyRandomList = function(head, cachedNode = new Map()) {
if(head == null) return null;
if(!cachedNode.has(head)) {
cachedNode.set(head, {val: head.val});
Object.assign(cachedNode.get(head), {next: copyRandomList(head.next, cachedNode), random: copyRandomList(head.random, cachedNode)})
}
return cachedNode.get(head);
};
2、迭代+节点拆分
var copyRandomList = function(head) {
if(head === null) return null;
for(let i = head; i !== null; i = i.next.next) {
const newNode = new Node(i.val, i.next, null);
i.next = newNode;
}
for(let i = head; i !== null; i = i.next.next) {
const newNode = i.next;
newNode.random = (i.random !== null) ? i.random.next : null;
}
let res = head.next;
for(let i = head; i !== null; i = i.next) {
const newNode = i.next;
i.next = i.next !== null ? i.next.next : null;
newNode.next = (newNode.next !== null) ? newNode.next.next : null;
}
return res;
};
第四题-替换空格
题目
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
思路
不需要什么思路吧,直接字符串替换啊
代码
var replaceSpace = function(s) {
return s.replaceAll(' ', '%20')
};
第五题-左旋转字符串
题目
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"
思路
字符串切割,然后再合一起
代码
var reverseLeftWords = function(s, n) {
const newStr = s.substring(0, n);
const newStr2 = s.substring(n);
return newStr2 + newStr;
};
第六题-数组中重复的数字
题目
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字
思路
思路很多,就先随便写一个吧
使用set,遍历数组,set中已经存在数组元素的话,直接返回数组元素,否则加在数组中
代码
var findRepeatNumber = function(nums) {
let set = new Set();
let res;
for(let i = 0; i < nums.length; i++) {
if(set.has(nums[i])) {
res = nums[i];
break;
}else {
set.add(nums[i])
}
}
return res;
};
第七题-在排序数组中查找数字I
题目
统计一个数字在排序数组中出现的次数。
思路
1、遍历,遍历就不写了
2、二分查找-二分查找数字出现的起始位置和结束位置
代码
var search = function(nums, target) {
if(nums.length == 0) return 0;
if(nums.length == 1) return nums[0] == target ? 1 : 0;
let res = 0;
let left = findIndex(nums, target, true);
let right = findIndex(nums, target, false) - 1;
if(left <= right && nums[left] == target && nums[right] == target && right < nums.length && left >=0) {
res = right - left + 1;
}
return res;
};
var findIndex = function(nums, target, lower) {
let left = 0;
let right = nums.length - 1;
let res = nums.length;
while(left <= right) {
let mid = Math.floor((left + right) / 2);
if(nums[mid] > target || (lower && nums[mid] >= target)) {
right = mid - 1;
res = mid;
}else {
left = mid + 1;
}
}
return res;
}
第八题-无重复字符的最长字串
题目
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度
思路
代码
var lengthOfLongestSubstring = function(s) {
if(s.length == 0 || s.length == 1) return s.length;
let left = 0;
let right = left + 1;
let set = new Set();
set.add(s[left]);
let maxNum = 0, maxTemp = 1;
while(right < s.length && left <= right) {
if(set.has(s[right])) {
set.delete(s[left]);
maxTemp--;
left++;
}else {
set.add(s[right]);
right++;
maxTemp++;
maxNum = Math.max(maxNum, maxTemp);
}
}
return maxNum;
};
第九题-字符串的排列
题目
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串
思路
依旧是滑动窗口
1、滑动窗口(我觉得我是傻叉,为什么不用数组toString进行对比)
- 如果s2的长度小于s1的话,直接返回返回false
- 初始化一个数组,来进行存储s1中每个字母出现的次数
- 遍历s2,遍历一个字母,遍历一个,在第二步初始化的数组进行相应字母的减操作
- 要是减完之后之后小于零,整个窗口右移,第二步的数组再初始化一遍
2、优化-官方思路
代码
1、
var checkInclusion = function(s1, s2) {
if(s2.length < s1.length) return false;
let arr1 = new Array(26).fill(0);
let arr1Copy = new Array(26).fill(0);
for(let i = 0; i < s1.length; i++) {
arr1[s1[i].charCodeAt() - 'a'.charCodeAt()]++;
arr1Copy[s1[i].charCodeAt() - 'a'.charCodeAt()]++;
}
let res = false;
let n1 = s1.length, n2 = s2.length;
let left = 0, right = n1 - 1, i = left;
while(right < n2 && i < right + 1) {
const num = s2[i].charCodeAt() - 'a'.charCodeAt();
arr1[num]--;
if(arr1[num] < 0) {
arr1 = [...arr1Copy];
left++;
right++;
i = left;
res = false;
}else {
res = true;
i++;
}
};
return res;
};
2、
var checkInclusion = function(s1, s2) {
const n = s1.length, m = s2.length;
if (n > m) {
return false;
}
const cnt = new Array(26).fill(0);
for (let i = 0; i < n; ++i) {
--cnt[s1[i].charCodeAt() - 'a'.charCodeAt()];
++cnt[s2[i].charCodeAt() - 'a'.charCodeAt()];
}
let diff = 0;
for (const c of cnt) {
if (c !== 0) {
++diff;
}
}
if (diff == 0) {
return true;
}
for (let i = n; i < m; ++i) {
const x = s2[i].charCodeAt() - 'a'.charCodeAt(), y = s2[i - n].charCodeAt() - 'a'.charCodeAt();
if (x == y) {
continue;
}
if (cnt[x] == 0) {
++diff;
}
++cnt[x];
if (cnt[x] == 0) {
--diff;
}
if (cnt[y] == 0) {
++diff;
}
--cnt[y];
if (cnt[y] == 0) {
--diff;
}
if (diff == 0) {
return true;
}
}
return false;
};