【感悟《剑指offer》典型编程题的极练之路】02字符串篇!
个人主页:秋风起,再归来~
文章所属专栏:《剑指offer》典型编程题的极练之路
个人格言:悟已往之不谏,知来者犹可追
克心守己,律己则安!
目录
一、C/C++中字符数组与字符常量
二、面试题(替换空格)
2.1时间复杂度为 Q(㎡)的解法,不足以拿到 Offer
2.2 创建新的数组解题
2.3时间与空间复杂度分别为O(N)和O(1)的算法!(搞定offer就靠它了!)
编辑
三. 完结散花
一、C/C++中字符数组与字符常量
为了节省空间,C/C++把常量字符串放到单独的一个内存区域。当几个指针赋值给相同的常量字符串时,它们实际上会指向相同的内存地址。但用常量内存初始化数组,情况却有所不同。下面通过一个面试题来学习这一知识点。
#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>int main()
{char* s1 = "abcdef";char* s2 = "abcdef";char* s3[] = { "abcdef" };char* s4[] = { "abcdef" };if (s1 == s2){printf("s1与s2相等\n");}else{printf("s1与s2不相等\n");}if (s3 == s4){printf("s3与s4相等\n");}else{printf("s3与s4不相等\n");}return 0;
}
如果我们运行下面代码的话结果是什么呢?
为什么呢?
我们先通过调试来理解一下?
我们可以看到s1和s2的值相等,说明它们指向了同一块内存地址!
反之,s3与s4所指向的空间则是不同的!
那这又是为什么呢?
首先我们知道常量是不能被改变的,当内存中开辟了一片空间存放一个字符串常量并且有一个指针指向它,有朝一日,我们不小心又创建了一个相同的常量字符串并且用另一个指针指向它,这时内存并不会再创建一片空间来存放这个常量字符串,而是直接让另一个指针指向向前开辟的空间。
那为什么字符数组却不同呢,那是因为字符数组是可修改的,如果它们指向同一块空间,有朝一日,我想修改这个字符数组,那另一个字符数组必然也会被修改!
二、面试题(替换空格)
描述:
请实现一个函数,将一个字符串s中的每个空格替换成“%20”。
例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
数据范围:0≤en(s)≤1000 。保证字符串中的字符为大写英文字母、小写英文字母和空格中的一种。
题目链接:点击进入
在网络编程中,如果URL参数中含有特殊字符,如空格“#”等,则可能导致服务器端无法获得正确的参数值。我们需要将这些特殊符号转换成服务器可识别的字符。转换的规则是在%后面跟上ASCLL码的两位十六进制的表示。比如空格的ASCLL码是32即十六进制的0x20,因此空格被替换为%20,再比如#的ASCLL为35,即十六进制的0x23,他在URL中被替换为%23。
看到这个题目,我们首先应该想到的是原来一个空格字符,替换之后变成"%、"2和0这3个字符,因此字符串会变长。如果是在原来的字符串上进行替换,就有可能覆盖修改在该字符串后面的内存。如果是创建新的字符串并在新的字符串上进行替换,那么我们可以自己分配足够多的内存。由于有两种不同的解决方案,我们应该向面试官问清楚,让他明确告诉我们他的需求。假设面试官让我们在原来的字符串上进行替换,并且保证输入的字符串后面有足够多的空余内存。
2.1时间复杂度为 Q(㎡)的解法,不足以拿到 Offer
现在我们考虑怎么执行替换操作。最直观的做法是从头到尾扫描字符每次碰到空格字符的时候进行替换。由于是把1个字符替换成3个字串,符,我们必须要把空格后面所有的字符都后移2字节,否则就有两个字符被覆盖了。
举个例子,我们从头到尾把"We are happy,"中的每个空格替换成"%20"。为了形象起见,我们可以用一个表格来表示字符串,表格中的每个格子表示一个字符,如图 所示。
注:(a)字符串"Weare happy."。(b)把字符串中的第一个空格替换成"%20”。黄色背景表示需要移动的字符。(c)把字符串中的第二个空格替换成”%20"。黄色背景表示需要移动一次的字符,红色色背景表示需要移动两次的字符。
我们替换第一个空格,这个字符串变成图(b)中的内容,表格中黄色背景的格子表示需要进行移动的区域。接着我们替换第二个空格,替换之后的内容如图2.3(c)所示。同时,我们注意到用红色背景标注的happy”部分被移动了两次。
假设字符串的长度是n。对每个空格字符,需要移动后面O(n)个字符:因此对于含有 0(n)个空格字符的字符串而言,总的时间效率是 O(n’)。
当我们把这种思路阐述给面试官后,他不会就此满意,他将让我们寻找更快的方法。在前面的分析中,我们发现数组中很多字符都移动了很多次,能不能减少移动次数呢?答案是肯定的。我们换一种思路,把从前向后替换改成从后向前替换。
2.2 创建新的数组解题
结合代码注释和图解就很容易理解这种算法了~
char* replaceSpace(char* s ) {// write code hereif(s==NULL)return NULL;//先记录与字符串的大小int len=strlen(s);//记录空格的数量int count=0;int i=0;while(s[i]!='\0'){if(s[i]==' ')count++;i++;}//创建一个新的字符数组char* newStr=(char*)malloc(sizeof(char)*(len+count*2));//将原字符串的内容拷贝进新的字符数组,遇到空格就替换int j=0;int end=0;while(s[end]!='\0'){if(s[end]==' '){newStr[j++]='%';newStr[j++]='2';newStr[j++]='0';}else{newStr[j++]=s[end];}end++;}return newStr;
}
2.3时间与空间复杂度分别为O(N)和O(1)的算法!(搞定offer就靠它了!)
char* replaceSpace(char* s ) {// write code hereint end=0;if(s==NULL)return NULL;int count=0;//记录空格的数量while(s[end]!='\0')//找到字符串的末尾{if(s[end]==' '){count++;}end++;}//找到替换过后字符串的末尾int newEnd=end+2*count;//从后往前移动字符并且替换空格while(end>=0&&end<newEnd){if(s[end]==' '){s[newEnd--]='0';s[newEnd--]='2';s[newEnd--]='%';}else{s[newEnd--]=s[end];}end--;}return s;
}
这个算法的核心在于如何高效地将字符串中的空格替换为"%20",同时保证不覆盖或遗漏任何字符。算法的原理可以概括为以下几个步骤:
步骤一:计算空格数量
首先,我们需要遍历一遍输入的字符串,统计其中空格的数量。这是因为每个空格都将被替换为三个字符('%'、'2' 和 '0'),所以我们需要知道有多少个空格,以便计算替换后字符串的总长度。
步骤二:计算新字符串长度
接下来,我们根据原始字符串的长度和空格的数量,计算出替换后字符串的总长度。由于每个空格替换为三个字符,所以新字符串的长度将是原始长度加上空格数量乘以2(因为每个空格增加了两个字符)。
步骤三:从后向前遍历并替换
然后,我们从字符串的末尾开始,向前遍历原始字符串。这样做的目的是为了避免在替换过程中覆盖还未处理的字符。对于每个字符,我们检查它是否是空格。如果是空格,我们就将其替换为"%20",并更新新字符串的索引位置。如果不是空格,我们则直接将字符复制到新字符串的对应位置,并更新索引。
这个算法的关键在于利用了字符串的末尾空间,从后向前进行替换操作,避免了额外的内存分配和字符串拷贝。它只需要一次遍历就能完成替换操作,时间复杂度是O(n),其中n是字符串的长度。因此,这个算法是高效且实用的。
此外,值得注意的是,这个算法直接修改了输入的字符串,而不是创建了一个新的字符串。这意味着在调用这个函数之后,原始的字符串已经被修改。如果原始字符串不应该被修改,那么在调用这个函数之前,你需要先复制一份原始字符串。
注:这个算法假设输入的字符串有足够的空间来容纳替换后的结果。如果原始字符串的空间不足以容纳替换后的结果,那么这个算法可能会导致缓冲区溢出。因此,在实际使用时,你需要确保输入的字符串有足够的空间,或者在调用这个函数之前,先分配一个足够大的新字符串来存放替换后的结果。
值得一提的是:这种算法在牛客上并不能通过全部的测试用例,我估计是后台调用函数时传递的字符数组并没有足够大的空间,导致数组越界访问了~
一些感悟:在面试的过程中,我们也可以和前面的分析一样画一两个示意图解释自己的思路,这样既可以帮助我们厘清思路,也可以使我们和面试官交流更加的高效!
三. 完结散花
好了,这期的分享到这里就结束了~
如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~
如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~
我们下期不见不散~~
相关文章:

【感悟《剑指offer》典型编程题的极练之路】02字符串篇!
个人主页:秋风起,再归来~ 文章所属专栏:《剑指offer》典型编程题的极练之路 个人格言:悟已往之不谏,知来者犹可追 克心守己,…...
通过 Docker 实现国产数据库 OpenGauss 开发环境搭建
通过 Docker 实现国产数据库 OpenGauss 开发环境搭建 一 前置准备 2.1 下载镜像 docker pull enmotech/opengauss:5.0.1构建镜像的 Dockerfile,方便后期实现个性化定制: FROM ubuntu:22.04 as builderARG TARGETARCHWORKDIR /warehouseRUN set -eux;…...

【Java】LinkedList模拟实现
目录 整体框架IMyLinkedList接口IndexNotLegalException异常类MyLinkedList类成员变量(节点信息)addFirst(头插)addLast(尾插)在指定位置插入数据判断是否存在移除第一个相等的节点移除所有相等的节点链表的长度打印链表释放回收链表 整体框架 IMyLinkedList接口 这个接口用来…...
ubuntu下mysql常用命令
1. 登录数据库 mysql -u root -p 2.创建数据库 create database 数据库名字 mysql> create database yourdb; Query OK, 1 row affected (0.03 sec)3.显示数据库 show databases; 实操结果如下 mysql> show databases; -------------------- | Database | ---…...

