Leetcode的AC指南 —— 字符串:151. 反转字符串中的单词

摘要:
Leetcode的AC指南 —— 字符串:151. 反转字符串中的单词。题目介绍:给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

文章目录

  • 一、题目
  • 二、解析
    • 1、快慢指针+相向双指针
    • 2、我的AC指南
    • 3、关于去除多余空格的另一种实现
  • 其他语言版本
    • Java
    • C++
    • Python

一、题目


题目介绍:给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

力扣题目链接

示例 1:

输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

输入:s = "  hello world  "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

输入:s = "a good   example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

提示:
1 <= s.length <= 104
s 包含英文大小写字母、数字和空格 ’ ’
s 中 至少存在一个 单词

进阶:

  • 如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。

二、解析


1、快慢指针+相向双指针

思路:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转

移除多余空格可以参考 27.移除元素

字符串的反转可以参考 344.反转字符串

go:

func reverseWords(s string) string {
	b := []byte(s)

	// 采用快慢指针,移除前面、中间、后面存在的多余空格
	slow := 0
	for i := 0; i < len(b); i++ {
		if b[i] != ' ' {
			if slow != 0 { // 在每个单词间添加空格
				b[slow] = ' '
				slow++
			}
			for i < len(b) && b[i] != ' ' {
				// 在数组中,覆盖空格,将单词向前移动,
				// 并且将slow和i指针指向该单词后的第一个空格
				b[slow] = b[i]
				slow++
				i++
			}
		}
	}
	b = b[0:slow] // 用修改后的数组对原数组进行覆盖

	// 翻转整个字符串
	reverse(b)
	// 翻转每个单词
	last := 0
	for i := 0; i <= len(b); i++ {
		if i == len(b) || b[i] == ' ' {
			reverse(b[last:i])
			last = i + 1
		}
	}
	return string(b)
}

func reverse(b []byte) {
	left := 0
	right := len(b) - 1
	for left < right {
		b[left], b[right] = b[right], b[left]
		left++
		right--
	}
}

  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

2、我的AC指南

思路同上,这是我自己梳理的代码逻辑

unc reverseWords(s string) string {
	runes := []byte(s)
	slow := 0
	// 去除多余的空格
	for fast := 0; fast < len(runes); fast++ {
		// 如果fast指针不为空格,就将当前元素移动到slow处,然后slow++
		// 若当前元素为空,slow不动,fast寻找下一个不为空的元素
		if runes[fast] != ' ' {
			runes[slow] = runes[fast]
			slow++
			// 在单词间增加空格,这里会出现一个问题就是最后一个元素有存在空格的可能,在下面解决
			if fast < len(runes)-1 && runes[fast+1] == ' ' {
				// 如果当前元素不是最后一个元素,并且它的下一个元素是空格,则说明当前单词遍历结束,在它的后面增加一个空格。
				runes[slow] = ' '
				slow++
			}
		}
	}
	// 解决最后一个元素存在空格的可能
	if runes[slow-1] == ' ' {
		slow -= 1
	}
	// 将去除多余空格后的数组更新
	runes = runes[0:slow]

	reverse(runes) // 反转整个字符串
	start, end := 0, 0
	for end < len(runes) { // 对空格前的每个单词进行单独反转
		if runes[end] == ' '{
			reverse(runes[start:end])
			start = end + 1
		}

		end++
	}
	reverse(runes[start:]) // 反转字符串中最后一个单词
	
	return string(runes)

}

// 反转字符串
func reverse(s []byte) []byte {
	left := 0
	right := len(s) - 1
	for left < right {
		s[left], s[right] = s[right], s[left]
		left++
		right--
	}
	return s
}

3、关于去除多余空格的另一种实现

移除多余空格,参考 27.移除元素的快慢指针覆盖法

移除多余空格的思路:

  • 当前元素不是空格,复制快指针元素到慢指针,更新慢指针位置

  • 当前元素是空格,

    • 如果上一个元素不是空格,证明为单词末尾,我们在其后(也就是慢指针的位置)增加一个空格,更新慢指针位置
  • 最后去除末尾可能会多出来的空格

