剑指offer_10

剑指offer_10(每日5题)来源:牛客网

1. 孩子们的游戏(圆圈中最后剩下的数)

题目:

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

分析:

如果只求最后一个报数胜利者的话,我们可以用数学归纳法解决该问题,为了讨 论方便,先把问题稍微改变一下,并不影响原意:

问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人 继续从0开始报数。求胜利者的编号。

我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新 的约瑟夫环(以编号为k=m%n的人开始):

​ k k+1 k+2 … n-2, n-1, 0, 1, 2, … k-2并且从k开始报0。

现在我们把他们的编号做一下转换:

k --> 0

k+1 --> 1

k+2 --> 2

k-2 --> n-2

k-1 --> n-1

变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解: 例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情 况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x’=(x+k)%n。

参考代码:

//递归写法
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n==0)
            return -1;
        if(n==1)
            return 0;
        else
            return (LastRemaining_Solution(n-1,m)+m)%n;
    }
}


/*
*这道题我用数组来模拟环,思路还是比较简单,但是各种下标要理清
*/
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n<1||m<1) 
            return -1;
        int[] array = new int[n];
        int i = -1,step = 0, count = n;
        while(count>0){   //跳出循环时将最后一个元素也设置为了-1
            i++;          //指向上一个被删除对象的下一个元素。
            if(i>=n) i=0;  //模拟环。
            if(array[i] == -1) 
                continue; //跳过被删除的对象。
            step++;                     //记录已走过的。
            if(step==m) {               //找到待删除的对象。
                array[i]=-1;
                step = 0;
                count--;
            }        
        }
        return i;//返回跳出循环时的i,即最后一个被设置为-1的元素
    }
}


//其它
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n==0) return -1; 
        int s=0; 
        for(int i=2;i<=n;i++){
            s=(s+m)%i; 
        } 
        return s; 
    }
}

2. 求1+2+3+…+n

题目:

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

分析:

1.需利用逻辑与的短路特性实现递归终止。

2.当n==0时,(n>0)&&((sum+=Sum_Solution(n-1))>0)只执行前面的判断,为false,然后直接返回0;

3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。

参考代码:

public class Solution {
    public int Sum_Solution(int n) {
        int sum = n;
        boolean ans = (n>0)&&((sum+=Sum_Solution(n-1))>0);
        return sum;
    }
}


//其它(本质上有乘法?)
public class Solution {
    public int Sum_Solution(int n) {
        int sum = (int) (Math.pow(n,2) + n);
       return sum>>1;
    }
}

3. 不用加减乘除做加法

题目:

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

分析:

首先看十进制是如何做的: 5+7=12,三步走
第一步:相加各位的值,不算进位,得到2。
第二步:计算进位值,得到10. 如果这一步的进位值为0,那么第一步得到的值就是最终结果。
第三步:重复上述两步,只是相加的值变成上述两步的得到的结果2和10,得到12。
同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111 第一步:相加各位的值,不算进位,
得到010,二进制每位相加就相当于各位做异或操作,101^111。
第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。
第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。
``继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果

参考代码:

public class Solution {
    public int Add(int num1,int num2) {
        while (num2!=0) {
            int temp = num1^num2;
            num2 = (num1&num2)<<1;
            num1 = temp;
        }
        return num1;
    }
}

4. 把字符串转换成整数

题目:

将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

输入描述:

输入一个字符串,包括数字字母符号,可以为空

输出描述:

如果是合法的数值表达则返回该数字,否则返回0

参考代码:

public class Solution {
    public static boolean flag;
    public static int StrToInt(String str) {
        flag = false;
        //判断输入是否合法
        if (str == null || str.trim().equals("")) {
            flag = true;
            return 0;
        }
        // symbol=0,说明该数为正数;symbol=1,该数为负数;start用来区分第一位是否为符号位
        int symbol = 0;
        int start = 0;
        char[] chars = str.trim().toCharArray();
        if (chars[0] == '+') {
            start = 1;
        } else if (chars[0] == '-') {
            start = 1;
            symbol = 1;
        }
        int result = 0;
        for (int i = start; i < chars.length; i++) {
            if (chars[i] > '9' || chars[i] < '0') {
                flag = true;
                return 0;
            }
            int sum= result * 10 + (int) (chars[i] - '0');
             
             
            if((sum-(int) (chars[i] - '0'))/10!=result){
                flag=true;
                return 0;
            }
             
            result=result * 10 + (int) (chars[i] - '0');
            /*
             * 本人认为java热门第一判断是否溢出是错误的,举个反例
             * 当输入为value=2147483648时,在计算机内部的表示应该是-2147483648
             * 显然value>Integer.MAX_VALUE是不成立的
            */
        }
        // 注意:java中-1的n次方不能用:(-1)^n .'^'异或运算
        // 注意,当value=-2147483648时,value=-value
        result = (int) Math.pow(-1, symbol) * result;
        return result;
    }
}

5. 数组中重复的数字

题目:

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

参考代码:

/*
不需要额外的数组或者hash table来保存,题目里写了数组里数字的范围保证在0 ~ n-1 之间,所以可以利用现有数组设置标志,当一个数字被访问过后,可以设置对应位上的数 + n,之后再遇到相同的数时,会发现对应位上的数已经大于等于n了,那么直接返回这个数即可。
*/
public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        for(int i=0; i<length; i++){
            int index = numbers[i];
            if(index >= length){
                index -= length;
            }   
            if(numbers[index] >= length){
                duplication[0] = index;
                return true;
            }   
            numbers[index] = numbers[index] + length;
        }   
        return false; 
    }
}


//boolean
public class Solution {
	public boolean duplicate(int numbers[], int length, int[] duplication) {
        boolean[] k = new boolean[length];
        for (int i = 0; i < k.length; i++) {
            if (k[numbers[i]] == true) {
                duplication[0] = numbers[i];
                return true;
            }
            k[numbers[i]] = true;
        }
        return false;
    }
}


//map
public class Solution {
	public boolean duplicate(int numbers[],int length,int [] duplication) {
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int i=0;i<length;i++){
            if(map.containsKey(numbers[i])){
                duplication[0]=numbers[i];
                return true;
            }else{
                map.put(numbers[i],0)
            }
        }
        return false;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值