1.剑指offer
第一题 :
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序, 每一列都按照从上到下递增的顺序排序。
请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
// 思想分析:从四个对角上面开始比较,这里用的是从左下角的值开始比较,
// target大于当前值,就向右移动j++;target小于当前值,就向上移动i--
arr = [
[1, 2, 8, 9],
[2, 4, 9, 12],
[4, 7, 10, 13],
[6, 8, 11, 15]
];
target = 5;
function Find(target, arr) {
// i 行;j 列
for (let i = arr.length - 1, j = 0; i >= 0 && j < arr[0].length;) {
if (arr[i][j] == target) {
return 'true'
}
if (arr[i][j] > target) {
i--;
} else {
j++;
}
}
return 'false'
}
console.log(this.Find(target, arr));
第二题:请实现一个函数,将一个字符串中的每个空格替换成“%20”。
例如,当字符串为We Are Happy. 则经过替换之后的字符串为We%20Are%20Happy。
str = "he llo world";
// 方法一:split、join 在js中字符串自带函数
function replaceSpace(str) {
let arr = str.split(' ');
return arr.join("%20")
}
// 方法二:堆栈 新建一个数组,遇到空的时候压替换值入栈
function replaceSpace(str) {
let result = [];
for (let i = 0; i < str.length; i++) {
if (str[i] == " ") {
result.push("%20");
} else {
result.push(str[i]);
}
}
return result.join('').toString();
}
console.log(replaceSpace(str));
链表
第三题:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
// 注解:这里要注意,这里所说的从尾到头,只需要存链表的值;而不是翻转链表
//这是定义一个链表的方法
function ListNode(x) {
this.val = x;
this.next = null;
}
//思想:将链表的值unshift从头插入另一个数组里面
function printListFromTailToHead(head) {
var ArrayList = [],
pNode = head;
while (pNode != null) {
ArrayList.unshift(pNode.val);
pNode = pNode.next;
}
return ArrayList;
}
2.LeetCode
第19题:给定一个链表,删除链表的倒数第 n个节点,并且返回链表的头结点。
示例: 给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:给定的 n 保证是有效的。
//Definition for singly - linked list.
function ListNode(val) {
this.val = val;
this.next = null;
}
var removeNthFromEnd = function (head, n) {
var fake = new ListNode(0);
fake.next = head;
var length = 0;
var current = head;
//得到长度
while (current != null) {
length++;
current = current.next;
}
// length-n后正好时要删除的那个
length -= n;
var current = fake;
// length-n后正好时要删除的那个的前一个
while (length > 0) {
length--;
current = current.next
}
current.next = current.next.next;
return fake.next
};
力扣 206反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
//Definition for singly - linked list.
function ListNode(val) {
this.val = val;
this.next = null;
}
1.自己写的算法:效率比较低15%,108ms/35.6MB
思想是先用数组存储反向链表的值,然后再从链表头到尾重新赋值
var reverseList = function (head) {
let pNode = head;
let arr = [];
while (pNode != null) {
arr.unshift(pNode.val); //[5,4,3,2,1]
pNode = pNode.next;
}
let first = head;
for (let i = 0; i < arr.length; i++) {
head.val = arr[i];
head = head.next;
}
head = null;
return first
};
2.官方方法一:迭代 86.3% 80ms/34.7MB
假设存在链表 1 → 2 → 3 → Ø,我们想要把它改成 Ø ← 1 ← 2 ← 3。
在遍历列表时,将当前节点的 next 指针改为指向前一个元素。
由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。
在更改引用之前,还需要另一个指针来存储下一个节点。
不要忘记在最后返回新的头引用!
var reverseList = function (head) {
let prev = null;
let current = head;
while (current != null) {
let next = current.next;
current.next = prev;
prev = current;
current = next;
}
return prev
}
3.官方方法二:递归
递归版本稍微复杂一些,其关键在于反向工作。
假设列表的其余部分已经被反转,现在我该如何反转它前面的部分?
假设列表为: n1->… -> n{k-1} -> n{k} -> n{k+1} -> … -> n{m} -> Ø
若从节点 n{k+1}到 n{m}已经被反转,而我们正处于 n{k}。
n{1}-> … -> n{k-1} -> n{k} -> n{k+1} <-… <-n{m}
我们希望 n{k+1} 的下一个节点指向 n{k} 。
所以,n{k}.next.next = n{k}。
要小心的是 n{1}的下一个必须指向 Ø 。
如果你忽略了这一点,你的链表中可能会产生循环。
如果使用大小为 2 的链表测试代码,则可能会捕获此错误。
var reverseList = function (head) {
if (head == null || head.next == null) return head;
var p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
力扣21题 将两个有序链表合并为一个新的有序链表并返回。
新链表是通过拼接给定的两个链表的所有节点组成的。
示例:输入:1->2->4, 1->3->4输出:1->1->2->3->4->4
1.自己写的 136ms/35.4MB 10% 也是官方的迭代方法
var mergeTwoLists = function (l1, l2) {
// 得到head
var head = new ListNode(-1);
var first = head;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
first.next = l1;
l1 = l1.next;
} else {
first.next = l2;
l2 = l2.next;
}
first = first.next;
}
first.next = l1 == null ? l2 : l1;
return head.next
};
2.方法:递归
想法:我们可以如下递归地定义在两个链表里的 merge 操作(忽略边界情况,比如空链表等):
{ list1[0] + merge(list1[1:], list2) & list1[0] < list2[0]
{ list2[0] + merge(list1, list2[1:]) & otherwise
也就是说,两个链表头部较小的一个与剩下元素的 merge 操作结果合并。
算法:我们直接将以上递归过程建模,首先考虑边界情况。
特殊的,如果 l1 或者 l2 一开始就是 null ,那么没有任何操作需要合并,所以我们只需要返回非空链表。
否则,我们要判断 l1 和 l2 哪一个的头元素更小,然后递归地决定下一个添加到结果里的值。
如果两个链表都是空的,那么过程终止,所以递归过程最终一定会终止。
var mergeTwoLists = function (l1, l2) {
if (l1 == null) {
return l2;
} else if (l2 == null) {
return l1;
} else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
笔试题:连字符字符串与驼峰字符串的相互转换
连字符转驼峰:
第一种方法:使用正则匹配函数
let str = 'str-arr-test';
str = str.replace(/(\w*)-(\w*)/g, function ($1, $2, $3) {
//console.log($2, $3);
return $2 + $3[0].toUpperCase() + $3.slice(1);
});
console.log(str);//strArrTest
第二种方法:使用 js 中 split 和 reduce 方法
let str = 'str-arr-test';
str = str.split('-').reduce((acc, val, idx) => idx === 0 ?
acc + val : acc + val[0].toLocaleUpperCase() + val.slice(1), '');
console.log(str);//strArrTest
驼峰转连字符
第一种方法:正则
let str = 'strArrTest';
str = str.replace(/([A-Z])/g, function ($1) {
return '-' + $1.toLocaleLowerCase();
});
console.log(str);//str-arr-test
第二种方法: 使用正则非捕获组来切割数组,然后使用 reduce 遍历所有元素
let str = 'strArrTest';
str = str.split(/(?=[A-Z])/g).reduce((acc, val, idx) => idx === 0 ?
acc + '-' + val : acc + '-' + val[0].toLocaleLowerCase() + val.slice(1));
console.log(str);//str-arr-test