JAVA入门算法题(十四)

一、最小的淘气值

题目:

/**
 * 老师要给学生安排座位,经统计教室有N个双人桌,M名学生,每个学生有一个淘气值,
 * 要合理安排座位,使得班内同桌之和的淘气值最大值尽可能的小,输出最小值。
 */

 这是一道360Android春招笔试题

要考虑这样的几种情况:

1.座位数>=学生数,那么就每个人做一个桌子,最大的淘气值就是最淘气的那个学生

2.座位数刚好坐满,那么就对淘气值排序,让最大的和最小的坐一起,然后找到和最大的值返回

3.有学生自己坐一桌,那也先对淘气值排序,让最淘气的都自己坐着去,然后其它人较大的和较小的坐一起,返回这些桌子淘气值最大的

这里的排序我用的快速排序,你可以替换为自己的排序方式

    public static void test1(){
        Scanner scanner=new Scanner(System.in);
        int m=scanner.nextInt();
        int n=scanner.nextInt();
        int student[] =new int[m];
        for (int i=0;i<m;i++){
            student[i]=scanner.nextInt();
        }
        int dan=2*n-m;
        System.out.println(dan);
        SortMethods.kuaiSu(student,0,m-1);
        if (n>=m){
            System.out.println(student[m-1]);
        }else {
            for (int i=0;i<(m-dan)/2;i++){
                student[i]+=student[m-1-i-dan];
            }
            SortMethods.kuaiSu(student,0,n-dan);
            System.out.println(student[n-dan]);
        }
    }

二、列名称

题目:

/***
 * 给定一个正整数,返回它在 Excel 表中相对应的列名称。
 *
 * 例如,
 *
 *     1 -> A
 *     2 -> B
 *     3 -> C
 *     ...
 *     26 -> Z
 *     27 -> AA
 *     28 -> AB
 *     ...
 *
 * 示例 1:
 * 输入: 1
 * 输出: "A"
 *
 * 示例 2:
 * 输入: 28
 * 输出: "AB"
 *
 * 示例 3:
 * 输入: 701
 * 输出: "ZY"
 */

 说到底,这是一道进制转换的题,把10进制转换为27进制,不是26,满26不进

这也是为什么要在循环里面减一的原因

    public static String method1(int n){
        StringBuilder sb = new StringBuilder();
        while (n > 0) {
            n--;
            sb.append((char) (n % 26 + 'A'));
            n =n / 26;
        }
        return sb.reverse().toString();
    }

与之相似的题目:

/**
 * 给定一个Excel表格中的列名称,返回其相应的列序号。
 *
 * 例如,
 *
 *     A -> 1
 *     B -> 2
 *     C -> 3
 *     ...
 *     Z -> 26
 *     AA -> 27
 *     AB -> 28
 *     ...
 *
 * 示例 1:
 * 输入: "A"
 * 输出: 1
 *
 * 示例 2:
 * 输入: "AB"
 * 输出: 28
 *
 * 示例 3:
 * 输入: "ZY"
 * 输出: 701
 */

 这道题就是把刚才转换的列名称转换回来

顺着刚才的思路,每次循环加一

    public static int method1(String s) {
        if (s==null||s.equals(""))return 0;
        int length=s.length()-1;
        int n=0;
        int result=0;
        while (length>-1){
            result+=Math.pow(26,n)*(s.charAt(length)-64);
            n++;
            length--;
        }
        return result;
    }

优化代码:

    public static int method2(String s) {
        if (s==null||s.equals(""))return 0;
        int result=0;
        for (int i = 0; i < s.length(); i++) {
            int tmp = (int)Math.pow(26, s.length()-i-1);
            result += (s.charAt(i)-'A'+1) * tmp;
        }
        return result;
    }

再优化:

    public static int method3(String s){
        int res = 0;
        for (int i = s.length() - 1,j=0; i >= 0; --i)
        {
            res += (s.charAt(i) - 'A' + 1)*Math.pow(26, j++) ;
        }
        return res;
    }

三、尾数中零的数量

题目:

