交换两个变量的思考
网上存在三种方法:
1) 算术运算
简单来说,就是通过+和-运算来实现。代码如下:
对于字符串做这种交换的话, 可以使用字符串连接和substr的方式来做, 建议在追加字符串的时候, insert到那个字符串的最前面, 防止其中一个字符串是另一个字符串的子串导致错误..
2) 指针操作
对指针的操作实际上进行的是整数运算。比如:两个int指针相减得到一个整数N,该整数表示两个指针变量在内存中的储存位置隔了N*sizeof(int)个字节;int指针和一个整数相加,例如“a+10”表示以a为基地址,偏移为10*sizeof(int)处的int变量。所以我们完全可以通过和算术算法类似的运算来完成指针变量值的交换,从而达到交换变量的目的。即:
简单来说,就是通过+和-运算来实现。代码如下:
int a,b; a=10;b=12; a=b-a; //a=2;b=12 b=b-a; //a=2;b=10 a=b+a; //a=12;b=10
通过以上运算,a和b中的值就进行了交换。表面上看起来很简单,但是不容易想到,尤其是在习惯标准算法之后。
此算法与标准算法相比,多了三个计算的过程,但是没有借助临时变量。(以下称为算术算法)对于字符串做这种交换的话, 可以使用字符串连接和substr的方式来做, 建议在追加字符串的时候, insert到那个字符串的最前面, 防止其中一个字符串是另一个字符串的子串导致错误..
2) 指针操作
对指针的操作实际上进行的是整数运算。比如:两个int指针相减得到一个整数N,该整数表示两个指针变量在内存中的储存位置隔了N*sizeof(int)个字节;int指针和一个整数相加,例如“a+10”表示以a为基地址,偏移为10*sizeof(int)处的int变量。所以我们完全可以通过和算术算法类似的运算来完成指针变量值的交换,从而达到交换变量的目的。即:
int *a,*b; a=new int(10); //给指针赋值 b=new int(20); //a=0x00030828,b=0x00030840 a=(int*)(b-a); //a=0x00000006 b=(int*)(b-int(a)); //b=0x00030828 a=(int*)(b+int(a)); //a=0x00030840
需要注意的是:最后三句话中,只有第一句是两个指针之间的计算,其他都是指针和整数的计算,否则会导致计算错误,严重导致系统出错。
通过以上运算a、b的地址就完成了交换,a指向了原先b指向的值,b指向原先a指向的值!此算法同样没有使用第三变量就完成了值的交换,与算术算法比较它显得不好理解,但是它有它的优点即在交换很大的数据类型时,比如说自定义的大型结构或者类,它的执行速度比算术算法快。因为它交换的时地址,而变量值在内存中是没有移动过的。(以下称为地址算法)
3) 位运算
通过异或运算也能实现变量的交换,这也许是最为神奇的,请看以下代码:
int a=10,b=12; //a=1010 b=1100; a=a^b; //a=1010^1100=0110; b=a^b; //b=0110^1100=1010; a=a^b; //a=0110^1010=1100;
//a=1100=12;b=1010;
异或操作, 对每一位而言, 0可以取得原数, 1可以取得该位的补码. //异或----相同取0,不同取1
第一次异或, 用0填充了相同的位, 用1填充了不同的位.
第二次异或, 操作数b和第一次结果做异或, 第一次的结果用0(那些相同的位)取得了b自己的值(由于这些位相同), 而第一次结果是1的那些位, 则取得了操作数b的该位的补码, 这些位的值是不同的, 因此, 操作数b中该位的补码, 实际上就是操作数a中的原码.
这样, 第二次的异或中, 将第一个操作数a中的所有位就都覆盖到了操作数b中, 完成了将a赋值给b句柄的工作.
第三次异或, 原理和第二次一样, 将b赋值给a句柄.
如果仔细思考,可以发现实际上有无数种方法。为什么需要第三个变量?我想这是绝大多数初学编程时都思考过的问题。我本身是做信号的,所以从信息的角度来分析。两个变量,必然存在两份信息(姑且以份为单位),如果直接交换,a=b,则a原来的信息丢失,所以引入一个临时变量来保存a的信息,以确保信息完整性。也就是说,temp的作用就是保存交换过程可能损失的信息量,那么只要这个信息量不损失,则无需temp.做编解码的人都知道,编码的是残差数据。残差数据包含的就是那额外的信息量。那么,完全可以在交换过程中传递额外信息,也就是a,b之间的耦合信息,则交换过程不会发生信息丢失,也就无需第三个变量。这耦合信息可以是a-b,也可以是a^b,还可以是a*b.如:
a=a*b;
a=a/b;
b=a/b;
同样可以完成交换(仅提供思路,未考虑除0溢出等问题)。举一反三,可以有无穷种方法,但原理都是一样的。
附上练习题目:
//题目描述 // //请实现一个算法,在不使用额外数据结构和储存空间的情况下,翻转一个给定的字符串(可以使用单个过程变量)。 //给定一个string iniString,请返回一个string,为翻转后的字符串。保证字符串的长度小于等于5000。 //测试样例: //"This is nowcoder" //返回:"redocwon si sihT" #include<iostream> #include<string> using namespace std; class Reverse { public: string reverseString(string iniString) { // write code here //string result; if(iniString.empty()) { return iniString; } //for (int i = iniString.size(); i >0; i--) //error: control reaches end of non-void function [-Werror,-Wreturn-type] //没有返回值啊 //{ // cout << iniString[i - 1]; //} //两个数字交换,可以借助第三变量,也可以不借助第三变量;有三种方法,自行总结 //iniString[i] ^= iniString[j] ^= iniString[i] ^= iniString[j];//交换 for (int i = 0; i < iniString.size()/2; i++) { char temp = iniString[i]; iniString[i] = iniString[iniString.size() - 1 - i]; iniString[iniString.size() - 1 - i] = temp; } return iniString; } };
C/C++基本语法学习
STL
C++ primer