牛客最近来了一个新员工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;
}
}
汇编语言中有一种移位指令叫做循环左移(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;
}
}
从扑克牌中随机抽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;
}
}
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;
}
}
求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;
}
}
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
这个题目和上一个题目都是限定了加减乘除运算符,这就要从 位运算 着手考虑问题。由于并不是很熟悉位运算,所以自认为解法是比较难想的。。代码如下:
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;
}
}
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。
这个题目更多还是考察逻辑的缜密性,代码如下:
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;
}
}
在一个长度为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;
}
}
给定一个数组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;
}
}
请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含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;
}
}