目录
前言
一、58剑指offer翻转单词顺序(传送门)
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。
示例 1:
输入: "the sky is blue"
输出: "blue is sky the"
示例 2:输入: " hello world! "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
🔑思路:
- 双指针,都从右往左遍历字符串数组
- start指针先找第一个非空字符
- end指针固定该非空字符后,就找到了单词最后一个字母,固定该位置不动
- start指针继续往前移动到单词首字母之前一格
- current指针找到start指针后面一格
- current指针到end指针区间即字母
- index指针是新数组ans里的指针,从0开始向后移动
- 每次加入单词后跳出循环后,加入一个空格' '
- 在返回字符串数组末尾去掉空格并加上'\0'
char* reverseWords(char* s){
int len = strlen(s);
int left = len-1; // 从右向左开始遍历
int right = len-1; // 从右向左开始遍历
int tmp = 0; // 目前指针的位置
int index = 0; // 索引记录
char* ret = (char*)malloc(sizeof(char)*(len+1)); //1
while(left>=0){
if(s[left] == ' '){
left--; // 2
}
else{ // 3
right = left; // 4
while(left >= 0 && s[left] != ' '){
left--; // 5
}
tmp = left+1; // 6
while(tmp <= right){ // 7
ret[index++] = s[tmp++]; // 8
}
// 9
ret[index++] = ' '; // 10
}
}
if(index>=1){ // 11
ret[index-1]='\0'; // 12
}
else{
ret = "";
}
return ret;
}
- 开辟空间多开一个
从右向左遍历,遇到空格指针就继续往左移动一格
从右向左遍历,遇到非空
right指针赶紧来固定pin单词末尾的非空字母
left指针继续往前移动到该单词第一个字母之前一格(空格)
tmp指针移动到该单词的第一个字母
开始遍历这个单词
把tmp到right俩指针区间(含)之间指向的单词从前往后拷贝到新数组ret中 index为数组ret的指针,从0开始往后移动
对于a[i++]和a[++i]: i++是先使用i现在的值,再给i加1 ++i是先给i加1,再用加1后的值
ret[index + 1]出现blueisskyth(?)
每个单词加入后再加一个空格' ',然后index指针移动到下一位
不考虑'\0'就会出现“执行出错”: 在字符串数组的末尾加上'\0'表示字符串结束. 如果没有这个\0,那么在使用系统函数处理这个字符串的时候,函数就不能准确判断字符串在哪里结束.从而导致数组越界 替换最后的' '为'\0' 这句话在剑指offer58里没:由于s中至少存在一个单词!!!所以index要判断是否大于0 否则测试案例""无法通过(输入"",输出也应该为"",所以根本没进入while循环!!!)
不是index,是index-1,回退到index上一位
二、151翻转字符串里的单词(传送门)
给你一个字符串 s ,逐个翻转字符串中的所有 单词 。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。
说明:
输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。
示例 1:
输入:s = "the sky is blue"
输出:"blue is sky the"
示例 2:输入:s = " hello world "
输出:"world hello"
解释:输入字符串可以在前面或者后面包含多余的空格,但是翻转后的字符不能包括。
🔑思路:
- 双指针,都从右往左遍历字符串数组
- start指针先找第一个非空字符
- end指针固定该非空字符后,就找到了单词最后一个字母,固定该位置不动
- start指针继续往前移动到单词首字母之前一格
- current指针找到start指针后面一格
- current指针到end指针区间即字母
- index指针是新数组ans里的指针,从0开始向后移动
- 每次加入单词后跳出循环后,加入一个空格' '
- 在返回字符串数组末尾去掉空格并加上'\0'
char *reverseWords(char *s)
{
int len = strlen(s);
char *ans = (char *)malloc(sizeof(char) * (len + 1));
int start = len - 1; // 从右向左开始遍历
int end = len - 1; // 从右向左开始遍历
int current = 0; // 目前指针的位置
int index = 0; // 索引记录
// start指针从右向左遍历,最左边为0,最右边为len - 1
while (start >= 0)
{
if (s[start] == ' ')
{
start--; // 从右向左遍历,遇到空格指针就继续往左移动一格
}
else // 从右向左遍历,遇到非空
{
end = start; // end指针赶紧来固定pin单词末尾的非空字母
while (start >= 0 && s[start] != ' ')
{
start--; // start指针继续往前移动到该单词第一个字母之前一格(空格)
}
current = start + 1; // current指针移动到该单词的第一个字母
while (current <= end) // 开始遍历这个单词
{
/*
* 把current到end俩指针区间(含)之间指向的单词从前往后拷贝到新数组ans中
* index为数组ans的指针,从0开始往后移动
*/
/*
* 对于a[i++]和a[++i]:
* i++是先使用i现在的值,再给i加1
* ++i是先给i加1,再用加1后的值
*/
ans[index++] = s[current++];
}
// ans[index + 1]出现blueisskyth(?)
ans[index++] = ' '; // 每个单词加入后再加一个空格' ',然后index指针移动到下一位
}
}
/*
* 不考虑'\0'就会出现“执行出错”:
* 在字符串数组的末尾加上'\0'表示字符串结束. 如果没有这个\0,那么在使用系统函数处理这个字符串的时候,函数就不能准确判断字符串在哪里结束.从而导致数组越界
* 由于s中至少存在一个单词,所以index一定大于0
*/
/*
* 在ans[index++]中index已经赋值' '后又加了1,移动到下一位,等待被赋值
* 替换掉最后的' '为'\0'
*/
ans[index - 1] = '\0'; // 不是index,是index-1,回退到index上一位
return ans;
}
总结
两题差异:
- 58比第151题多了标点符号和普通字母一样处理,但标点符号不影响任何代码改变
- 第151题题目中有这样一句话:s中至少存在一个单词,但是本题中没有,所以index要判断是否大于0,否则测试案例""无法通过(输入"",输出也应该为"",所以根本没进入while循环!)
那么最后我们就不能像第151题那样写成:
ans[index - 1] = '\0';
而是分支考虑index是否大于0
if (index >= 1)
ans[index - 1] = '\0'; // 不是index,是index-1,回退到index上一位
else
ans = "";
由于第151题一定至少有一个单词,不可能有空字符串"",所以最后写成考虑分支index的代码第151题里也可以AC(空字符串不对其产生影响,其实只有一个分支起作用)