/**
 * 给定一个整数 n,返回 n! 结果尾数中零的数量。
 * 示例 1:
 * 输入: 3
 * 输出: 0
 * 解释: 3! = 6, 尾数中没有零。
 * 示例 2:
 * 输入: 5
 * 输出: 1
 * 解释: 5! = 120, 尾数中有 1 个零.
 */

 看到这道题第一时间的想法就是,求出n!,然后不断判断最后一位,除10

事实证明这种不考虑数的上限的做法是完全行不通的

    public static int method1(int n) {
        if (n<1)return 0;
        int number=1,result=0;
        while (n>1){
            number*=n--;
        }
        while (number>10){
            if (number%10==0)result++;
            number/=10;
        }
        return result;
    }

那么就要思考一下如何在尾数中产生0了?

尾数中0的数量就是乘了多少个10嘛,只有乘10或者10的倍数才能在尾数中产生0

10=2*5

然而一串连续的数字中,分解为质因数,2的数量肯定比5多,那么就是找5的数量喽

    public static int method2(int n) {
        int num = 0;
        while (n > 0) {
            n /= 5;
            num += n;
        }
        return num;
    }

四、旋转数组

题目:

/**
 * 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
 *
 * 示例 1:
 * 输入: [1,2,3,4,5,6,7] 和 k = 3
 * 输出: [5,6,7,1,2,3,4]
 * 解释:
 * 向右旋转 1 步: [7,1,2,3,4,5,6]
 * 向右旋转 2 步: [6,7,1,2,3,4,5]
 * 向右旋转 3 步: [5,6,7,1,2,3,4]
 *
 * 示例 2:
 * 输入: [-1,-100,3,99] 和 k = 2
 * 输出: [3,99,-1,-100]
 * 解释:
 * 向右旋转 1 步: [99,-1,-100,3]
 * 向右旋转 2 步: [3,99,-1,-100]
 *
 * 说明:
 *     尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
 *     要求使用空间复杂度为 O(1) 的原地算法。
 */

 主要的限制就是空间复杂度O(1)的原地算法,不然使用俩数组拼接,很简单就能完成

可以用双层for循环,循环k次,每次记住后面要移动到前面来的一个数,然后将该数前方的所有数后移一位

    public static void method1(int[] nums, int k) {
        int swap,pos;
        k=k%nums.length;
        for (int i=1;i<=k;i++){
            pos=nums.length-1-(k-i);
            swap=nums[pos];
            for (int j=pos;j>=i;j--){
                nums[j]=nums[j-1];
            }
            nums[i-1]=swap;
        }
    }

或者整体后移k次,每次记住最后的那个数

    public static void method2(int[] nums,int k){
        int n = nums.length;
        k %= n;
        for (int i = 0; i < k; i++) {
            int temp = nums[n - 1];
            for (int j = n - 1; j > 0; j--) {
                nums[j] = nums[j - 1];
            }
            nums[0] = temp;
        }
    }

加一些技巧:

先将整体反转:把后面的数变道前面了

再把前k个数反转,这样前k个数顺序调整正确了,也把需要调整的位置的数调整了

再把最后的数反转,原因同上

完成

    public static void method3(int[] nums,int k){
        int n = nums.length;
        k %= n;
        reverse(nums, 0, n - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, n - 1);
    }

    private static void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start++] = nums[end];
            nums[end--] = temp;
        }
    }

下面的这种方法大同小异,不太好想

    public static void method4(int[] nums,int k){
        int n = nums.length;
        k %= n;
        // 第一次交换完毕后,前 k 位数字位置正确,后 n-k 位数字中最后 k 位数字顺序错误,继续交换
        for (int start = 0; start < nums.length && k != 0; n -= k, start += k, k %= n) {
            for (int i = 0; i < k; i++) {
                swap(nums, start + i, nums.length - k + i);
            }
        }
    }

    private static void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

使用递归

public static void method5(int[] nums,int k){
        recursiveSwap(nums, k, 0, nums.length);
    }

    private static void recursiveSwap(int[] nums, int k, int start, int length) {
        k %= length;
        if (k != 0) {
            for (int i = 0; i < k; i++) {
                swap(nums, start + i, nums.length - k + i);
            }
            recursiveSwap(nums, k, start + k, length - k);
        }
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幽蓝丶流月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值