【刷题笔记/剑指Offer】41-50

13 篇文章 0 订阅
13 篇文章 0 订阅

1. 翻转单词顺序列

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

之前做笔试的时候遇到过这题目,当时解法是借助头尾指针识别出非空格块,再将非空格块复制到新数组中,需要申请同样大小的新空间。
书中的解法是这样的:先将字符串反转,然后再从头往后依次识别非空格字符串反转,无需新空间,代码如下:

public class Solution {
    public String ReverseSentence(String str) {
        if(str == null || str.length() == 0 || str.trim().equals(""))
            return str;
        char[] set = str.toCharArray();
        Reverse(set, 0, set.length - 1);

        int start = 0;
        int end = 0;
        while(end < set.length) {
            while(set[start] == ' ')
                start++;
            end = start;
            while(end < set.length && set[end] != ' ')
                end++;
            Reverse(set, start, end - 1);
            start = end;            
        }
        String result = new String(set);
        return result;
    }

    public void Reverse(char[] set, int start, int end) {
        int length = end - start + 1;
        for(int i = 0; i < length / 2; i++) {
            char tmp = set[start + i];
            set[start + i] = set[end - i];
            set[end - i] = tmp;
        }
        return;
    }
}

2. 左旋转字符串

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

需要注意的是:n可能会大于字符串长度,所以要先对n做 n %= length 操作。之后进入正题:确定循环左移n位,发现结果只是原字符串的前n位与后length - n位进行调换,用java就可以很讨巧的用concat来实现,但是需要新申请空间,而且出题人肯定也不是这个本意啦。利用上题的思路:先将前后部分翻转,在将整个字符串反转即可达到目的:

public class Solution {
    public String LeftRotateString(String str,int n) {
        if(str == null || str.length() ==0)
            return str;
        n %= str.length();
        char[] set = str.toCharArray();
        int start1 = 0;
        int end1 = n - 1;
        int start2 = n;
        int end2 = str.length() - 1;

        Reverse(set, start1, end1);
        Reverse(set, start2, end2);
        Reverse(set, start1, end2);

        return new String(set);
    }

    public void Reverse(char[] set, int start, int end) {
        int length = end - start + 1;
        for(int i = 0; i < length / 2; i++) {
            char tmp = set[start + i];
            set[start + i] = set[end - i];
            set[end - i] = tmp;
        }
        return;
    }
}

3. 扑克牌顺子

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

思路:先把输入的5个数字排序,这里调用Arrays类中已有的排序方法,注意要import java.util.Arrays; Arrays.sort(numbers) 即可。排序完成后先获得0的个数,再在后面遍历每相邻两个数之间的大于1的差,最后比较这两个结果来进行判断。代码如下:

import java.util.Arrays;

public class Solution {
    public boolean isContinuous(int [] numbers) {
        if(numbers == null || numbers.length == 0)
            return false;
        Arrays.sort(numbers);
        int count = 0;
        while(numbers[count] == 0)
            count++;
        int sum = 0;
        for(int i = count; i < numbers.length - 1; i++) {
            if(numbers[i] == numbers[i + 1])
                return false;
            sum += numbers[i + 1] - numbers[i] - 1;
        }
        return sum > count ? false : true;
    }
}

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

0, 1, ……, n-1 这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

不得不吐槽一下,牛客网上这个题目的描述太让人费解了,就直接把书中对题目的描述贴了上来。有两种解法,一是创建一个环形链表,模拟这个过程;二是找数字中的规律,得到递归表达式。第二种解法的分析还是去书中看吧,代码如下:

import java.util.ArrayList;

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n < 1 || m < 1)
            return -1;

        int last = 0;
        for(int i = 2; i <= n; i++) {
            last = (last + m) % i;
        }
        return last;
    }
}

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

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

看了书中的解法,大部分适合C++但是不太适合java,但是在网上搜了搜,找到种可以说是脑洞大开的解法,贴上来膜拜一下:

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

6. 不用加减乘除做加法

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

这个题目和上一个题目都是限定了加减乘除运算符,这就要从 位运算 着手考虑问题。由于并不是很熟悉位运算,所以自认为解法是比较难想的。。代码如下:

