异或运算也可以理解为无进位相加:相同为0,不同为1
0^N = N N^N=0
异或运算满足交换律和结合律
a^b = b^a (a^b)^c = a^(b^c)
同样一批数,异或结果是一样的
交换两个变量的值
假设: int a = 甲 ,int b = 乙
a = a^b; 此时: a = 甲^乙 b=乙
b = a^b; 此时: a = 甲^乙 b=甲^乙^乙=甲
a = a^b; 此时: a = 甲^乙^甲=乙 b=甲
前提是a和b在内存中是两个独立的区域,也就是a和b不能同时指向相同的地址,例如不能自己和自己交换,因为自己和自己异或结果为0.
LeetCode链接 344. 反转字符串 - 力扣(LeetCode)
package daimasuixiangshuati.day08_zifuchuan;
/**
* @Author LeiGe
* @Date 2023/10/14
* @Description todo
*/
public class FanZhuanZiFuChuan344_2 {
/**
* 方法1-双指针
* 直接将数组头尾的元素交换位置即可,直到left=right
*
* @param s
*/
public void reverseString(char[] s) {
int left = 0;
int right = s.length - 1;
char temp;
while (left < right) {
temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
}
时间复杂度:O(N)
空间复杂度:O(1)
package daimasuixiangshuati.day08_zifuchuan;
/**
* @Author LeiGe
* @Date 2023/10/14
* @Description todo
*/
public class FanZhuanZiFuChuan344_3 {
/**
* 方法2-方法1的优化:交换使用位运算
*
* @param s
*/
public void reverseString(char[] s) {
int left = 0;
int right = s.length - 1;
while (left < right) {
//构造a^b的结果,并放入到a中
s[left] ^= s[right];
//s[left] = (char) (s[left] ^ s[right]);
//将a^b这一结果再^b,存放到b中,此时b=a,a=a^b
s[right] ^= s[left];
//s[right] = (char) (s[right] ^ s[left]);
//a^b的结果再^a,存入a中,此时b=a,a=b,交换完成
s[left] ^= s[right];
//s[right] = (char) (s[left] ^ s[right]);
left++;
right--;
}
}
}
LeetCode链接 541. 反转字符串 II - 力扣(LeetCode)
package daimasuixiangshuati.day08_zifuchuan;
/**
* @Author LeiGe
* @Date 2023/10/22
* @Description todo
*/
public class FanZhuanZiFuChuanII541_2 {
/**
* 方法1-模拟,每遇到2k个字符,就翻转这2k个字符中的前k个
*
* @param s
* @param k
* @return
*/
public String reverseStr(String s, int k) {
char[] chars = s.toCharArray();
int n = chars.length;
for (int i = 0; i < n; i += 2 * k) {
// 如果剩余字符少于k个,将剩余字符全部翻转
if ((i + k) > n) {
reverseStrK(chars, i, n - 1);
}
// 如果剩余字符大于k个,翻转k个字符
else {
reverseStrK(chars, i, i + k - 1);
}
}
return new String(chars);
}
/**
* 反转前start-end之间的字符串:双指针,左闭右闭
*
* @param s
* @param start
* @param end
*/
private void reverseStrK(char[] s, int start, int end) {
if (start < 0 || end > s.length - 1) {
return;
}
int left = start;
int right = end;
char temp;
while (left < right) {
temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
}
时间复杂度:O(N)
空间复杂度:O(1)
LeetCode链接 LCR 122. 路径加密 - 力扣(LeetCode)
package daimasuixiangshuati.day08_zifuchuan;
/**
* @Author LeiGe
* @Date 2023/10/22
* @Description todo
*/
public class TiHuanKongGeOffer05_2 {
/**
* 方法1:双指针
* 1.将字符扩充为加上2倍空格的长度,追加到末尾
* 2.双指针从后往前遍历:left-原来字符串最后一个位置,right-扩充后字符串的最后一个位置
* 2.1如果chars[left]==' ',将空格替换 chars[right--] = '0';chars[right--] = '2';chars[right--] = '%'; left--;right--;
* 2.2如果chars[left]!=' ',chars[right]=chars[left] left--;right--;
*
* @param s
* @return
*/
public String replaceSpace(String s) {
if (s == null || s.length() == 0) {
return s;
}
//扩充空间,空格数量的2倍数
StringBuilder str = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') {
str.append(' ');
str.append(' ');
}
}
// 若没有空格,直接返回
if (str.length() == 0) {
return s;
}
// 有空格,定义两个指针
// 左指针,指向原始字符串的最后一个位置
int left = s.length() - 1;
// 将空格追加到原始字符串后面
s += str.toString();
// 右指针,指向追加空格后的字符串的最后一个位置
int right = s.length() - 1;
char[] chars = s.toCharArray();
while (left >= 0) {
if (chars[left] == ' ') {
chars[right--] = '0';
chars[right--] = '2';
chars[right--] = '%';
} else {
chars[right] = chars[left];
}
left--;
right--;
}
return new String(chars);
}
}
LeetCode链接 151. 反转字符串中的单词 - 力扣(LeetCode)
package daimasuixiangshuati.day08_zifuchuan;
/**
* @Author LeiGe
* @Date 2023/10/22
* @Description todo
*/
public class FanZhuanZiFuChuanZhongDeDanCi151_2 {
/**
* 方法1-将字符串反转两次:
* 第一次整体反转,第二次反转单词
* 1.去除首尾以及中间的多余空格
* 2.反转整个字符串
* 3.反转各个单词
*
* @param s
* @return
*/
public String reverseWords(String s) {
//1.去除首尾以及中间的多余空格
StringBuilder sb = removeSpace(s);
//2.反转整个字符串
reverseString(sb, 0, sb.length() - 1);
//3.反转各个单词
reverseEachWord(sb);
return sb.toString();
}
/**
* 1.去除首尾以及中间的多余空格
*
* @param s
* @return
*/
private StringBuilder removeSpace(String s) {
int start = 0;
int end = s.length() - 1;
//1.去除两边的空格
while (s.charAt(start) == ' ') {
start++;
}
while (s.charAt(end) == ' ') {
end--;
}
//2.去除中间的空格
StringBuilder sb = new StringBuilder();
while (start <= end) {
char c = s.charAt(start);
//当前遍历s的位置不为' '或者sb的最后一位不为' ',则添加当前字符
if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {
sb.append(c);
}
start++;
}
return sb;
}
/**
* 翻转指定区间的字符
*
* @param sb
* @param start
* @param end
*/
public void reverseString(StringBuilder sb, int start, int end) {
while (start < end) {
char temp = sb.charAt(start);
sb.setCharAt(start, sb.charAt(end));
sb.setCharAt(end, temp);
start++;
end--;
}
}
/**
* 反转字符串中每一个单词
*
* @param sb
*/
private void reverseEachWord(StringBuilder sb) {
int n = sb.length();
int start = 0;//单词反转的开头位置
int end = 1;//单词反转的结束位置
while (start < n) {
while (end < n && sb.charAt(end) != ' ') {
end++;
}
//注意这里的写法,先反转再更新变量,保证了最后一个单词会被反转
reverseString(sb, start, end - 1);
start = end + 1;
end = start + 1;
}
}
}
LeetCode链接 LCR 182. 动态口令 - 力扣(LeetCode)
package daimasuixiangshuati.day08_zifuchuan;
/**
* @Author LeiGe
* @Date 2023/10/22
* @Description todo
*/
public class DongTaiKou182_2 {
/**
* 方法2:不使用额外空间
* 1.先反转区间为前n的字串
* 2.反转区间为n到末尾的字串
* 3.反转整个字符串
*
* @param s
* @param n
* @return
*/
public String reverseLeftWords(String s, int n) {
char[] chars = s.toCharArray();
int length = chars.length;
reverse(chars, 0, n - 1);
reverse(chars, n, length - 1);
reverse(chars, 0, length - 1);
return new String(chars);
}
private void reverse(char[] chars, int start, int end) {
while (start < end) {
char temp = chars[start];
chars[start] = chars[end];
chars[end] = temp;
start++;
end--;
}
}
}
时间复杂度:O(1)
空间复杂度:O(1)
字符串翻转相关的题目,如果一次翻转不能解决问题,可以尝试多次翻转去解决,比如:先整体翻转,后局部翻转,先翻转一部分后翻转一部分等