剑指Offer刷题记录
- 1. 斐波那契数列
- 2. 数组——数组中重复的数字
- 3. 数组——构建乘积数组
- 4. 数组——数组中出现次数超过一半的数字
- 5. 数组——二维数组中的查找
- 6. 数组——数字在升序数组中出现的次数
- 7. 栈——用两个栈实现队列
- 8. 数字计算——不用加减乘除做加法
- 9. 贪心算法——跳台阶拓展问题
- 10. 链表——合并两个排序的链表
- 11. 字符串——第一个只出现一次的字符
- 12. 动态规划、分治——连续子数组的最大和
- 13. 树——二叉树的镜像
- 14. 树——二叉树的深度(递归算法)
- 15. 树——平衡二叉树
- 16. 树——二叉搜索树的第k个节点
- 双指针——删除有序数组中重复的元素,并返回非重复数组的个数
- 双指针——和为S的两个数字
- 双指针——顺时针打印矩阵
- 替换空格
1. 斐波那契数列
题目:
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。(这句话很重要!)
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
if (n <= 1) return n;
let temp, pre0 = 0, pre1 = 1;
for (let i = 2; i < n; i++){
temp = pre1;
pre1 = (pre0 + pre1) % 1000000007;
pre0 = temp;
}
return (pre0 + pre1) % 1000000007;
};
2. 数组——数组中重复的数字
题目链接: https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/
/**
* @param {number[]} nums
* @return {number}
*/
var findRepeatNumber = function(nums) {
let arr = []
for (i = 0; i < nums.length; i++){
if (!arr[nums[i]]){
arr[nums[i]] = 1;
} else {
return nums[i];
}
}
return arr;
};
或用集合set来存储数组,若加入新的元素后集合set的长度不变,则加入的是重复的元素。
/**
* @param {number[]} nums
* @return {number}
*/
var findRepeatNumber = function(nums) {
let numset = new Set();
for (let i = 0; i < nums.length; i++){
let setlen = numset.size;
numset.add(nums[i]);
if (setlen == numset.size) {
return nums[i];
}
}
return -1;
};
3. 数组——构建乘积数组
function multiply(array)
{
// write code here
if (!array.length){return []}
let result = [];
let arrlen = array.length;
result[0] = 1;
for (let i = 1; i < arrlen; i++){
result[i] = array[i - 1] * result[i - 1];
}
let temp = 1;
for (let i = arrlen - 2; i >= 0; i--){
temp *= array[i + 1];
result[i] *= temp;
}
return result;
}
module.exports = {
multiply : multiply
};
4. 数组——数组中出现次数超过一半的数字
参考链接:https://blog.csdn.net/m0_37925276/article/details/105950755
使用摩尔投票算法来做,思想是:正负抵消。
function MoreThanHalfNum_Solution(numbers)
{
// write code here
if (!numbers.length) return 0;
//摩尔投票法
var tempo = numbers[0];
var countnum = 1;
let len = numbers.length;
for (let i = 1; i < len; i++){
if (countnum == 0){
tempo = numbers[i];
} else {
if (numbers[i] == tempo){
countnum++;
} else {
countnum--;
}
}
}
//判断是否超过了一半
countnum = 0;
for (let j of numbers){
if (j == tempo){
countnum++;
if (countnum > len / 2)
return j;
}
}
return null;
}
module.exports = {
MoreThanHalfNum_Solution : MoreThanHalfNum_Solution
};
5. 数组——二维数组中的查找
可以从左上角或右下角开始查找,不同的是从左上角开始查找,那就是数值是一直变大的;从右下角开始查找,那么数值就是要逐渐减小。
function Find(target, array)
{
// write code here
if (!array) return false;
let rows = array.length; //行
let cols = array[0].length; //列
col = 0; //列
row = rows - 1; //行
while (( col < cols) && (row >= 0)){
if (array[row][col] < target){
col++;
} else if (array[row][col] > target){
row--;
} else {
return true;
}
}
return false;
}
module.exports = {
Find : Find
};
6. 数组——数字在升序数组中出现的次数
function GetNumberOfK(data, k)
{
// write code here
let count = 0;
for (let i = 0; i < data.length; i++){
if (data[i] == k){
count++;
}
}
return count;
}
module.exports = {
GetNumberOfK : GetNumberOfK
};
7. 栈——用两个栈实现队列
题目链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/
思路:一个栈用来做出队列,另一个栈用来做入队列;要考虑出栈pop()时,两个栈都为空的情况。
var CQueue = function() {
this.StackA = [];
this.StackB = [];
};
/**
* @param {number} value
* @return {void}
*/
CQueue.prototype.appendTail = function(value) {
//入栈
this.StackA.push(value);
};
/**
* @return {number}
*/
CQueue.prototype.deleteHead = function() {
//出栈
if (this.StackB.length){
return this.StackB.pop();
} else {
while (this.StackA.length){
this.StackB.push(this.StackA.pop());
}
if (!this.StackB.length){
return -1;
} else {
return this.StackB.pop();
}
}
};
/**
* Your CQueue object will be instantiated and called as such:
* var obj = new CQueue()
* obj.appendTail(value)
* var param_2 = obj.deleteHead()
*/
8. 数字计算——不用加减乘除做加法
在计算机运算中,有半加器和全加器;
两个二进制相加结果是用一个异或门实现的;
两个二进制进位结果是用一个与门实现的。
(a^b) ^ ((a&b)<<1)
即:每次无进位求 + 每次得到的进位数--------我们需要不断重复这个过程,直到进位数为0为止
口诀: 异或保留,与进位,与为空时就返回。
/**
* @param {number} a
* @param {number} b
* @return {number}
*/
var add = function(a, b) {
let sum = 0, result;
do {
sum = a ^ b;
result = (a&b)<<1;
a = sum;
b = result;
} while (b != 0)
return sum;
};
9. 贪心算法——跳台阶拓展问题
题目链接: https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/
题目描述:
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
为什么需要取模?
根据题目得到的递归关系:
f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n−1)+f(n−2)
f ( 1 ) = 1 f(1)=1 f(1)=1
f ( 0 ) = 1 f(0)=1 f(0)=1
/**
* @param {number} n
* @return {number}
*/
var numWays = function(n) {
if (n == 0 || n == 1) {
return 1;
} else if (n == 2) {
return 2;
}
let sum, a = 1, b = 1;
for (let i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
};
10. 链表——合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
注:链表有val和next两个部分,这是根据定义来的。
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var mergeTwoLists = function(l1, l2) {
if (!l1) return l2;
if (!l2) return l1;
if (l1.val <= l2.val){
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
};
11. 字符串——第一个只出现一次的字符
看到一个很巧妙的解法,如下所示:
若某字符首个出现索引等于反向第一次出现的索引,那么这个字符在该字符串中就只出现了一次。
/**
* @param {string} s
* @return {character}
*/
var firstUniqChar = function(s) {
if (s.length === 0) return ' ';
for (let i = 0; i < s.length; i++) {
if (s.indexOf(s[i]) === s.lastIndexOf(s[i])) {
return s[i];
}
}
return ' ';
};
12. 动态规划、分治——连续子数组的最大和
若求和的值小于0,那么就将其抛弃,取后面一个值作为最大值;
若求和的值大于0,那么就将其加上后一个值,作为最大值。
dp[n-1]>0时:dp[n]=array[i]+dp[n-1];
dp[n-1]<0时:dp[n]=array[i];
function FindGreatestSumOfSubArray(array)
{
// write code here
//动态规划
// let max = array[0];
// for (let i = 0; i < array.length; i++){
// array[i] += array[i-1] > 0 ? array[i-1]:0;
// max = Math.max(max, array[i]);
// }
// return max;
//计算求和
let max = array[0], sum = 0;
for (let i = 0; i < array.length; i++){
sum += array[i];
// if (sum > max) max = sum;
// if (sum < array[0]) sum = 0;
max = sum > max ? sum: max;
if (sum < 0){
sum = 0;
}
}
return max;
}
module.exports = {
FindGreatestSumOfSubArray : FindGreatestSumOfSubArray
};
13. 树——二叉树的镜像
设定一个temp变量,用于左右两边进行交换。
/*
* function TreeNode(x) {
* this.val = x;
* this.left = null;
* this.right = null;
* }
*/
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
function Mirror( pRoot ) {
// write code here
if(pRoot == null) return null;
var temp = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = temp;
Mirror(pRoot.left);
Mirror(pRoot.right);
return pRoot;
}
module.exports = {
Mirror : Mirror
};
14. 树——二叉树的深度(递归算法)
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function TreeDepth(pRoot)
{
// write code here
//递归算法,找到左右两边最大的深度值,再加上root那一层。
if (pRoot == null) return null;
else {
return Math.max(TreeDepth(pRoot.left), TreeDepth(pRoot.right))+1;
}
}
module.exports = {
TreeDepth : TreeDepth
};
15. 树——平衡二叉树
使用递归来查看子节点是否平衡,若平衡则向上继续判断,直到root节点。
在原始函数中写一个子函数,用于获取子节点的高度。
Math.abs
这是绝对值函数,要输入两个值进行相减。
参考链接:https://www.bilibili.com/video/BV1T5411V7V9/
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function IsBalanced_Solution(pRoot)
{
// write code here
function GetHeight(Root)
{
if (Root === null) return 0;
let lh = GetHeight(Root.left);
let rh = GetHeight(Root.right);
return Math.max(lh, rh)+1;
}
if (pRoot == null) return true;
if (!IsBalanced_Solution(pRoot.left)) return false;
if (!IsBalanced_Solution(pRoot.right)) return false;
let lh = GetHeight(pRoot.left);
let rh = GetHeight(pRoot.right);
if (Math.abs(lh - rh) > 1) return false;
return true;
}
module.exports = {
IsBalanced_Solution : IsBalanced_Solution
};
16. 树——二叉搜索树的第k个节点
依旧是使用递归函数编写代码。
二叉搜索树定义:https://blog.csdn.net/rodman177/article/details/89771156
思路: 用中序遍历把二叉搜索树进行排序,并保存到列表中,得到按正向排序的序列,即可得到要抽取的第k个节点。
/* function TreeNode(x) {
this.val = x;
this.left = null;
this.right = null;
} */
function KthNode(pRoot, k)
{
// write code here
//用中序遍历把二叉搜索树进行排序,得到按正向排序的序列,即可得到要抽取的某个节点
let index = 0;
if (!pRoot || k <= 0) return null;
const array = [];
function dfs(root){
if (!root) return null;
dfs(root.left);
array.push(root);
dfs(root.right);
}
dfs(pRoot); //递归深度搜索
return array[k-1];
}
module.exports = {
KthNode : KthNode
};
双指针——删除有序数组中重复的元素,并返回非重复数组的个数
双指针有同向和反向两种不相同的操作,同向可以保持元素的相对位置是一致的,而反向则不能。==
参考链接:https://www.bilibili.com/video/BV1V54y1Q7bd?from=search&seid=11749884154883310997
/**
*
* @param arr int整型一维数组 the array
* @return int整型
*/
function maxLength( arr ) {
// write code here
let i = 0, j = 0, count = 0;
while(j < arr.length){
if (i == 0 || arr[j] != arr[i-1]){
arr[i++] = arr[j++];
count++;
} else {
j++;
}
}
return count;
}
module.exports = {
maxLength : maxLength
};
双指针——和为S的两个数字
题目: 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
根据示例可得,我们可以使用反向双指针来操作,即不需要保持元素相对位置的一致性。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
if (nums == []) return [];
let i = 0, j = nums.length - 1;
while (i <= j){
if (nums[i] + nums[j] == target){
return [nums[i], nums[j]];
} else if (nums[i] + nums[j] > target) {
j--;
} else {
i++;
}
}
return [];
};
双指针——顺时针打印矩阵
参考链接:https://www.bilibili.com/video/BV1J7411671B?from=search&seid=9001370532338198530
注意!!!一定要先判断数组是否存在,需要先对数组进行非空验证!
/**
* @param {number[][]} matrix
* @return {number[]}
*/
var spiralOrder = function(matrix) {
let ans = [];
if (matrix.length == 0 || matrix[0].length == 0) return [];
const columns = matrix[0].length;
const rows = matrix.length;
if (rows == 0) return ans;
let c1 = 0, r1 = 0, c2 = columns - 1, r2 = rows - 1;
let times = Math.min(columns, rows) % 2 == 0? Math.min(columns, rows) / 2: (Math.min(columns, rows) + 1) / 2;
for (let i = 0; i < times; i++){
for (let c = c1; c <= c2; c++) ans.push(matrix[r1][c]);
for (let r = r1 + 1; r <= r2; r++) ans.push(matrix[r][c2]);
if (c1 < c2 && r1 < r2){
for (let c = c2 - 1; c > c1; c--) ans.push(matrix[r2][c]);
for (let r = r2; r > r1; r--) ans.push(matrix[r][c1]);
}
c1++;
c2--;
r1++;
r2--;
}
return ans;
};
替换空格
/**
* @param {string} s
* @return {string}
*/
var replaceSpace = function(s) {
return s.replace(/\s/g, '%20');
};