public class Solution {
    public int Add(int num1,int num2) {
        int sum = 0;
        int carry = 0;
        do {
            sum = num1 ^ num2;
            carry = (num1 & num2) << 1;

            num1 = sum;
            num2 = carry;
        } while(num2 != 0);
        return num1;
    }
}

7. 把字符串转换成整数

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。

这个题目更多还是考察逻辑的缜密性,代码如下:

public class Solution {
    public int StrToInt(String str) {
        char[] tmp = str.toCharArray();
        int sum = 0;
        boolean flag = true;
        for(int i = 0; i < tmp.length; i++) {
            if(tmp[i] >= '0' && tmp[i] <= '9')
                sum = sum * 10 + (tmp[i] - '0');
            else if(tmp[i] == '-')
                flag = false;
            else if(tmp[i] == '+')
                continue;
            else
                return 0;
        }

        return flag ? sum : -sum;
    }
}

8. 数组中重复的数字

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

找题目中的规律,给出每个数字都在 1~n-1 的范围,所以若没有重复,那么每个数字都应该在自己的位置上(排好序后)。代码如下:

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if(numbers == null || length == 0)
            return false;
        for(int i = 0; i < length; i++) {
            if(numbers[i] < 0 || numbers[i] > length - 1)
                return false;
        }
        int index = 0;
        for(; index < length; index++) {
            while(numbers[index] != index) {
                if(numbers[index] == numbers[numbers[index]]) {
                    duplication[0] = numbers[index];
                    return true;
                }
                int tmp = numbers[index];
                numbers[index] = numbers[numbers[index]];
                numbers[tmp] = tmp;
            }
        }

        return false;
    }
}

9. 构建成绩数组

给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]A[i-1]*A[i+1]…*A[n-1]。不能使用除法。

要画出图看起来更方便,主要的思路是要能看出矩阵B可以看为是前后两部分的乘积,再分别求前后两部分就好了。代码如下:

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        if(A == null || A.length == 0)
            return null;

        int n = A.length;

        int[] C = new int[n];
        C[0] = 1;
        for(int i = 1; i < n; i++) {
            C[i] = C[i - 1] * A[i - 1];
        }

        int[] D = new int[n];
        D[n - 1] = 1;
        for(int i = n - 2; i >= 0; i--) {
            D[i] = D[i + 1] * A[i + 1];
        }

        int[] B  = new int[n];
        for(int i = 0; i < n; i++) {
            B[i] = C[i] * D[i];
        }

        return B;
    }
}

10. 正则表达式匹配

请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”ab*ac*a”匹配,但是与”aa.a”和”ab*a”均不匹配

最开始的思路是用while循环判断,但是发现逻辑比较难理清,参考了树中的解法,用递归进行解答。下面贴上别人的代码:

public class Solution {
    public boolean match(char[] str, char[] pattern) {
    if (str == null || pattern == null) {
        return false;
    }
    int strIndex = 0;
    int patternIndex = 0;
    return matchCore(str, strIndex, pattern, patternIndex);
}

public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
    //str到尾,pattern到尾,匹配成功
    if (strIndex == str.length && patternIndex == pattern.length) {
        return true;
    }
    //str未到尾,pattern到尾,匹配失败
    if (strIndex != str.length && patternIndex == pattern.length) {
        return false;
    }
    //str到尾,pattern未到尾(不一定匹配失败,因为a*可以匹配0个字符)
    if (strIndex == str.length && patternIndex != pattern.length) {
        //只有pattern剩下的部分类似a*b*c*的形式,才匹配成功
        if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
            return matchCore(str, strIndex, pattern, patternIndex + 2);
        }
        return false;
    }

    //str未到尾,pattern未到尾
    if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
        if (pattern[patternIndex] == str[strIndex] || (pattern[patternIndex] == '.' && strIndex != str.length)) {
            return matchCore(str, strIndex, pattern, patternIndex + 2)//*匹配0个,跳过
                    || matchCore(str, strIndex + 1, pattern, patternIndex + 2)//*匹配1个,跳过
                    || matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个
        } else {
            //直接跳过*(*匹配到0个)
            return matchCore(str, strIndex, pattern, patternIndex + 2);
        }
    }

    if (pattern[patternIndex] == str[strIndex] || (pattern[patternIndex] == '.' && strIndex != str.length)) {
        return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
    }

    return false;
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值