字符串是由若干字符组成的序列。字符串在编程时使用的频率非常高。
C/C++中每个字符串都以\0为结尾,这样我们就能很方便的找到字符串的最后尾部。但由于这个特点,每个字符串中都有一个额外字符的开销,很容易造成字符串越界
例如
char str[10];
strcpy(str,"0123456789");
看起来只有10个字符,但是要正确的复制该字符串,至少需要一个长度为11的数组
为了节省内存,C/C++把常量字符串放到一个单独的内存区域。当几个指针赋值给相同的常量字符串时,他们实际上会指向相同的内存地址。但用常量内存初始化数组,情况却有所不同。
int judge(){
char str1[]="hello world";
char str2[]="hello world";
char* str3="hello world";
char* str4="hello world";
if(str1==str2){
printf("str1 and str2 are same\n");
}
else{
printf("str1 and str2 are not same\n");
}
if(str3==str4){
printf("str3 and str4 are same\n");
}
else{
printf("str3 and str4 are not same\n");
}
return 0;
}
str1和str2是两个字符串数组,创建地址不同,后来在赋值,所以str1和str2不同
str3和str4是两个指针,无需为他们分配内存,只需要把他们指向hello world在内存中的地址就可以了。由于hello world是常量字符串,他在内存中只有一个拷贝,因此str3和str4只想同一个地址,所以比较str3和str4的值得到的结果相同
面试题5.替换空格
题目:请实现一个函数,把字符串中每个空格替换成%20,例如"We are happy"替换为"We%20are%20happy"
思路:对于空格,#等我们需要将这些特殊符号转换成服务器可以识别的字符。转换的规则是在%后面加上ASCII码的两位十六进制的表示。比如空格ASCII码是32,即十六进制的0x20,因此空格被替换为%20,同理#的ASCII码为35,十六进制0x23,URL中被替换为%23
本题中原来是一个空格字符,替换后变成%,2,0三个字符,那么这个字符串会变长。如果是在原来的字符串上进行替换,就有可能覆盖修改在该字符串后面的内存。如果是创建新的字符串并在新的字符串上进行替换,就可以分配足够的内存。
①从前往后替换,每次添加%20就要把后面的字符往后移动,时间复杂度O(n^2)
②从后往前替换,遍历一遍字符数组,有一个空格就在后面加上2长度,以此计算出要往后的总长度,We are happy长度为14(包括\0)里面有两个空格,因此替换后为18,时间复杂度为O(n)
而在Java里面一切都是对象,是对象的话,字符串肯定就有长度,即然有长度,编译器就可以确定要输出的字符个数,当然也就没有必要去浪费那1字节的空间用以标明字符串的结束了。
使用双标记,一个标记指向被移动的字符,一个标记指向移动目的地,而他们二者的间距就是一个单词的长度
public class spaceReplace {
public static void spaceReplace(String strOld,int len){
char[] chs =new char[len];
char[] ch = strOld.toCharArray();
for (int i = 0; i < ch.length; i++) {
chs[i] = ch[i];
}
int strOldLen = 0;
int blackString = 0;
if(chs==null || len<=0)
{
new NullPointerException();
}
int i=0;
while(chs[i]!='\0'){
strOldLen++;
if(chs[i]==' '){
blackString++;
}
i++;
}
//将空格转换成%20字符的长度
int strNewLen = strOldLen + blackString*2;
if(strNewLen>len){
new ArrayIndexOutOfBoundsException();
}
int indexOfOld=strOldLen;//指向'\0'
int indexOfNew=strNewLen;
while(indexOfOld>0 && indexOfNew>indexOfOld){
if(chs[indexOfOld] == ' '){
chs[indexOfNew--] = '0';
chs[indexOfNew--] = '2';
chs[indexOfNew--] = '%';
}
else{
chs[indexOfNew--] = chs[indexOfOld];
}
--indexOfOld;
}
for (char c : chs) {
if(c=='\0'){
break;
}
System.out.print(c);
}
System.out.println();
}
public static void main(String[] args) {
String str = "We are happy.";
spaceReplace(str,100);//We%20are%20happy.
}
}

举一反三:
在合并两个数组时,如果从前往后复制每个元素,则需要重复移动元素多次,那么我们可以考虑从后往前复制,这样就能减少移动的次数
拓展题:
有两个排序的数组A1和A2,内存在A1的末尾有足够多的空余空间容纳A2,请实现一个函数,把A2中所有的数字插入A1,并且新数组是排好序的
public class arrayMerge {
public static void main(String[] args){
int[] A1,A2;
A1=new int[]{1,3,5,7,9};
// A1=new int[]{};
A2=new int[]{2,2,3};
// A2=new int[]{2,4,6,8,10};
arrayMerge(A1,A2,100);
}
public static void arrayMerge(int[] A1,int[] A2,int num){
int[] res=new int[100];
int len1=A1.length;
int len2=A2.length;
if(len1==0&&len2!=0){
System.out.println(A2);
}
else if(len1!=0&&len2==0){
System.out.println(A1);
}
else if(len1==0&&len2==0){
new NullPointerException();
}
int mark1=len1-1,mark2=len2-1;
int mark=len1+len2-1;
while(mark1>=0&&mark2>=0){
if(A1[mark1]>A2[mark2]){
res[mark--]=A1[mark1--];
}
else if(A1[mark1]<=A2[mark2]){
res[mark--]=A2[mark2--];
}
}
// System.out.print("a");
while(mark1<0&&mark2>=0){
res[mark--]=A2[mark2--];
}
// System.out.print("b");
while(mark1>=0&&mark2<0){
res[mark--]=A1[mark1--];
}
// System.out.print("c");
for(int i=0;i<len1+len2;i++){
System.out.print(res[i]+" ");
}
}
}
