力扣----双指针

本文介绍了多种字符串处理的算法,包括反转字母、两数之和、比较版本号、压缩字符串、反转单词前缀等,并提供了Python和C的实现。解题思路涉及双指针、排序、二分查找和字符串转换等技术。此外,还讨论了C语言中的atoi和strtok函数用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

  1. 917. 仅仅反转字母
  2. 167. 两数之和 II - 输入有序数组
  3. 165. 比较版本号
  4. 443. 压缩字符串
  5. 2000. 反转单词前缀
  6. 917. 仅仅反转字母
  7. 475. 供暖器
  8. 面试题 16.06. 最小差

解题思路与代码详情

【题1】917. 仅仅反转字母

917. 仅仅反转字母: 给你一个字符串 s ,根据下述规则反转字符串:

所有非英文字母保留在原有位置。
所有英文字母(小写或大写)位置反转。

返回反转后的 s 。

题目解析: 将所有英文字符前后位置反转,其中字符串中包含非英文字符,若遇非英文字符,则反转时顺位。
解题思路: 设置双指针分别表示前后字符的位置,若当前前后位置都为英文字符,则进行元素互换,若前后位置有任何一处为非英文字符,则顺位。

# python3
class Solution:
    def reverseOnlyLetters(self, s: str) -> str:
        n=len(s)
        slist=list(s)
        left,right=0,n-1
        while left<right:
            if (ord(slist[left])>=97 and ord(slist[left])<=122) or (ord(slist[left])>=65 and ord(slist[left])<=90):
                if (ord(slist[right])>=97 and ord(slist[right])<=122) or (ord(slist[right])>=65 and ord(slist[right])<=90):
                    slist[left],slist[right]=slist[right],slist[left]
                    left+=1
                    right-=1
                else:
                    right-=1
            else:
                left+=1
        return "".join(slist)
//C
char * reverseOnlyLetters(char * s){
    int n=strlen(s);
    int left=0,right=n-1;
    char temp;
    while(left<right){
        if((s[left]-'a'>=0&&s[left]-'a'<26)||(s[left]-'A'>=0&&s[left]-'A'<26)){
            if((s[right]-'a'>=0&&s[right]-'a'<26)||(s[right]-'A'>=0&&s[right]-'A'<26)){
                temp=s[left];
                s[left]=s[right];
                s[right]=temp;
                left++;
                right--;
            }else{
                right--;
            }
        }else{
            left++;
        }
    }
    return s;
}

【题2】167. 两数之和 II - 输入有序数组

167. 两数之和 II - 输入有序数组: 给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

题目解析: 找出一组数中加和为目标值的唯一一对数。
解题思路: 设置前后位置双指针,判断加和与目标值的关系。

#python3
class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        n=len(numbers)
        left,right=0,n-1
        ret=[0]*2
        while left<right:
            if numbers[left]+numbers[right]<target:
                left+=1
            elif numbers[left]+numbers[right]>target:
                right-=1
            else:
                ret[0]=left+1
                ret[1]=right+1
                break
        return ret
//C
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
    *returnSize=2;
    int* ret=(int*)malloc(sizeof(int)*2);
    int left=0,right=numbersSize-1;
    while(left<right){
        if(numbers[left]+numbers[right]<target){
            left++;
        }else if(numbers[left]+numbers[right]>target){
            right--;
        }else{
            ret[0]=left+1;
            ret[1]=right+1;
            break;
        }
    }
    return ret;
    free(ret);
}

【题3】165. 比较版本号

**165. 比较版本号:**给你两个版本号 version1 和 version2 ,请你比较它们。

版本号由一个或多个修订号组成,各修订号由一个 ‘.’ 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。

比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。

返回规则如下:
如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。

知识点涉及

  1. zip()函数是Python内置函数。可用于将可迭代的对象作为参数,将对象中对应的元素打包成一个元组,然后由这些元组形成zip类型的新对象。可用list()转换输出列表若作为参数的对象长度不一致,则返回的对象长度与最短的那个作为参数的对象的长度一致
  • 使用*号可将zip类型的对象解压,解压后的数据类型为元组类型。如
