算法-数学题

本文介绍了编程中常用的位操作技巧,如n&(n-1)用于清除最低位的1,以及异或操作在找多数元素和数组计数中的应用。此外,还探讨了如何高效实现斐波那契数列、螺旋矩阵遍历、数组中特定数字出现次数的查找以及字符串的左旋转。这些算法和技巧对于提升编程效率和理解计算机底层运作至关重要。
摘要由CSDN通过智能技术生成


1. &:任何数&1都会得到这个数本身
(1)可将某数的某一位与1来获得相应位
(2)n&1等价于n%2是否等于0
(3)n&(n-1)可以把n的最后一个1变为0 即把n中最低位的1变为0 并保持其他位不变
(4)负数x的补码是反码加1 即x=x取反+1 此操作相当于将x所以位取反 但最右边的1除外 所以x与-x只有一个共同点:最右边的1相等 所以x&(-x)将保留最右边的1 并将其他位设为0
2. 异或^:两个数相同为0 不相同为1

50. Pow(x, n)

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。

示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:
输入:x = 2.10000, n = 3
输出:9.26100
示例 3:
输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25

解:例如:
x64=x32*x32
x65=x32*x32*x
所以我们可以先递归地计算出 y = x(n/2),如果n为偶数,那么 xn = y2;如果n为奇数,那么 xn = y2 * x,例如假设要计算x64 先递归计算出x32 此时n=64为偶数 所以答案为x32的平方=x32*x32 若计算x65 先递归计算出x32 此时n=65为奇数 所以答案为x32的平方*x=x32*x32*x
还要记得判断n的正负
注:下面这行语句可以用来判断n是否为奇数 若n的最低为为1 则是奇数 则(n & 1) === 1

boolean odd = (n & 1) === 1;// 判断odd是否是奇数 为true则是

代码如下:
JS:

var myPow = function(x, n) {
    if (n === 1) return x;
    if (n === -1) return 1 / x;
    if (n === 0) return 1;
    let odd = false;
    let smaller = false;
    let m = x;
    let res;
    if (n < 0) {
        n = 0 - n;
        smaller = true;
    }
    if (n % 2 === 1) {//奇数
        odd = true;
        n--;
    }
    let count = n / 2;
    res = myPow(x, count);
    res *= res;
    res = odd ? res * m : res;
    res = smaller ? 1 / res : res;
    return res;
};

54. 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

解:在这里插入图片描述
在这里插入图片描述
按照图中顺时针一层层遍历就行 需要4个指针 分别表示上下左右 用来定位到哪一层
代码如下:
JS:

var spiralOrder = function(matrix) {
    let ans = [];
    let l = 0;
    let r = matrix[0].length;
    let t = 0;
    let b = matrix.length;
    while (l < r && t < b) {
        for (let j = l; j < r; j++) {
            ans.push(matrix[t][j]);
        }
        t++;
        for (let j = t; j < b; j++) {
            ans.push(matrix[j][r - 1]);
        }
        r--;
        if (l >= r || t >= b) break;
        for (let j = r - 1; j >= l; j--) {
            ans.push(matrix[b - 1][j]);
        }
        b--;
        for (let j = b - 1; j >= t; j--) {
            ans.push(matrix[j][l]);
        }
        l++;
    }
    return ans;
};

JAVA

private static List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> list = new LinkedList<>();
        if (matrix == null || matrix.length == 0) return list;
        int left = 0;
        int right = matrix[0].length - 1;
        int top = 0;
        int bottom = matrix.length - 1;
        while (left <= right && top <= bottom){
            for (int i = left; i <= right; i++) {
                list.add(matrix[top][i]);
            }
            top++;
            for (int i = top; i <= bottom; i++) {
                list.add(matrix[i][right]);
            }
            right--;
            if (top > bottom || left > right) break;
            for (int i = right; i >= left ; i--) {
                list.add(matrix[bottom][i]);
            }
            bottom--;
            for (int i = bottom; i >= top; i--) {
                list.add(matrix[i][left]);
            }
            left++;
        }
        return list;
    }

剑指 Offer 10- I. 斐波那契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
解:由于fib的第i项只与第i-1和第i−2项有关,因此只需要初始化三个整形变量 l1,l2,l3,l3代表fib(n)的值 了代表fib(n - 2)的值 l3代表fib(n - 1)的值 只需要推进l1 l2 l3即可
代码如下:
JS:

var fib2 = function(n) {
    if (n === 0) return 0;
    if (n === 1) return 1;
    let l1,l2,l3;
    l1 = 0;
    l2 = 1;
    for (let i = 2; i <= n; i++) {
        l3 = (l1 + l2) % 1000000007;
        l1 = l2;
        l2 = l3;
    }
    return l3;
}

169. 多数元素

剑指 Offer 39. 数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
解:数组排序后返回中间那个数
代码如下:
JS:

var majorityElement = function(nums) {
    nums.sort();
    return nums[Math.floor(nums.length / 2)];
};

191. 位1的个数

剑指 Offer 15. 二进制中1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。

解:n & (n−1)的运算结果为把 n 的二进制位中的最低位的 1 变为 0 之后的结果。这样我们不断让当前的 n 与 n−1 做&运算,直到 n 变为 0。因为每次运算会使得 n 的最低位的 1 被翻转,因此运算次数就等于 n 的二进制位中 1 的个数。
代码如下:
JS:

var hammingWeight = function(n) {
    let ret = 0;
    while (n) {
        n &= n - 1;
        ret++;
    }
    return ret;
};

剑指 Offer 56 - I. 数组中数字出现的次数

一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

解:先对所有数字进行一次异或,得到两个出现一次的数字的异或值。在异或结果中找到任意为 11 的位。根据这一位对所有的数字进行分组。在每个组内进行异或操作,得到两个数字。

剑指 Offer 56 - II. 数组中数字出现的次数 II

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1

解:建立一个长度为 32 的数组counts,记录所有数字的各二进制位的1出现次数。将counts各元素对3求余,则结果为 “只出现一次的数字” 的各二进制位。利用 左移操作 和 或运算 ,可将counts数组中各二进位的值恢复到数字res上
实际上,只需要修改求余数值m,即可实现解决 除了一个数字以外,其余数字都出现m次的通用问题。

剑指 Offer 58 - II. 左旋转字符串

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。

示例 1:
输入: s = “abcdefg”, k = 2
输出: “cdefgab”
示例 2:
输入: s = “lrloseumgh”, k = 6
输出: “umghlrlose”

解:
方法一:使用内部方法进行拼接:

function LeftRotateString(str, n) {
	return str.slice(n) + str.slice(0,n);
}

方法二:遍历添加到新的字符串

var reverseLeftWords = function(s, n) {
    let str = "";
    for (let i = n; i < s.length; i++) {
        str += s.charAt(i);
    }
    for (let i = 0; i < n; i++) {
        str += s.charAt(i);
    }
    return str;
};

遇到【到尾端就要从头开始】的,基本都可以用取余来操作,i是线性增长,通过取余可以让线性增长的数据在模内循环起来
优化:

var reverseLeftWords = function(s, n) {
    let res = "";
    for(let i = n; i < n + s.length; i++) res += s.charAt(i % s.length);
    return res;
};

剑指 Offer 61. 扑克牌中的顺子

从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

示例 1:
输入: [1,2,3,4,5]
输出: True
示例 2:
输入: [0,0,1,2,5]
输出: True

解:
此5张牌是顺子的 充分条件 如下:
(1)除大小王外,所有牌 无重复 ;
(2)设此5张牌中最大的牌为max ,最小的牌为min (大小王除外),则需满足:max - min < 5

var isStraight = function(nums) {
    nums.sort((a, b) => a - b);
    let count = 0;
    for (let i = 0; i < 4; i++) {
        if(nums[i] === 0) {
            count++;
        } else if (nums[i] === nums[i + 1]) return false;
    }
    return nums[4] - nums[count] < 5;
};

用set判断是否重复更高效:

var isStraight = function(nums) {
    /* 
       分治思想 五张牌构成顺子的充分条件需要满足
       1. 不重复 使用Set去重
       2. max - min < 5 最大牌值 减去 最小牌值 小于5 且跳过大小王
    */
    const set = new Set()
    let min = 14, max = 0 // ⚠️ min和max的初始值是两个边界值[0, 13]
    for (const num of nums) {
        // 遇到大小王 跳过
        if (!num) continue
        // 遇到重复则直接 返回false
        if (set.has(num)) return false
        set.add(num)
        // 迭代更新 min和max 以及set
        min = Math.min(min, num)
        max = Math.max(max, num)
    }
    return max - min < 5
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值