1 反转字符串 II
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
- 如果剩余字符少于 
k个,则将剩余字符全部反转。 - 如果剩余字符小于 
2k但大于或等于k个,则反转前k个字符,其余字符保持原样。 
示例 1:
输入:s = "abcdefg", k = 2 输出:"bacdfeg"
示例 2:
输入:s = "abcd", k = 2 输出:"bacd"
提示:
1 <= s.length <= 104s仅由小写英文组成1 <= k <= 104
思路:
- 通过循环每隔 
2k个字符遍历字符串s。 - 在循环中,根据剩余字符的数量分三种情况处理: 
  
- 若剩余字符数量大于等于 
k,则反转前k个字符。 - 若剩余字符数量少于 
k但大于0,则反转剩余所有字符。 - 若剩余字符数量为 
0,则说明已经处理完所有字符,跳出循环并返回结果 
 - 若剩余字符数量大于等于 
 
代码:
class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s.begin() + i, s.begin() + i + k );
            } else {
                // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};
 
2反转字符串中的单词
给你一个字符串 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 <= 104s包含英文大小写字母、数字和空格' 's中 至少存在一个 单词
思路:
解题思路分为以下三个步骤:
-  
移除多余空格:
- 使用两个指针,一个快指针用于遍历字符串,一个慢指针用于记录有效字符的位置。
 - 先去除字符串前面的空格,然后遍历字符串中间部分,去除冗余的空格,最后去除末尾的空格。
 - 根据空格的位置移动慢指针,将有效字符复制到合适的位置。
 - 最后重新设置字符串大小,确保末尾没有空格。
 
 -  
将整个字符串反转:
- 使用 
reverse函数将整个字符串进行反转,即将字符串首尾对应位置的字符互换。 
 - 使用 
 -  
将每个单词反转:
- 使用两个指针 
start和end来标识每个单词的起始和结束位置。 - 遍历字符串,当遇到空格或字符串末尾时,将当前单词的区间 
[start, end]进行反转。 - 更新 
start指针为下一个单词的起始位置。 
 - 使用两个指针 
 
代码:
class Solution {
public:
    // 函数功能:翻转字符串s中从start到end的字符
    void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 []
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }
    // 函数功能:去除字符串s中的多余空格
    void removeExtraSpaces(string& s) {
        int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
        // 去掉字符串前面的空格
        while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
            fastIndex++;
        }
        for (; fastIndex < s.size(); fastIndex++) {
            // 去掉字符串中间部分的冗余空格
            if (fastIndex - 1 > 0
                    && s[fastIndex - 1] == s[fastIndex]
                    && s[fastIndex] == ' ') {
                continue;
            } else {
                s[slowIndex++] = s[fastIndex];
            }
        }
        if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
            s.resize(slowIndex - 1);
        } else {
            s.resize(slowIndex); // 重新设置字符串大小
        }
    }
    // 函数功能:翻转字符串s中的单词
    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;
    }
}; 
3反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:

输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:

输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[]
提示:
- 链表中节点的数目范围是 
[0, 5000] -5000 <= Node.val <= 5000
思路:
只需要改变链表的next指针的指向,直接将链表反转
- 定义三个指针 
cur、pre和temp,分别代表当前节点、前一个节点和临时节点。 - 将 
cur指针初始化为头节点head,pre初始化为nullptr,表示反转后的链表的尾节点为nullptr。 - 使用 
while循环遍历链表,直到当前节点cur为nullptr,即遍历到链表尾部。 - 在循环中,先将 
cur的下一个节点暂存到temp中,以免丢失后续节点。然后将cur的next指针指向pre,实现反转操作。 - 接着更新 
pre和cur的位置,将pre移动到cur的位置,cur移动到temp的位置,继续下一轮循环。 - 当循环结束时,链表已经完成了反转,最后返回 
pre,即为反转后的头节点 
重点部分:
首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
代码:
class Solution {
public:
    // 函数功能:反转链表
    ListNode* reverseList(ListNode* head) {
        ListNode* temp;
        ListNode* cur = head; // 当前节点指针
        ListNode* pre = nullptr; // 前一个节点指针
        while (cur) {
            temp = cur->next; // 暂存当前节点的下一个节点
            cur->next = pre; // 当前节点的下一个节点指向前一个节点,实现反转
            pre = cur; // 前一个节点指针后移
            cur = temp; // 当前节点指针后移
        }
        return pre; // 返回反转后的头节点
    }
}; 
4删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1 输出:[]
示例 3:
输入:head = [1,2], n = 1 输出:[1]
提示:
- 链表中结点的数目为 
sz 1 <= sz <= 300 <= Node.val <= 1001 <= n <= sz
思路:
- 首先,定义了两个指针 
fast和slow,初始都指向虚拟头结点。 - 然后,
fast指针先向前移动n + 1步。这里为什么是n + 1呢?因为要保证slow指向要删除节点的前一个节点,方便后续删除操作。 - 接着,
fast和slow同时向前移动,直到fast指针指向链表的末尾。 - 最后,删除 
slow指向的下一个节点,即为倒数第n个节点。 
重点部分:
fast 首先走 n + 1 步的重点在于确保 slow 指针能够指向待删除节点的前一个节点,这样在删除操作时就能够方便地处理。举个例子,如果 fast 先行 n 步而不是 n + 1 步,那么 slow 就会指向待删除节点本身,而不是它的前一个节点,这会使得删除操作变得复杂。因此,通过让 fast 先行 n + 1 步,可以确保 slow 正好指向待删除节点的前一个节点,从而简化删除操作的实现。
代码部分:
// 快指针先走n步,使得快指针和慢指针相距n个节点
        while (n-- && fast != nullptr) {
            fast = fast->next;
        }
        fast = fast->next; // 快指针再提前走一步,因为需要让慢指针指向删除节点的上一个节点 
完整代码:
class Solution {
public:
    // 函数功能:删除倒数第N个节点
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0); // 创建虚拟头节点,简化边界情况处理
        dummyHead->next = head; // 将虚拟头节点指向原始头节点
        ListNode* slow = dummyHead; // 慢指针,指向待删除节点的前一个节点
        ListNode* fast = dummyHead; // 快指针,用于遍历链表
        // 快指针先走n步,使得快指针和慢指针相距n个节点
        while (n-- && fast != nullptr) {
            fast = fast->next;
        }
        fast = fast->next; // 快指针再提前走一步,因为需要让慢指针指向删除节点的上一个节点
        // 快慢指针一起移动,直到快指针到达链表尾部
        while (fast != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }
        // 删除倒数第N个节点
        slow->next = slow->next->next; 
        return dummyHead->next; // 返回删除节点后的头节点
    }
}; 
                

