a=[1,2,3];
b=[4,5,6,7];
new=zip(a,b)
print('1.',new)
print('2.',type(new))
newlist=list(zip(a,b))
print('3.',newlist)
a1,b1=zip(*zip(a,b))
print('4.',a1)
print('4.',type(a1))
print('5.',b1)
'''
输出结果为:
2. <zip object at 0x0000013437AE9B00>
3. <class 'zip'>
4. [(1, 4), (2, 5), (3, 6)]
5. (1, 2, 3)
6. <class 'tuple'>
7. (4, 5, 6)
'''
  1. zip_longest 函数. 使用时需要导入itertools模块,且使用的时候需要指定一个填充值fillvalue。返回的对象类型为itertools.zip_longest。返回的对象长度与最长的那个作为参数的对象的长度一致。较短的对象遍历完毕后使用设置的fillvalue值与较长的对象的元素形成元组。若填充值fillvalue不设定,则默认的填充值为None
    举例
a=[1,2,3];
b=[4,5,6,7];
from itertools import zip_longest
newlongest1=zip_longest(a,b)
newlongest2=zip_longest(a,b,fillvalue=666)
print('1.',type(newlongest1))
newlongestlist1=list(newlongest1)
newlongestlist2=list(newlongest2)
print('2.',newlongestlist1)
print('3.',newlongestlist2)
'''
输出结果为:
9. <class 'itertools.zip_longest'>
10. [(1, 4), (2, 5), (3, 6), (None, 7)]
11. [(1, 4), (2, 5), (3, 6), (666, 7)]
'''
  1. 字符串类型的数据转换为int型整数

C 库函数 int atoi(const char *str) (函数声明) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。

此函数包含在C标准库<stdlib.h>

该函数返回转换后的长整数,如果没有执行有效的转换,则返回零

栗子

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
  int val;
  char str[20];
  
  strcpy(str, "98993489");
  val = atoi(str);
  printf("字符串值 = %s, 整型值 = %d\n", str, val);

  strcpy(str, "runoob.com");
  val = atoi(str);
  printf("字符串值 = %s, 整型值 = %d\n", str, val);

  return(0);
}
/*
输出结果为:
字符串值 = 98993489, 整型值 = 98993489
字符串值 = runoob.com, 整型值 = 0
*/
  1. 将字符串按规定分隔符分解

C 库函数 char *strtok(char *str, const char *delim)(函数声明)分解字符串 str 为一组字符串,delim 为分隔符。

此函数包含在C标准库<string.h>

该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。

栗子

#include <string.h>
#include <stdio.h>

int main () {
  char str[80] = "This is - www.runoob.com - website";
  const char s[2] = "-";
  char *token;
  
  /* 获取第一个子字符串 */
  token = strtok(str, s);
  
  /* 继续获取其他的子字符串 */
  while( token != NULL ) {
     printf( "%s\n", token );
   
     token = strtok(NULL, s);
  }
  
  return(0);
}
/*
输出结果为:
This is 
www.runoob.com 
website
*/

题目解析: 比较两个版本号的大小。
解题思路: 字符整型的转换。

#python3
class Solution:
    def compareVersion(self, version1: str, version2: str) -> int:
        listtuple=[ll for ll in zip_longest(version1.split('.'), version2.split('.'), fillvalue=0)]
        for v1, v2 in listtuple:
            #zip_longest具体可以用来对列表的一一对应,如果列表的长度不一致,则其会选择最长的那个列表,#并将没有的填充为None(这个可以自己传参时设置)
            x, y = int(v1), int(v2)
            if x != y:
                return 1 if x > y else -1
        return 0
int compareVersion(char * version1, char * version2){
    int ver1[1000] = {0};
    int ver2[1000] = {0};
    int ver1_len = 0; int ver2_len = 0;

    for (char* p = strtok(version1, "."); p != NULL; p = strtok(NULL, ".")) {
        ver1[ver1_len] = atoi(p);
        ver1_len++;
    }
    for (char* q = strtok(version2, "."); q != NULL; q = strtok(NULL, ".")) {
        ver2[ver2_len] = atoi(q);
        ver2_len++;
    }

    int max_len = fmax(ver1_len, ver2_len);
    for (int i = 0; i < max_len; i++) {
        if (ver1[i] < ver2[i]) {
            return -1;
        } else if (ver1[i] > ver2[i]) {
            return 1;
        }
    }
    return 0;
}

【题4】443. 压缩字符串

443. 压缩字符串: 给你一个字符数组 chars ,请使用下述算法压缩:

从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符
  1.如果这一组长度为 1 ,则将字符追加到 s 中。
  2.否则,需要向 s 追加字符,后跟这一组的长度。

压缩后得到的字符串 s 不应该直接返回 ,需要转储到字符数组 chars 中。需要注意的是,如果组长度为 10 或 10 以上,则在 chars 数组中会被拆分为多个字符。

