数据结构--字符串

 

今天要跟大家介绍的是数据结构当中的字符串,字符串是由若干个字符组成的序列,由于现今使用的计算机硬件结构是面向数值计算的需要而设计的,在处理字符串数据时比处理整数和浮点数要复杂得多,而且字符串在编程时使用的频率非常的高,下面先让我们一起看看 C/C++ 中字符串的特性。

C/C++ 中每个字符串都以字符 '\0' 作为结尾,这样我们就能很方便地找到字符串的最后尾部。但正由于这个特点,每个字符串中都有一个额外字符的开销,稍不留神就会造成字符串的越界。比如下面代码:

char str[10];
strcpy(str,"0123456789"); // 10位

我们先声明一个长度为 10 的字符数组,然后把字符串"0123456789" 复制到数组中。"0123456789" 这个字符串看起来只有 10 个字符,但实际上它的末尾还有一个 '\0' 字符,因此它的实际长度为 11 字符。要正确地复制该字符串,至少需要一个长度为 11 字节的数组。

大多牵扯到字符串的内容,多是模拟,当然不是简单粗暴的模拟,需要在有限的时间与空间里完成相应的算法,众所周知的有 KMP 算法等,但是今天我们只是简单的涉及下,KMP 留在后面的文章里,下面我们看看相关题目。

面试题:二进制求和

给定两个二进制字符串,返回他们的和(用二进制表示)。

输入为非空字符串且只包含数字 1 和 0。

给定两个二进制字符串,返回他们的和(用二进制表示)。

输入为非空字符串且只包含数字 1 和 0

示例 1:

输入: a = "1010", b = "1011"输出: "10101"

来源(leetcode  )

我昨天写了代码,虽然通过了,但是时间消耗还是挺大的,没办法,自身实力比较菜,就不给你们看我的代码了,咱们一起来瞅瞅耗时为 4 的代码,想法一样差不多,就是代码实现的过程不同。

 

一开始就说了,字符串的题目主要是模拟的过程,在这里我们要模拟的有这么几种情况,值得注意的是③④⑤,它们需要进位处理,当要进位的时候,怎么处理才是最简单方便的呢?。

 

我们看看代码,可以看到代码中用了 temp 判断是否有进位和其他几种情况对应字符的处理,用 carrybit 标记了进位的情况,有进位则高位加一,我在代码中也写有详细的注释,相信自己动手模拟几组就可以明白的,上代码。

string addBinary(string a, string b) {
       int na=a.size(),nb=b.size();
       int n=max(na,nb),carrybit=0; // carrybit 初始化为零,一开始是不能可能有进位的
       string res;
       if(na<nb){  // 这个 if 语句是为了是两个字符创的长度相等,方便之后进行比较。
           for(int i=0;i<nb-na;i++)
               a.insert(a.begin(),'0');
       }
       else if(na>nb){
           for(int i=0;i<na-nb;i++)
               b.insert(b.begin(),'0');
       }
       reverse(a.begin(),a.end());// 从低位开始
       reverse(b.begin(),b.end());
       for(int i=0;i<n;i++){
           int temp=0;
           if(carrybit) temp=(a[i]-'0')+(b[i]-'0')+1;// carrybit 有进位,则加一
           else temp=(a[i]-'0')+(b[i]-'0');
           switch(temp){
               case 0:
                   res.insert(res.begin(),'0'); // 对应 ① 这种情况
                   carrybit=0;
                   break;
               case 1:
                   res.insert(res.begin(),'1'); // 对应 ② 这两种情况
                   carrybit=0;
                   break;
               case 2:
                   res.insert(res.begin(),'0'); // ③④⑤,carrybit为 1, 有进位
                   carrybit=1;
                   break;
               case 3:
                   res.insert(res.begin(),'1'); //对应 ④ 的情况,低位有进位,且高位为 1 1 ,则此位为 1 
                   carrybit=1;                 
                   break;
               default:
                   break;
           }
       }
       if(carrybit)  res.insert(res.begin(),'1'); // 最后判断最高位是否还有进位,也还是对应 ④ 的情况。
       return res;
   }

面试题:替换空格

题目:请实现一个函数,把字符串中的每个空格替换成“%20”。例如,输入“We are happy.” , 则输出 “We%20are%20happy.” 。 来源:(剑指offer 面试题5)

哈哈哈,第一眼看到这题的时候,想的非常的简单,就在输出的时候判断下是否是空格,如果是空格则输出 %20 不就好了,但如果这样子的话,那这就偏离面试官的想法了,年轻人,你很危险哟。

言归正传,现在我们开始考虑怎么模拟替换操作。最直观的做法是从头到尾扫描字符串,每次碰到空格字符的时候进行替换。由于是把 1 个字符替换成 3 个字符,我们必须要把空格后面所有的字符都后移 2 两个字节,否则就有两个字符被覆盖了,那么问题也就在这里,从前往后替换的过程中,每替换一次,后面的字符就要整体向后移动两位,时间消耗是巨大的。假设字符串的长度是 n,对每个空格字符,需要移动后面 O(n) 个字符,因此对于含有 O(n) 个空格字符的字符串而言,总的时间效率是 O(n*n) ,这显然会不符面试官的要求,年轻人,你会很危险哟。

从前往后模拟图:

 

注:浅蓝色代表需要移动一次的字符,深蓝色背景表示需要移动两次的字符。

接下来我们换个思路,把从前向后替换改成从后向前替换,为什么要这样子呢,这样节省下的时间就是第一种思路中最浪费时间的步骤,我们不需要再重复的进行移动。我们知道每替换一个空格,长度就增加 2,因此替换以后字符串的长度等于原来的长度加上 2 *空格数目,现在我们从字符串的后面开始复制和替换,首先准备两个指针:P1 和 P2 。P1 指向原始字符串的末尾,而 P2 指向替换之后的字符串的末尾,文字可能会讲不明白,我们来看看模拟图的模拟操作。

注:(a)把第一个指针指向字符串的末尾,把第二个指针指向替换之后的字符串的末尾。(b)依次复制字符串的内容,直至第一个指针碰到第一个空格。(c)把第一个空格替换成“%20”,把第一个指针向前移动一格,把第二个指针向前移动 3 格。(d)依次向前复制字符串中的字符,直至碰到空格。(e)替换字符串中的倒数第二个空格,把第一个指针向前移动 1格,把第二个指针向前移动 3 格。

上代码:

void ReplaceBlank(char string[],int len){
 if(len<=0||string==NULL){
   cout<<"NULL String"<<endl;
   return ;
 }
 int p1 = len;// 初始实际长度
 int Blanknum = 0;// 空格数量
 int i = 0;
 while(string[i]!='\0'){
   if(string[i]==' ')
     Blanknum++;
   i++;
 }
 int p2 = p1 + Blanknum*2;// 空格替换后的长度
 while(p1>=0&&p2>p1){

   if(string[p1]==' '){ // 替换空格, p2 前进 3 格
     string[p2--] = '0';
     string[p2--] = '2';
     string[p2--] = '%';
   }
   else {
     string[p2--] = string[p1];// 依次替换
   }
   p1--; // 不管怎样,p1 都是需要前进一格的
 }
 cout<<string<<endl;
}

好了,接触了两个题目之后,相信你对字符串也有了一定的了解了,如果对文章中的内容不懂或者有错误,欢迎大家私聊我或者在评论区留言,我们一起讨论。

 

欢迎关注我的个人微信公众号【正经的码农】,更多精彩文章等着你。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值