剑指Offer刷题记录(javascript版本)

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(n1)+f(n2)
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两个部分,这是根据定义来的。

参考解答:https://blog.csdn.net/zihuanhansxin/article/details/84145256?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

/**
 * 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');
};

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值