请在 修改完输入数组后 ,返回该数组的新长度。

你必须设计并实现一个只使用常量额外空间的算法来解决此问题。

题目解析: 需要在原列表中记录原列表中出现的字符与字符出现次数,若出现次数为1,则仅记录出现的字符,若出现次数大于等于10,则将出现的次数分割成0-9之内的数字。
解题思路:
(1)分别设置表示写入,遍历位置,求算出现次数的指针write,left,right。
(2)每次新字母开始遍历的位置为left,从该位置开始遍历只要字母相同,就继续往下查找,直至字母不再相同,记录该字符出现的次数
(3)将字母以及出现的次数依次替换原列表中的元素
(4)若出现次数大于10,则需要将出现次数分割为0-9以内的数字。

class Solution:
    def compress(self, chars: List[str]) -> int:
        n=len(chars)
        write=0
        left=0
        while left<n:
            num=0
            right=left
            while right<n and chars[right]==chars[left]:
                right+=1
            num=right-left
            chars[write]=chars[left]
            write+=1
            if num>1:
                for c in str(num):
                    chars[write]=c
                    write+=1
            left=right
        return write
//C
void swap(char *a, char *b) {
    char t = *a;
    *a = *b, *b = t;
}
void reverse(char *a, char *b) {
    while (a < b) {
        swap(a++, --b);
    }
}
int compress(char* chars, int charsSize){
    int write=0;
    int left=0;
    int num;
    while(left<charsSize){
        num=0;
        int right;
        right=left;
        while(right<charsSize && chars[right]==chars[left]){
            right++;
        }
        num=right-left;
        chars[write++]=chars[left];
        left=right;
        if(num>1){
            int ancor=write;
            while(num>0){
                chars[write++]=num % 10 +'0';
                num /= 10;  
            }
        reverse(&chars[ancor], &chars[write]);
        }   
}
    return write;
}

【题5】2000. 反转单词前缀

2000. 反转单词前缀: 给你一个下标从 0 开始的字符串 word 和一个字符 ch 。找出 ch 第一次出现的下标 i ,反转 word 中从下标 0 开始、直到下标 i 结束(含下标 i )的那段字符。如果 word 中不存在字符 ch ,则无需进行任何操作。

 例如,如果 word = “abcdefd” 且 ch = “d” ,那么你应该 反转 从下标 0 开始、直到下标 3 结束(含下标 3 )。结果字符串将会是 “dcbaefd” 。

返回 **结果字符串 **

题目解析: 反转从下标0到目标元素位置的子字符串。
解题思路: 遍历查找目标元素记录位置,反转子字符串。

class Solution:
    def reversePrefix(self, word: str, ch: str) -> str:
        n=len(word)
        index=0
        for i in range(n):
            if word[i]==ch:
                index=i
                break
        return word[:index+1][::-1]+word[index+1:]
void swap(char *a,char *b){
    char temp=*a;
    *a=*b;
    *b=temp;
}
char * reversePrefix(char * word, char ch){
    int n=strlen(word);
    int index=0;
    for(int i=0;i<n;i++){
        if(word[i]==ch){
            index=i;
            break;
        }
    }
    int j=index;
    int i=0;
    while(i<j){
        swap(&word[i],&word[j]);
        i++;
        j--;
    }
    return word;
}

【题6】917. 仅仅反转字母

917. 仅仅反转字母: 给你一个字符串 s ,根据下述规则反转字符串:

所有非英文字母保留在原有位置。
所有英文字母(小写或大写)位置反转。

返回反转后的 s 。

见【题1】

【题7】475. 供暖器

475. 供暖器: 冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。

在加热器的加热半径范围内的每个房屋都可以获得供暖。

现在,给出位于一条水平线上的房屋 houses 和供暖器 heaters 的位置,请你找出并返回可以覆盖所有房屋的最小加热半径。

说明:所有供暖器都遵循你的半径标准,加热的半径也一样。

题目解析: 求供暖器与房间之间的距离,确定每个供暖器与每个房间之间的所有最小距离中的最大值。
解题思路: 此处使用了排序+二分排序。
(1)将所有供暖器的位置按升序顺序排序
(2)逐一将房间的位置作为目标值,在供暖器的位置数组中二分查找与该目标值最近的那个值。
(3)(2)中的所有值取最大值。