燃气官网安全运行监测系统-阀井燃气监测仪-旭华智能
近年来,燃气爆炸事故频发,造成了重大人员伤亡和财产损失。这也再次为我们敲响警钟,燃气是我们日常生活中不可或缺的能源,但其潜在的危险性也是不容小觑。因此在重要节点加装燃气阀井气体监测仪,并将数据上传到系统平台…...
vue 文件预览(docx、.xlsx、pdf)
1.ifream <iframe src"" ></iframe> 注: src里面是文件地址 2.vue-office 支持vue2和vue3提供docx、.xlsx、pdf多种文档的在线预览方案 2.1安装 #docx文档预览组件 npm install vue-office/docx vue-demi#excel文档预览组件 npm install vue-office…...

云架构(二) 大使模式
Ambassador pattern (https://learn.microsoft.com/en-us/azure/architecture/patterns/ambassador) 简单描述 创建一个助手服务,这个服务代表消费服务或者应用程序发送网络请求。大使服务可以看做是与客户机同一个位置的进程外代理。 这种…...
.NET Path类库的特殊方法
在.NET中Path类库是非常常用的一个类库,包含很多我们常用的方法,常用的方法这里就不详细说明了,这里记录下几个非常规的方法。 获取随机文件名: //将返回随机的文件名Console.WriteLine(Path.GetRandomFileName()); 获取禁止在路…...
【JVM】JVM常用性能调优参数详细介绍
JVM常用性能调优参数详细介绍 一、何时进行JVM调优二、性能调优三、JVM调优的基本原则四、JVM调优目标五、JVM调优的步骤六、JVM参数七、JVM参数解析及调优八、JVM参数使用手册8.1 内存相关8.2 GC策略相关8.3 GC日志相关8.4 异常相关8.5 问题定位及优化相关九、参考文档一、何时…...
React中的受控组件与非受控组件
受控组件与非受控组件 受控组件 组件(input, select)的状态与state的值绑定,组件的状态全程响应外部数据 class TestComponent extends React.Component {constructor (props) {super(props);this.state { username: lindaidai };}render () {return <input …...
uniapp实现u-datetime-picker时间选择器的默认日期定位,解决default-value不生效问题
uniapp实现u-datetime-picker,设置默认定位日期,解决default-value不生效问题 想实现的效果是点开时间选择器默认显示当前日期,而不是该选择器最早的日期 给选择器添加ref属性,如下: <u-datetime-picker :show&q…...
react native 使用ScrollView实现下拉更新,上拉加载更多
在React Native中,要实现下拉更新和上拉加载更多的功能,你需要自定义ScrollView组件,监听滚动事件并根据滚动的位置来判断何时触发更新和加载更多的操作。以下是一个基本的实现思路: 监听滚动事件:使用ScrollView的on…...
vue2完结
笔记 关于不同版本的Vue: 1.vue.js与vue.runtime.xxx.js的区别:(1)vue.js是完整版的Vue,包含:核心功能模板解析器(2)vue.runtime.xxx.js是运行版本的Vue,只包含核心功能,没有模板解析器 2.因为…...
前端网页之间传递参数
在多页面应用中,我们可能面临着前端页面之间传递参数的情况,在一个页面获取到一些参数信息后,到另一个页面去进行后续处理,需要将前一个页面得到的一些参数带到第二个页面。当参数较少时,可以在跳转第二个页面时通过se…...
【常见面试题】Golang中,协程数最多可以开多少个?
参考: Goroutine 究竟可以开多少? 一、先说结论: 能开多少个协程,取决于单个协程处理方法所占用的CPU和内存资源(也就是看你计算机运行的应用程序的具体代码逻辑)。 二、具体来说: 如果是C…...

RabbitMQ基础笔记
视频链接:【黑马程序员RabbitMQ入门到实战教程】 文章目录 1.初识MQ1.1.同步调用1.2.异步调用1.3.技术选型 2.RabbitMQ2.1.安装2.1.1 Docker2.1.1 Linux2.1.1 Windows 2.2.收发消息2.2.1.交换机2.2.2.队列2.2.3.绑定关系2.2.4.发送消息 2.3.数据隔离2.3.1.用户管理2…...
大型项目管理神器:掌握yarn monorepo的安装和使用
I. 引言 在当今的前端开发中,由于项目规模的不断增长和多团队协同,Monorepo成为了越来越流行的开发模式。Monorepo指的是将多个相关项目或者模块打包在一起的软件开发模式,它可以让开发人员更好地组织管理代码,减少重复的代码&am…...

算法打卡day28|贪心算法篇02|Leetcode 122.买卖股票的最佳时机 II、55. 跳跃游戏、45.跳跃游戏 II
算法题 Leetcode 122.买卖股票的最佳时机 II 题目链接:122.买卖股票的最佳时机 II 大佬视频讲解:买卖股票的最佳时机 II视频讲解 个人思路 因为只有一只股票,且两天作一个交易单元,那每次只收集正利润就可以最终最多可以获取的利润…...

2013年认证杯SPSSPRO杯数学建模A题(第一阶段)护岸框架全过程文档及程序
2013年认证杯SPSSPRO杯数学建模 A题 护岸框架 原题再现: 在江河中,堤岸、江心洲的迎水区域被水流长期冲刷侵蚀。在河道整治工程中,需要在受侵蚀严重的部位设置一些人工设施,以减弱水流的冲刷,促进该处泥沙的淤积&…...

【3】3道链表力扣题:删除链表中的节点、反转链表、判断一个链表是否有环
3道链表力扣题 一、删除链表中的节点🌏 题目链接📕 示例🍀 分析💻 代码 二、反转链表🌏 题目链接📕 示例🍀 分析① 递归② 迭代 三、判断一个链表是否有环🌏 题目链接📕 …...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...