不只为了糊口,还要有抱负。你要想:在这个行业中,我要成为什幺样的人。
一、最大的时间
题目:
给定一个由 4 位数字组成的数组,返回可以设置的符合 24 小时制的最大时间。
最小的 24 小时制时间是 00:00,而最大的是 23:59。从 00:00 (午夜)开始算起,过得越久,时间越大。
以长度为 5 的字符串返回答案。如果不能确定有效时间,则返回空字符串。
示例:
输入:[1,2,3,4]
输出:"23:41"
输入:[5,5,5,5]
输出:""
这道题我有两种思路,一种是把所有组合情况列出来,在列的过程中把不符合条件的去掉,然后找最大的,另一种是找到最大的小时,然后找分钟,找不到就找次大的小时,再找分钟,都找不着就返回空字符串。
把所有情况列出来的问题叫做全排列问题,最简单的全排列实现方式便是for循环
public String largestTimeFromDigits(int[] A) {
StringBuilder stringBuilder = new StringBuilder();
for (int sum = 23; sum >= 0; sum--) {
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < A.length; j++) {
if (i != j && A[i] * 10 + A[j] == sum) {
// 找到小时数后找分钟数
for (int fen = 59; fen >= 0; fen--) {
for (int m = 0; m < A.length; m++) {
for (int n = 0; n < A.length; n++) {
if (m != n && A[m] * 10 + A[n] == fen && m != i && m != j && n != i && n != j) {
stringBuilder.append(A[i]);
stringBuilder.append(A[j]);
stringBuilder.append(':');
stringBuilder.append(A[m]);
stringBuilder.append(A[n]);
return stringBuilder.toString();
}
}
}
}
}
}
}
}
return stringBuilder.toString();
}
那这种写法呢,首先就很low,其次还很累,况且写出来也很难看,我在之前的博客中有写过全排列问题的解法,我们只要在最后去除一下不符合的情况就能得到所有符合条件的时间了。对于怎么找到最大的时间也有两种方案,一种是使用list.sort(),另一种就是自己写for循环了。注意一下过滤时间,小时的最大是23,当第一位为1时第二位的范围是0到4,当第一位为2时,第二位的范围是0到3。下面这种
public static List<String> stringList=new ArrayList<>();
private static String largestTimeFromDigits(int[] A) {
disorder(A,0,A.length);
stringList.sort(new SortByString());
integerList.sort(new SortByInteger());
if (stringList.size()==0)return "";
return stringList.get(stringList.size()-1);
}
static class SortByString implements Comparator {
public int compare(Object o1, Object o2) {
return o1.toString().compareTo(o2.toString());
}
}
public static void disorder(int array[],int m,int n){
if (m==n){
if (array[0]<=2&&array[1]<=4&&array[2]<=6){
if (array[0]*1000+array[1]*100!=2400){
stringList.add(array[0]+""+array[1]+":"+array[2]+""+array[3]);
}
}
return;
}else {
for (int i=m;i<n;i++){
swap(array,m,i);
disorder(array,m+1,n);
swap(array,m,i);
}
}
}
private static void swap(int[] array,int m,int n){
int temp=array[m];
array[m]=array[n];
array[n]=temp;
}
另一种实现方式便是组合小时和分钟,因为组合方式比较少,所以可以直接手动写
static String ans;
public static String largestTimeFromDigits(int[] A) {
ans = "";
check(A[0], A[1], A[2], A[3]);
check(A[0], A[2], A[1], A[3]);
check(A[0], A[3], A[1], A[2]);
check(A[1], A[2], A[0], A[3]);
check(A[1], A[3], A[0], A[2]);
check(A[2], A[3], A[0], A[1]);
return ans;
}
public static void check(int h1, int h2, int m1, int m2) {
String hour = best(h1, h2, 24);
String minute = best(m1, m2, 60);
if (hour.isEmpty() || minute.isEmpty()) return;
String cand = hour + ":" + minute;
if (cand.compareTo(ans) > 0) ans = cand;
}
public static String best(int d1, int d2, int limit) {
int ans = Math.max(10*d1 + d2 < limit ? 10*d1 + d2 : -1,
10*d2 + d1 < limit ? 10*d2 + d1 : -1);
return ans >= 0 ? String.format("%02d", ans) : "";
}
但这种方式我测试了一下运行100次的所需时间,几次平均下来大概在90毫秒左右,我还一种更快的算法。其实和第二种写法差别不大,只是把对字符串的操作变成了对数字的操作,因为几乎在所有的编程语言中对数字的操作永远比字符串快。但要注意的一点就是对于0的处理,这一步差不多增加了15%的运算时间。对于列表求最大值的求解也没啥变动,感觉换成for循环更快一些,感兴趣的试一下。下面方式运行1000次的耗时平均在68毫秒左右。
public static List<Integer> integerList=new ArrayList<>();
private static String largestTimeFromDigits(int[] A) {
disorder(A,0,A.length);
integerList.sort(new SortByInteger());
if (integerList.size()==0)return "";
int number=integerList.get(integerList.size()-1);
String hour="0"+number/100;
String minute="0"+number%100;
return hour.substring(hour.length()-2)+":"+minute.substring(minute.length()-2);
}
static class SortByInteger implements Comparator {
public int compare(Object o1, Object o2) {
int a=(Integer) o1;
int b=(Integer)o2;
return a-b;
}
}
public static void disorder(int array[],int m,int n){
if (m==n){
if (array[0]<=2&&array[1]<=4&&array[2]<=6){
if (array[0]*1000+array[1]*100!=2400){
integerList.add(array[0]*1000+array[1]*100+array[2]*10+array[3]);
}
}
return;
}else {
for (int i=m;i<n;i++){
swap(array,m,i);
disorder(array,m+1,n);
swap(array,m,i);
}
}
}
private static void swap(int[] array,int m,int n){
int temp=array[m];
array[m]=array[n];
array[n]=temp;
}
二、最长公共前缀
题目:
编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。
示例:
输入: ["flower","flow","flight"]
输出: "fl"
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
看到这道题我第一反应就是全排列加排序查找,但后来想了想,不对,想复杂了,他只是让求公共前缀,这里的公共是大家都有,不是其中几个共有,所以只要拿一个和其它所有字符串进行对比就好了。在对比之前还可以进行一些过滤,数组为0的返回空字符串,长度是1的返回本身,数组中有空字符串的直接返回空字符串,这样可以省去很多计算。
public static String longestCommonPrefix(String[] strs) {
if (strs.length == 0) {
return "";
}else if (strs.length==1){
return strs[0];
}
String ret = strs[0];
if (ret.length()==0)return "";
for (int i = 0; i< strs.length-1; i++) {
if (strs[i+1].length()==0)return "";
ret = compare(ret, strs[i+1]);
if (ret.length() == 0) {
return "";
}
}
return ret;
}
public static String compare(String s1, String s2) {
int min = Math.min(s1.length(), s2.length());
for (int i = 0; i< min; i++) {
if (s1.charAt(i) != s2.charAt(i)) {
return s1.substring(0, i);
}
}
return s1.length() == min ? s1 : s2;
}
三、罗马数字转换
题目:
罗马字符对照表
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
规律:
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
示例:
输入: "III"
输出: 3
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
题目很长,意思很简单,就是给你一个罗马数字表示的字符串让你转换成阿拉伯数字
你可能想上来就判断判断判断,其实不用,你找找规律,凡是大数放到小数前面的就加上,大数放到小数后面的就减去,你就从前往后循环字符串取字符判断大小就好了,写个switch把罗马单字符转换成数字。
public static int romanToInt(String s) {
int result=0;
int last=99999;
int current=0;
for (int i=0;i<s.length();i++){
current=singleRomanToInt(s.charAt(i));
if (last<current){
result-=2*last;
result+=current;
}else {
result+=current;
}
last=current;
}
return result;
}
public static int singleRomanToInt(char c){
switch (c) {
case 'I':
return 1;
case 'V':
return 5;
case 'X':
return 10;
case 'L':
return 50;
case 'C':
return 100;
case 'D':
return 500;
case 'M':
return 1000;
default:
return 0;
}
}
四、符合规则的括号
题目:
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例:
输入: "()[]{}"
输出: true
输入: "([)]"
输出: false
这题就比上面那题简单明了,给你一堆括号,你判断一下是不是都有开有合。
给你的字符串可能是这样的“([{}])”,也可能是这样的“{()[]}”,还可能更复杂,对于这种问题最适合的数据结构就是栈了,从字符串不断取值,如果栈顶有和它匹配的就弹出,没有就压入。需要注意的是这题的算法时间是有要求的,它可能会给你一个几千长度的字符串让你判断,如果你要是用循环什么的肯定是来不及了,从字符串取字符也尽量不要用String.toCharArray(),这种方式比String.charAt()慢,因为转换成字符数组是用的System.arraycopy(),而charAt()是直接取值,可能一次比较差不了多少时间,但是一旦字符串长起来就差多了。
public static boolean isValid(String s) {
if (s.length()==0)return true;
if (s.length()%2!=0)return false;
Stack<Character> stack=new Stack<>();
for (int i=0;i<s.length();i++){
char a=s.charAt(i);
if (!stack.empty()){
char b=stack.pop();
if (a!=getTargetChar(b)){
stack.push(b);
stack.push(a);
}
}else {
stack.push(a);
}
}
if (stack.empty()){
return true;
}else {
return false;
}
}
public static char getTargetChar(char a){
switch (a){
case '(':
return ')';
case '[':
return ']';
case '{':
return '}';
}
return 'X';
}