class Solution:
    def findRadius(self, houses: List[int], heaters: List[int]) -> int:
        heaters.sort()
        nheaters=len(heaters)
        nhouse=len(houses)
        maxs=0
        for i in range(nhouse):
            ans=-1
            left,right=0,nheaters-1
            while left<=right:
                mid=left+(right-left)//2
                if heaters[mid]>houses[i]:
                    right=mid-1
                else:
                    left=mid+1
                    ans=mid
            if left>nheaters-1 or right>nheaters-1:
                maxs=max(maxs,abs(houses[i]-heaters[ans]))
            else:
                maxs=max(maxs,min(abs(houses[i]-heaters[right]),abs(houses[i]-heaters[left])))
        return maxs
//C
int cmp(const void* p1, const void*p2){
    return *(int*)p1-*(int*)p2;
}
int findRadius(int* houses, int housesSize, int* heaters, int heatersSize){
    qsort(heaters, heatersSize, sizeof(int),cmp);
    int ret=0;
    int i;
    for(i=0;i<housesSize;++i){
        int ans=-1;
        int left=0, right=heatersSize-1;
        int mid;
        while(left<=right){
            mid=left+(right-left)/2;
            if(heaters[mid]>houses[i]){
                right=mid-1;
            }else{
                left=mid+1;
                ans=mid;
            }
        }
        int x;
        if(ans==heatersSize-1){
            x=abs(houses[i]-heaters[ans]);
        }else if(ans<0){
            x=abs(houses[i]-heaters[left]);
        }else{
            x=fmin(abs(houses[i]-heaters[ans]),abs(houses[i]-heaters[ans+1]));
        }
        ret=fmax(ret,x);
    }
    return ret;
}

【题8】面试题 16.06. 最小差

面试题 16.06. 最小差: 给定两个整数数组ab,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差

题目解析: 求两个整数数组各个元素之间的绝对差值,确定所有绝对差值中的最小值。
解题思路: 此处使用了排序+双指针。
(1)将两个整数数组都按升序顺序排序
(2)逐一比较两个数组元素之间的大小关系,求算绝对差值最小值

class Solution:
    def smallestDifference(self, a: List[int], b: List[int]) -> int:
        b.sort()
        na=len(a)
        nb=len(b)
        ret=2147483647
        for i in range(na):
            ans=-1
            left,right=0,nb-1
            while left<=right:
                mid=left+(right-left)//2
                if b[mid]>a[i]:
                    right=mid-1
                else:
                    left=mid+1
                    ans=mid
            if ans==nb-1:
                x=abs(a[i]-b[ans])
            else:
                x=min(abs(a[i]-b[ans]),abs(a[i]-b[ans+1]))
            ret=min(ret,x)
        return ret
            
/*先排序,然后两数字互相逼近*/
/*先排序,然后两数字互相逼近*/
#define ABS_NUM(a)              ((a) > 0  ? (a) : -(a))
#define MIN_NUM(a, b)           ((a) > (b) ? (b) : (a))
/*
long long ABS_NUM(long long a){
    return a>0?a:(-a);
}
long long MIN_NUM(long long a, long long b){
    return a>b?b:a;
}*/

int cmp(const void *a, const void *b) {
    //const int l = *(int *)a;
    //const int r = *(int *)b;
    //return l > r ? 1 : -1;
    return *(int*)a>*(int*)b;
}
int smallestDifference(int* a, int aSize, int* b, int bSize){
    //if (a == NULL || aSize == 0 || b == NULL || bSize == 0) {
        //return 0;
    //}
    qsort(a, aSize, sizeof(int), cmp);
    qsort(b, bSize, sizeof(int), cmp);
    long long min = ABS_NUM((long long)(a[0] - b[0]));
    int i = 0, j = 0;
    while (i < aSize && j < bSize) {
        if (a[i] > b[j]) {
            min = MIN_NUM(min, ABS_NUM((long long)(a[i] - b[j])));
            j++;
        }else if (a[i] < b[j]) {
            min = MIN_NUM(min, ABS_NUM((long long)(a[i] - b[j])));
            i++;
        }else {
            return 0;
        }
    }
    return min;
}

参考文献

  1. https://blog.youkuaiyun.com/WhereIsHeroFrom/article/details/124522256
  2. https://blog.youkuaiyun.com/WhereIsHeroFrom/article/details/125076627
  3. Python:解析zip函数和zip_longest函数
  4. python zip_longest如何使用
  5. C 库函数 - atoi()
  6. C 库函数 - strtok()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值