字符串(String)之习题分析
)
一、字符串的概念
(一)、字符串的概念
字符串主要用于编程,概念说明、函数解释、用法详述见正文,这里补充一点:字符串在存储上类似字符数组,所以它每一位的单个元素都是可以提取的,如s=“abcdefghij”,则s[1]=“b”,s[9]=“j”,这可以给我们提供很多方便,如高精度运算时每一位都可以转化为数字存入数组。
(二)、String的概念和特点
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间。
//假设a指向地址0x0001
String a = "a";
//重新赋值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的,a 已经指向了其它地址。
a = "b";
因此String的操作都是改变赋值地址而不是改变值操作。
(三)、StringBuffer的概念和特点
StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
//分配长16字节的字符缓冲区
StringBuffer buf=new StringBuffer();
//分配长512字节的字符缓冲区
StringBuffer buf=new StringBuffer(512);
//在缓冲区中存放了字符串,并在后面预留了16字节的空缓冲区。
StringBuffer buf=new StringBuffer("this is a test")
(四)、StringBuilder的概念和特点
StringBuilder( 内部有维护ArrayList )
StringBuffer和StringBuilder类功能基本相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。对于经常要改变值的字符串应该使用StringBuffer和StringBuilder类。
(五)、总结
-
线程安全
StringBuffer 线程安全
StringBuilder 线程不安全 -
速度
一般情况下,速度从快到慢:StringBuilder>StringBuffer>String,这种比较是相对的,不是绝对的。 -
使用选择
(1).如果要操作少量的数据用 = String
(2).单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
(3).多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
以上概念与特点参考《Java核心知识点-String、StringBuilder和StringBuffer的特点》(https://blog.csdn.net/u013782203/article/details/51125517)
二、字符串(String)的常见操作
(一)、基本操作方法
-
获取字符串长度length()
int length = str.length();
-
获取字符串中的第i个字符charAt(i)
//i为字符串的索引号,可得到字符串任意位置处的字符,保存到字符变量中 char ch = str.charAt(i);
-
获取指定位置的字符方法getChars(4个参数)
//i为字符串的索引号,可得到字符串任意位置处的字符,保存到字符变量中 char array[] = new char[80];
(二)、字符串的比较
-
字符串比较也分为两大类:
- 一类是字符串大小的比较,这样的比较有三种结果,大于、等于以及小于;
- 一类比较方法就是比较两个字符串是否相等,这样产生的比较结果无非就两种,ture和false。
-
字符串大小的比较:
-
不忽略字符串大小写比较方法——compareTo(another str)
int result = str1.compareTo(str2);
- 输出三种比较结果:
- 若该字符串的Unicode值<参数字符串的Unicode值,结果返回一负整数;
- 若若该字符串的Unicode值=参数字符串的Unicode值,结果返回0;
- 若该字符串的Unicode值>参数字符串的Unicode值,结果返回一正整数。
- 输出三种比较结果:
-
忽略字符串大小写比较方法——compareTOIgnoreCase(another str)
int result = str1.compareToIgnoreCase(str2);
- 输出三种比较结果:
- 若该字符串的Unicode值<参数字符串的Unicode值,结果返回一负整数;
- 若若该字符串的Unicode值=参数字符串的Unicode值,结果返回0;
- 若该字符串的Unicode值>参数字符串的Unicode值,结果返回一正整数。
- 输出三种比较结果:
-
-
字符串是否相等的比较:
-
不忽略字符串大小写情况下判别字符串相等的方法——eaquals(another str)
boolean result = str1.equals(str2);
- 当且仅当str1和str2的长度相等,且对应位置字符的Unicode编码完全相等,返回true,否则返回false
-
忽略字符串大小写情况下判别字符串相等的方法——equalsIgnoreCase(another str)
boolean result = str1.equals(str2);
-
(三)、字符串与其他数据类型的转换
数据类型 | 字符串转换为其他数据类型的方法 | 其它数据类型转换为字符串的方法1 | 其他数据类型转换为字符串的方法2 |
---|---|---|---|
byte | Byte.parseByte(str) | String.valueOf([byte] bt) | Byte.toString([byte] bt) |
int | Integer.parseInt(str) | String.valueOf([int] i) | Int.toString([int] i) |
long | Long.parseLong(str) | String.valueOf([long] l) | Long.toString([long] l) |
float | Float.parseFloat(str) | String.valueOf([float] f) | Float.toString([float] f) |
double | double.parseDouble(str) | String.valueOf([double] d) | Double.toString([double] b) |
char | str.charAt() | String.valueOf([char] c) | Character.toString([char] c) |
boolean | Boolean.getBoolean(str) | String.valueOf([boolean] b) | Boolean.toString([boolean] b) |
(四)、字符串查找
-
查找字符出现的位置
-
indexOf()方法
str.indexOf(ch); //包含fromIndex位置 str.indexOf(ch,fromIndex);
- 格式1返回指定字符在字符串中第一次出现位置的索引
- 格式2返回指定索引位置之后第一次出现该字符的索引号
-
lastIndexOf()方法
str.lastIndexOf(ch); str.lastIndexOf(ch,fromIndex);
- 格式1返回指定字符在字符串中最后一次出现位置的索引
- 格式2返回指定索引位置之前最后一次出现该字符的索引号
-
-
查找字符串出现的位置
-
indexOf()方法
str.indexOf(str); str.indexOf(str,fromIndex);
- 格式1返回指定子字符串在字符串中第一次出现位置的索引
- 格式2返回指定索引位置之前第一次出现该子字符串的索引号
-
lastIndexOf()方法
str.lastIndexOf(str); str.lastIndexOf(str,fromIndex);
- 格式1返回指定子字符串在字符串中最后一次出现位置的索引
- 格式2返回指定索引位置之前最后一次出现该子字符串的索引号
-
(五)、截取与拆分
-
截取方法
-
substring()方法
String result = str.substring(index); //实际索引号[beginIndex,EndIndex-1] String result = str.substring(beginIndex,EndIndex);
- 截取出范围内的字符串
-
-
拆分方法
-
split()方法
// 拆分的结果保存到字符串数组中 String strArray[] = str.split(正则表达式); String strArray[] = str.split(正则表达式,limit);
-
(六)、替换或修改
-
合并字符串——concat()方法
//将str1和str2合并 String result = str1.concat(str2);
-
将字符全部转化为小写——toLowerCase()方法
String result = str.toLowerCase();
-
将字符全部转化为大写——toUpperCase()方法
String result = str.toUpperCase();
-
需要匹配正则表达式——replaceAll()、replaceFirst()方法
以上内容参考至《Java字符串String类操作方法详细整理》(https://www.cnblogs.com/huxiuqian/p/10167415.html)
三、逆转单词一
(一)、题目需求
给定一个字符串,逐个翻转字符串中的每个单词。
说明:
- 无空格字符构成一个 单词 。
- 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
- 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
示例 1:
输入:"the sky is blue"
输出:"blue is sky the"
示例 2:
输入:" hello world! "
输出:"world! hello"
解释:输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:
输入:"a good example"
输出:"example good a"
解释:如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
示例 4:
输入:s = " Bob Loves Alice "
输出:"Alice Loves Bob"
示例 5:
输入:s = "Alice does not even like bob"
输出:"bob like even not does Alice"
提示:
1 <= s.length <= 104
s
包含英文大小写字母、数字和空格' '
s
中 至少存在一个 单词
进阶:
- 请尝试使用 O(1) 额外空间复杂度的原地解法。
(二)、解法
public String reverseWords(String s) {
if (s == null || s.trim().length() == 0) {
return "";
}
// trim 去除前后空格
String[] words = s.trim().split(" ");
StringBuilder sb = new StringBuilder();
for (int i = words.length - 1; i >= 0; i--) {
if (!"".equals(words[i])) {
sb.append(words[i]).append(" ");
}
}
return sb.toString().trim();
}
(三)、代码分析
1、通过trim方法进行字符串中去除前面空格,留下待逆转单词。同时以split方法进行单词的分割
String[] words = s.trim().split(" ");
2、进行字符串的拼接,由于连续的空格,以空格进行分割,会产生“”,所以需要判断“”情况
for (int i = words.length - 1; i >= 0; i--) {
if (!"".equals(words[i])) {
sb.append(words[i]).append(" ");
}
}
四、逆转单词二
(一)、题目需求
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例:
输入:"Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"
提示:
在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。
(二)、解法
public String reverseWords(String s) {
if (s == null || s.length() == 0) {
return "";
}
String[] words = s.split(" ");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < words.length; i++) {
words[i] = new StringBuilder(words[i]).reverse().toString();
sb.append(words[i]).append(" ");
}
return sb.toString().trim();
}
(三)、代码分析
1、首先使用split方法进行字符串分割
String[] words = s.split(" ");
2、进行单个单词的逆转,以及拼接
for (int i = 0; i < words.length; i++) {
words[i] = new StringBuilder(words[i]).reverse().toString();
sb.append(words[i]).append(" ");
}
五、罗马数字转为整数
(一)、题目需求
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
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 的范围内。
示例 1:
输入: "III"
输出: 3
示例 2:
输入: "IV"
输出: 4
示例 3:
输入: "IX"
输出: 9
示例 4:
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IC 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。
(二)、解法
public int romanToInt(String s) {
int sum = 0;
int preNum = s.charAt(0);
for (int i = 1; i < s.length(); i++) {
int num = getIntValue(s.charAt(i));
if (num > preNum) {
sum -= preNum;
} else {
sum += preNum;
}
preNum = num;
}
sum += preNum;
return sum;
}
public int getIntValue(Character ch) {
switch (ch) {
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;
}
}
(三)、代码分析
1、首先通过getIntValue方法进行罗马字符和整数的转换。
2、由于罗马数字的特点是若小的数字在大的数字前面,则表示大的数字减去小的数字。例如:IV == 5-1 == 4
。而前一个数字与后一个数字相等或大于他,则表示两者的和。例如XX == 20
。所以通过判断每个数字与它后一个数字的大小比较来决定需要相加还是相减。
public int romanToInt(String s) {
int sum = 0;
int preNum = s.charAt(0);
for (int i = 1; i < s.length(); i++) {
int num = getIntValue(s.charAt(i));
if (num > preNum) {
sum -= preNum;
} else {
sum += preNum;
}
preNum = num;
}
sum += preNum;
return sum;
}
六、整数转为罗马数字
(一)、题目需求
罗马数字包含以下七种字符: I
, V
, X
, L
,C
,D
和 M
。
字符 数值
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 的范围内。
示例 1:
输入: 3
输出: "III"
示例 2:
输入: 4
输出: "IV"
示例 3:
输入: 9
输出: "IX"
示例 4:
输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
示例 5:
输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.
(二)、解法
public String intToRoman(int num) {
HashMap<String, Integer> map = new HashMap<>();
String[] one = new String[]{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
String[] ten = new String[]{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
String[] hundred = new String[]{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
String[] thousand = new String[]{"", "M", "MM", "MMM"};
StringBuilder sb = new StringBuilder();
sb.append(thousand[num / 1000]).append(hundred[num % 1000 / 100]).append(ten[num % 100 / 10]).append(one[num % 10]);
return sb.toString();
}
(三)、代码分析
1、首先将每个位数的所有的可能都列举。
String[] one = new String[]{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
String[] ten = new String[]{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
String[] hundred = new String[]{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
String[] thousand = new String[]{"", "M", "MM", "MMM"};
2、通过每个位置的转换,得到它最终数字形式
sb.append(thousand[num / 1000]).append(hundred[num % 1000 / 100]).append(ten[num % 100 / 10]).append(one[num % 10]);