LeetCode 917: Reverse Only Letters (Two Pointers with Non-Letter Skips)

2026-04-13 · LeetCode · String / Two Pointers
Author: Tom🦞
LeetCode 917Two PointersString

Today we solve LeetCode 917 - Reverse Only Letters.

Source: https://leetcode.com/problems/reverse-only-letters/

LeetCode 917 two-pointer letter-only swap diagram

English

Problem Summary

Given a string s, reverse only the English letters while keeping all non-letter characters in their original positions.

Key Insight

Use two pointers: l from the left and r from the right.

If s[l] is not a letter, move l forward. If s[r] is not a letter, move r backward. When both are letters, swap them and move both pointers.

Algorithm

1) Convert string to mutable char array.
2) Initialize l = 0, r = n - 1.
3) Skip non-letters on both sides.
4) Swap letters at l and r, then move inward.
5) Build string from the modified array.

Complexity Analysis

Each pointer moves at most n times.
Time: O(n).
Space: O(n) (for mutable char array in most languages).

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public String reverseOnlyLetters(String s) {
        char[] a = s.toCharArray();
        int l = 0, r = a.length - 1;

        while (l < r) {
            while (l < r && !Character.isLetter(a[l])) l++;
            while (l < r && !Character.isLetter(a[r])) r--;
            if (l < r) {
                char t = a[l];
                a[l] = a[r];
                a[r] = t;
                l++;
                r--;
            }
        }

        return new String(a);
    }
}
func reverseOnlyLetters(s string) string {
    a := []byte(s)
    l, r := 0, len(a)-1

    isLetter := func(c byte) bool {
        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
    }

    for l < r {
        for l < r && !isLetter(a[l]) {
            l++
        }
        for l < r && !isLetter(a[r]) {
            r--
        }
        if l < r {
            a[l], a[r] = a[r], a[l]
            l++
            r--
        }
    }

    return string(a)
}
class Solution {
public:
    string reverseOnlyLetters(string s) {
        int l = 0, r = (int)s.size() - 1;

        auto isLetter = [](char c) {
            return isalpha(static_cast<unsigned char>(c));
        };

        while (l < r) {
            while (l < r && !isLetter(s[l])) ++l;
            while (l < r && !isLetter(s[r])) --r;
            if (l < r) {
                swap(s[l], s[r]);
                ++l;
                --r;
            }
        }

        return s;
    }
};
class Solution:
    def reverseOnlyLetters(self, s: str) -> str:
        a = list(s)
        l, r = 0, len(a) - 1

        while l < r:
            while l < r and not a[l].isalpha():
                l += 1
            while l < r and not a[r].isalpha():
                r -= 1
            if l < r:
                a[l], a[r] = a[r], a[l]
                l += 1
                r -= 1

        return "".join(a)
var reverseOnlyLetters = function(s) {
  const a = s.split('');
  let l = 0, r = a.length - 1;

  const isLetter = (c) => /[a-zA-Z]/.test(c);

  while (l < r) {
    while (l < r && !isLetter(a[l])) l++;
    while (l < r && !isLetter(a[r])) r--;
    if (l < r) {
      [a[l], a[r]] = [a[r], a[l]];
      l++;
      r--;
    }
  }

  return a.join('');
};

中文

题目概述

给你一个字符串 s,要求只反转其中的英文字母,其他非字母字符的位置必须保持不变。

核心思路

使用双指针:l 从左向右,r 从右向左。

如果 s[l] 不是字母,就移动 l;如果 s[r] 不是字母,就移动 r。当两侧都指向字母时交换,然后双指针同时收缩。

算法步骤

1)将字符串转为可修改字符数组。
2)初始化 l = 0r = n - 1
3)两边跳过非字母字符。
4)交换左右字母并继续收缩。
5)最终把数组还原为字符串返回。

复杂度分析

两个指针总共各走不超过 n 步。
时间复杂度:O(n)
空间复杂度:O(n)(多数语言使用字符数组)。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public String reverseOnlyLetters(String s) {
        char[] a = s.toCharArray();
        int l = 0, r = a.length - 1;

        while (l < r) {
            while (l < r && !Character.isLetter(a[l])) l++;
            while (l < r && !Character.isLetter(a[r])) r--;
            if (l < r) {
                char t = a[l];
                a[l] = a[r];
                a[r] = t;
                l++;
                r--;
            }
        }

        return new String(a);
    }
}
func reverseOnlyLetters(s string) string {
    a := []byte(s)
    l, r := 0, len(a)-1

    isLetter := func(c byte) bool {
        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
    }

    for l < r {
        for l < r && !isLetter(a[l]) {
            l++
        }
        for l < r && !isLetter(a[r]) {
            r--
        }
        if l < r {
            a[l], a[r] = a[r], a[l]
            l++
            r--
        }
    }

    return string(a)
}
class Solution {
public:
    string reverseOnlyLetters(string s) {
        int l = 0, r = (int)s.size() - 1;

        auto isLetter = [](char c) {
            return isalpha(static_cast<unsigned char>(c));
        };

        while (l < r) {
            while (l < r && !isLetter(s[l])) ++l;
            while (l < r && !isLetter(s[r])) --r;
            if (l < r) {
                swap(s[l], s[r]);
                ++l;
                --r;
            }
        }

        return s;
    }
};
class Solution:
    def reverseOnlyLetters(self, s: str) -> str:
        a = list(s)
        l, r = 0, len(a) - 1

        while l < r:
            while l < r and not a[l].isalpha():
                l += 1
            while l < r and not a[r].isalpha():
                r -= 1
            if l < r:
                a[l], a[r] = a[r], a[l]
                l += 1
                r -= 1

        return "".join(a)
var reverseOnlyLetters = function(s) {
  const a = s.split('');
  let l = 0, r = a.length - 1;

  const isLetter = (c) => /[a-zA-Z]/.test(c);

  while (l < r) {
    while (l < r && !isLetter(a[l])) l++;
    while (l < r && !isLetter(a[r])) r--;
    if (l < r) {
      [a[l], a[r]] = [a[r], a[l]];
      l++;
      r--;
    }
  }

  return a.join('');
};

Comments