Java数学算法题-00

数组中出现次数超过一半的数字

给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。

  • 两件事。第一件事找众数。第二件事检查这个数有没有超一半。
  • 我从前往后查 i 个数。这 i 个数中的那个众数,他的数量一定大于其他数出现的次数。可以认为 临时众数 - 其他数出现次数 > 0
  • 如果等于某次等于0了,说明有两个候选的众数了。此时不管,将新的那个数设为临时众数。因为按照题意,不管怎样只会有一个众数存在,如果存在两个众数,一定不会超过数组长度一半。那假设存在一个符合题意的众数。在查到 i 的时候,两个数出现的次数相同,那么在后续的遍历中,肯定会因为次数的原因而改变为正确答案。
  • 可以理解为查找众数的一个算法。
  • 众数找出来了,遍历一下就可以知道出现几次了。
public int MoreThanHalfNum_Solution(int[] nums) {
    int majority = nums[0];
    for (int i = 1, cnt = 1; i < nums.length; i++) {
        cnt = nums[i] == majority ? cnt + 1 : cnt - 1;
        if (cnt == 0) {
            majority = nums[i];
            cnt = 1;
        }
    }
    int cnt = 0;
    for (int val : nums)
        if (val == majority)
            cnt++;
    return cnt > nums.length / 2 ? majority : 0;
}

https://github.com/CyC2018/CS-Notes/blob/master/notes/39.%20%E6%95%B0%E7%BB%84%E4%B8%AD%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%E8%B6%85%E8%BF%87%E4%B8%80%E5%8D%8A%E7%9A%84%E6%95%B0%E5%AD%97.md


圆圈中最后剩下的数

让小朋友们围成一个大圈。然后,随机指定一个数 m,让编号为 0 的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续 0...m-1 报数 .... 这样下去 .... 直到剩下最后一个小朋友,可以不用表演。

  • 经典约瑟夫环。
  • 我们做题的时候,往往会不自觉的注重一道题的过程。比如这道题,我最开始的思路是哪些数死,死到最后,哪个数活下来。但是,这道题的思路则要相反。直接想办法确定哪个数活下来,这个活下来的数,对后面的答案的影响。
  • 2个参数。一个随机数m,一个总人数n。看到两个参数,首先想到的肯定是f(n,m),先不管是什么函数,用的什么方法,肯定存在这个函数。
  • 那接下来就是找这个函数的对应关系或者逻辑。
  • 针对这道题,我的理解是先画图,根据结果,反推逻辑。
    红色是第一个圈删除 , 绿色是第二个圈删除,黑色是第三个圈即以上删除。
    a. m=2


    m=2.png

    b. m=3


    m=3.png
  • 结论是上一个数的答案往后数m个,就是本次的答案。
public int LastRemaining_Solution(int n, int m) {
    if (n == 0)     /* 特殊输入的处理 */
        return -1;
    if (n == 1)     /* 递归返回条件 */
        return 0;
    return (LastRemaining_Solution(n - 1, m) + m) % n;
}

https://github.com/CyC2018/CS-Notes/blob/master/notes/62.%20%E5%9C%86%E5%9C%88%E4%B8%AD%E6%9C%80%E5%90%8E%E5%89%A9%E4%B8%8B%E7%9A%84%E6%95%B0.md


从 1 到 n 整数中 1 出现的次数

输入一个整数 n ,求 1~n 这 n 个整数的十进制表示中 1 出现的次数
例如, 1~13 中包含 1 的数字有 1 、 10 、 11 、 12 、 13 因此共出现 6 次
注意:11 这种情况算两次

  • 先明确思路啊,按位计算1出现的次数,然后加起来。
  • 问题变成每一个位上1出现的次数,怎么计算。
  • 123123,比如计算百位上1出现的次数时,开头的123算往上计算,后面的23算向下计算。
  • 考虑2种情况。
    a. 这个位上的数字为1。
    和123200百位上1出现的次数不同的是,最后一次循环,没有123124-123199。
    在往前的计算过程中,通过前3位123*100,即为1出现的次数。
    在往后计算的过程中。通过最后两位的可能性可知,有00-23的次数,为24次。即最后两位+1。
    b. 这个位上的数字为2-9。
    和123200百位上1出现的次数类似,最后一次循环,包含了123124-123199。
    在往前的计算过程中,前3位123+1,得到124,124*100即为1出现的次数。
    没有必要往后计算。
    c. 这个位上的数字位0。
    假设为123023。
    往前计算,123*100。
    往后计算不了。最后一次循环,达不到1。
  • 分类的计算方式,代码上看一眼就明白了。+8确实很精彩。
public int NumberOf1Between1AndN_Solution(int n) {
    int cnt = 0;
    for (int m = 1; m <= n; m *= 10) {
        int a = n / m, b = n % m;
        cnt += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0);
    }
    return cnt;
}

https://github.com/CyC2018/CS-Notes/blob/master/notes/43.%20%E4%BB%8E%201%20%E5%88%B0%20n%20%E6%95%B4%E6%95%B0%E4%B8%AD%201%20%E5%87%BA%E7%8E%B0%E7%9A%84%E6%AC%A1%E6%95%B0.md


引用仓库:https://github.com/CyC2018/CS-Notes/blob/master/notes/%E5%89%91%E6%8C%87%20Offer%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md

你可能感兴趣的:(Java数学算法题-00)