剑指offer刷题记录
文章目录
一,数组
1. 数组中重复的数字-剑指offer面试3
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
一般关于数组,常有思路先排序再操作,这题也一样.除此之外,还有其他思路如下:
解法1: 哈希表或者新数组装出现过的数字, 空间复杂度O(N),时间复杂度O(N)
/* 时间复杂度O(N), 空间复杂度O(N) */
var findRepeatNumber = function(nums) {
var arr = [];
for (var i = 0; i < nums.length; i++) {
if (arr.indexOf(nums[i]) == -1) {
arr.push(nums[i]);
} else {
console.log(nums[i]);
}
}
}
解法2: 利用题目中的条件:所有数字都在 0~n-1 的范围内, 所以按照下标排序, 比如0就放在nums[0], 这样如果有重复的, 比较nums[i]和nums[i+1]即可
/* 时间复杂度O(N), 空间复杂度O(1) */
var findRepeatNumber = function(nums) {
for (let i = 0; i < nums.length; i++) {
var n = 0;
var temp;
//while一不小心就写出了死循环,加个跳出机制比较好调试
while (i != nums[i]) {
n++;
if (n > 10) {
break;
}
if (nums[nums[i]] == nums[i]) {
return nums[i];
} else {
//因为nums[nums[i]]依赖于nums[i]决定下标,所以必须在nums[i]修改前修改nums[nums[i]]
temp = nums[nums[i]];
nums[nums[i]] = nums[i];
nums[i] = temp;
}
}
}
}
2.二维数组中的查找-剑指offer面试4
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
解法1: 我的解法是基于暴力法的遍历, 不过遍历之前比较了target是否在该行的左端点和右端点之间
/* 时间复杂度最坏为O(n*2),空间复杂度O(1) */
var findNumberIn2DArray = function(matrix, target) {
//注意这里用matrix[0]判断数组为空而不是matrix,因为matrix即使为空,也依然是一个对象,既然是对象,那么判断语句就自动为true,除非不存在,就像[]的matrix[0]不存在.
while (matrix[0]) {
let n = matrix.length;
let m = matrix[0].length;
//比较target是否在左上端点和右下端点里
if (matrix[0][0] <= target && target <= matrix[n - 1][m - 1]) {
for (let y = 0; y < n; y++) {
//按行遍历
if (matrix[y][0] <= target && target <= matrix[y][m - 1]) {
for (let x = 0; x < m; x++) {
if (matrix[y][x] != target) {
continue;
} else {
return true;
}
}
} else {
continue;
}
}
return false;
} else {
return false;
}
}
return 0;
};
解法2: 线性查找.
从右上角开始,如果9>target,说明此列不行,往左查找;找到了<target的值,开始在此列往下查找,如此循环
/* 时间复杂度O(n+m),空间复杂度O(1) */
var findNumberIn2DArray = function(matrix, target) {
if (matrix.length == 0) {
return 0;
}
let n = matrix.length;
let m = matrix[0].length;
//注意这里for循环里两个条件同时满足就不能用逗号隔开,否则代表只有满足其中一个
for (let x = m - 1, y = 0; y < n && x >= 0;) {
if (target == matrix[y][x]) {
return true;
} else if (target < matrix[y][x]) {
x--;
} else if (target > matrix[y][x]) {
y++;
}
}
return false;
};
//应该在for循环之前再补一次比较 左上角<=target<=右下角, 懒得改了
运行结果: 解法1和解法2看起来差不太多,应该是测试用例没有特别大的
二,字符串
1. 替换字符串中的空格-剑指offer05
解法1: String的split()和join()方法实现的
var replaceSpace = function(s) {
return s.split(' ').join('%20');
};
并且这种方法对连续的空格同样适用, 检测如下:
s = "We are happy."//两个空格,那么空格间就是一个空字符串,也会进数组
var replaceSpace = function(s) {
var arr = s.split(' ');
console.log(arr); //["We", "are", "", "happy."]
return arr.join('%20');
};
console.log(replaceSpace(s));
解法2: String的replace方法
var replaceSpace = function(s) {
return s.replace(/\ /g, "%20");//注意全局匹配
};
三, 链表
1. 从尾到头打印链表-剑指offer06
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
输入:head = [1,3,2]
输出:[2,3,1]
虽然链表用数组形式输入,但head其实是指向链表的头节点的指针
//解法1: 遍历链表压入栈,然后弹出栈就获得了倒序
//时间复杂度O(2N),空间复杂度O(2N)
var reversePrint = function(head) {
var arr=[];
var out=[];
var t=0;
while(head !== null){
arr.push(head.val);
head = head.next;
t++;
}
for(var i=0; i<t; i++){
out[i]=arr.pop();
}
return out;
};
使用链表经常涉及到栈和队列方式操作, 所以这里复习一下如何用数组实现栈和队列:
// 数组的栈操作: push从末端添加任意项, pop从末端弹出最后一项
// 数组的队列操作: 结合push末端添加, 和shift前端弹出, 实现队列
// push,unshift返回添加项后的数组, 它们都可以同时添加多项; pop和shift返回弹出的这一项,每次只能弹出一项
//它们都会修改原数组
2. 删除链表节点-剑指offer18
改变 cur->pre 节点的指针地址等于 cur->next, 即可实现删除
var deleteNode = function(head, val) {
if(head==null){
return;
}
if(head.val == val){
return head.next;
}
let ohead = head;
while(head != null){
var p = head;
head = head.next;
if(head.val == val){
p.next=head.next;
return ohead;
}
}
};
3. 链表中倒数第k个节点-剑指offer22
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
//遍历一次,但额外开辟一个空间指针数组, 时空复杂度都是O(N)
var getKthFromEnd = function(head, k) {
var arr=[]; //装链表的每个节点的指针地址值
var i=0;
while(head){
arr[i] = head;
head = head.next;
i++;
}
if(i>=k){
return arr[i-k];
}
return null;
};