题目描述
给你一个字符串 s
。
你的任务是重复以下操作删除 所有 数字字符:
- 删除 第一个数字字符 以及它左边 最近 的 非数字 字符。
请你返回删除所有数字字符以后剩下的字符串。
示例 1:
输入:s = "abc"
输出:"abc"
解释:
字符串中没有数字。
示例 2:
输入:s = "cb34"
输出:""
解释:
一开始,我们对 s[2]
执行操作,s
变为 "c4"
。
然后对 s[1]
执行操作,s
变为 ""
。
提示:
1 <= s.length <= 100
s
只包含小写英文字母和数字字符。- 输入保证所有数字都可以按以上操作被删除。
问题解析
先分析下问题,本题的要求是删除字符串中的数字字符以及其左边的第一个字符。
在偷瞄答案使用栈处理之前,我考虑过通过组合新字符的方式来解决问题,但是实际上这个方法需要考虑的因素太多了。
大概的思路是通过遍历字符串,当遍历到数字字符时不做处理,遍历到其他字符存入新字符,但是这样存在的问题是在新字符中删除数字字符对应的左边字符还需要一次处理,如果新建一个索引表又会导致更多的内存开销,而且还有一个需要考虑的情况是,需要删除的字符可能与数字字符间隔几个字符,这时候再去确认之后需要删除的索引就更麻烦了。
随后我又考虑检查非数字字符的方式,如果这个字符不是数字字符,再判断右边是否为数字字符,但是存在的问题和上面一样,并且这两种方式都需要考虑边界索引溢出的问题。
由于缺乏一定的算法基础,思考再三也没有想到合适的解题思路,偷摸打开题解看了一下,这题给的答案是使用栈来解决问题,理解了一下代码瞬间就通透了,这个问题就很好解决了。
当然解题不是目的,看完题解要去了解其中的背景知识,深入理解背后的原因,达到一通百通的目的。
代码(C语言)
char* clearDigits(char* s) {
int top = 0; // 栈顶
for(int i=0;i<strlen(s);i++){
if(isdigit(s[i])){
// 栈顶向前
top--;
}else{
// 入栈
s[top++]=s[i];
}
}
// 在栈顶结束
s[top]='\0';
return s;
}
什么是栈?
栈和队列这四个人老生常谈了,他俩都是典型的线性数据结构,最直观的区别是栈是后进先出的,队列是先进先出的结构。
不管是栈还是队列,每次读取都只能读取一个数据,并且必须按顺序读取,也就是说,不能直接取出中间的数据。
栈的数据是顺序存储的,每次写入数据即从栈顶压入数据,读取数据即从栈顶弹出数据。
作为数据结构,栈更多是一种算法思想,而不是一种具体的物理结构。
为什么这题使用栈?
从问题的现实情况入手,本题的现实问题是删除字符串中的数字字符以及其对应的左边一个字符。
从问题中就可以得到,本题的输入就是一个字符串,结合数据结构绪论中的内容,设计这个算法要处理的数据对象就是一个字符串,而字符串是一种典型的线性结构数据。
那么就可以考虑使用相关处理线性结构数据的算法思想来解决这个问题。
其实如果能够想到这一点的话,自然也就想到栈了,毕竟提到线性结构数据最先想到的大概也就是栈和队列了。(总不能想着用链表和数组来解决吧?)
那么栈和队列选择哪一个?
还是从现实问题入手。
结合本题的要求,我们需要删除数字字符和对应的前一个字符。也就是,需要有一个回退操作。
提到回退操作,那自然就是使用栈了,在遍历到数字字符的时候,弹出当前字符,栈顶后移到前一个非数字字符,然后最后将非数字字符改为空白符也就是实现了删除前一个字符。
通透!
算法设计
根据栈的思路,本题的代码就很简单了,只需要一个模拟栈顶的变量储存栈顶索引,然后通过遍历字符串,在循环中的基本语句设计判断是否为数字字符的流程控制并且做出对应的操作:是数字,弹出栈顶;不是数字,执行入栈
扩展
依据本题的思路来扩展思考一下,本题处理的数据是线性结构数据,如果是其他的线性结构数据的问题求解,也可以使用栈或者队列来实现,思考下大概可能的情况:
叫号取票:当叫到号时,此号从队列中移出。
回溯算法:栈天然能够保存之前的状态,对于历史记录的回退通过弹出栈顶即可实现。
正则匹配:尤其是成对出现的符号例如括号,通过栈回退匹配前一个对应字符。
Comments NOTHING