func reverseWords(s string) string {
	str := []byte(s)

	// 去除多余的空格
	l, r := 0, 0
	for r < len(s) {
		if str[r] != ' ' {
			str[l] = str[r]
			l++
		} else {
			if r > 0 && str[r-1] != ' ' {
				str[l] = ' '
				l++
			}
		}
		r++
	}

	length := l
	if str[l-1] == ' ' {
		length = l - 1
	}

	// 反转整个字符串
	reverse(str[0:length])

	// 反转每个单词
	start := 0
	for end := 0; end < length; end++ {
		if str[end] == ' ' {
			reverse(str[start:end])
			start = end + 1
		}
		if end == length-1 {
			reverse(str[start:length])
		}
	}

	return string(str[0:length])
}

func reverse(str []byte) {
	l, r := 0, len(str)-1
	for l <= r {
		str[l], str[r] = str[r], str[l]
		l++
		r--
	}
}

其他语言版本


Java

class Solution {
   /**
     * 不使用Java内置方法实现
     * 

* 1.去除首尾以及中间多余空格 * 2.反转整个字符串 * 3.反转各个单词 */ public String reverseWords(String s) { // System.out.println("ReverseWords.reverseWords2() called with: s = [" + s + "]"); // 1.去除首尾以及中间多余空格 StringBuilder sb = removeSpace(s); // 2.反转整个字符串 reverseString(sb, 0, sb.length() - 1); // 3.反转各个单词 reverseEachWord(sb); return sb.toString(); } private StringBuilder removeSpace(String s) { // System.out.println("ReverseWords.removeSpace() called with: s = [" + s + "]"); int start = 0; int end = s.length() - 1; while (s.charAt(start) == ' ') start++; while (s.charAt(end) == ' ') end--; StringBuilder sb = new StringBuilder(); while (start <= end) { char c = s.charAt(start); if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') { sb.append(c); } start++; } // System.out.println("ReverseWords.removeSpace returned: sb = [" + sb + "]"); return sb; } /** * 反转字符串指定区间[start, end]的字符 */ public void reverseString(StringBuilder sb, int start, int end) { // System.out.println("ReverseWords.reverseString() called with: sb = [" + sb + "], start = [" + start + "], end = [" + end + "]"); while (start < end) { char temp = sb.charAt(start); sb.setCharAt(start, sb.charAt(end)); sb.setCharAt(end, temp); start++; end--; } // System.out.println("ReverseWords.reverseString returned: sb = [" + sb + "]"); } private void reverseEachWord(StringBuilder sb) { int start = 0; int end = 1; int n = sb.length(); while (start < n) { while (end < n && sb.charAt(end) != ' ') { end++; } reverseString(sb, start, end - 1); start = end + 1; end = start + 1; } } }

C++

public:
    void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 []
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }

    void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
        int slow = 0;   //整体思想参考https://programmercarl.com/0027.移除元素.html
        for (int i = 0; i < s.size(); ++i) { //
            if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
                if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
                while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
                    s[slow++] = s[i++];
                }
            }
        }
        s.resize(slow); //slow的大小即为去除多余空格后的大小。
    }

    string reverseWords(string s) {
        removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。
        reverse(s, 0, s.size() - 1);
        int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。
        for (int i = 0; i <= s.size(); ++i) {
            if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。
                reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。
                start = i + 1; //更新下一个单词的开始下标start
            }
        }
        return s;
    }
};

Python

class Solution:
    def reverseWords(self, s: str) -> str:
        # 删除前后空白
        s = s.strip()
        # 反转整个字符串
        s = s[::-1]
        # 将字符串拆分为单词,并反转每个单词
        s = ' '.join(word[::-1] for word in s.split())
        return s

class Solution:
    def reverseWords(self, s: str) -> str:
        # 将字符串拆分为单词,即转换成列表类型
        words = s.split()

        # 反转单词
        left, right = 0, len(words) - 1
        while left < right:
            words[left], words[right] = words[right], words[left]
            left += 1
            right -= 1

        # 将列表转换成字符串
        return " ".join(words)

你可能感兴趣的:(leetcode的AC指南,leetcode,算法)