user-0 提问: 一道国人通信的笔试题

回复:
user: user-1
content: a = a + b;     b = a - b;     a = a - b; 溢出对结果无影响

user: user-2
content: 引用 楼主 u010867462 的回复:有字符串 char a[] = "A1A2A3...An"; 不定义任何变量(注意 是任何，嵌入汇编的AX都不行哈)。 升序排序！！！  这个有人会做不！ 昨年12月国人通信的笔试题！ 出这种题的人就是蛋疼，正常的东西不用，非让用烧火棍

user: user-3
content: 引用 楼主 u010867462 的回复:有字符串 char a[] = "A1A2A3...An"; 不定义任何变量(注意 是任何，嵌入汇编的AX都不行哈)。 升序排序！！！  这个有人会做不！ 昨年12月国人通信的笔试题！  首先要定义一个void swap(char* A) {*A=*A+*(A+1); *(A+1)=*A-*(A+1); *A=*A-*(A+1);} 再定义2元素的排序void sort_2_F(char*A){if(*A>*(A+1) ) swap(A);  } void sort_2_B(char* A){if(*A<*(A-1) )  swap(A-1); } 有了这个两个函数，可以实现冒泡。因为没有中间变量定义指针的初始位置，只能移动（移动了就失去了初始位置），所以采用了双向的冒泡，冒泡两趟回到初始位置，并且每一趟都遍历了整个数组。时间效率是正常冒泡法的4倍。 void Bbl_Sort(char* A, int n){ while(n>1) {while(++A) sort_2_F(A-1); while(--A) sort_2_B(A+1); --n; } } 顺便吐槽，编译器都给你加了不知道多少的临时变量。

user: user-4
content: 用字典树存贮后输出就可以吧

user: user-5
content: 数组算一个变量，n算一个变量，两个变量足以。 比如使用冒泡排序  交换变量时(a是数组的变量，b是n) a=10;b=12; a=b-a; //a=2;b=12 b=b-a; //a=2;b=10 a=b+a; //a=10;b=10。   发下其他几种不需要临时变量的交换方式   1） 指针地址操作 因为对地址的操作实际上进行的是整数运算，比如：两个地址相减得到一个整数，表示两个变量在内存中的储存位置隔了多少个字节；地址和一个整数相加即“a+10”表示以a为基地址的在a后10个a类数据单元的地址。所以理论上可以通过和算术算法类似的运算来完成地址的交换，从而达到交换变量的目的。即： int *a,*b; //假设 *a=new int(10); *b=new int(20); //&a=0x00001000h,&b=0x00001200h a=(int*)(b-a); //&a=0x00000200h,&b=0x00001200h b=(int*)(b-a); //&a=0x00000200h,&b=0x00001000h a=(int*)(b+int(a)); //&a=0x00001200h,&b=0x00001000h 通过以上运算a、b的地址真的已经完成了交换，且a指向了原先b指向的值，b指向原先a指向的值了吗？上面的代码可以通过编译，但是执行结果却令人匪夷所思！原因何在？ 首先必须了解，操作系统把内存分为几个区域：系统代码/数据区、应用程序代码/数据区、堆栈区、全局数据区等等。在编译源程序时，常量、全局变量等都放入全局数据区，局部变量、动态变量则放入堆栈区。这样当算法执行到“a=(int*)(b-a)”时，a的值并不是0x00000200h，而是要加上变量a所在内存区的基地址，实际的结果是：0x008f0200h，其中0x008f即为基地址，0200即为a在该内存区的位移。它是由编译器自动添加的。因此导致以后的地址计算均不正确，使得a,b指向所在区的其他内存单元。再次，地址运算不能出现负数，即当a的地址大于b的地址时，b-a<0，系统自动采用补码的形式表示负的位移，由此会产生错误，导致与前面同样的结果。 有办法解决吗？当然！以下是改进的算法： if(a<b) { a=(int*)(b-a); b=(int*)(b-(int(a)&0x0000ffff)); a=(int*)(b+(int(a)&0x0000ffff)); } else { b=(int*)(a-b); a=(int*)(a-(int(b)&0x0000ffff)); b=(int*)(a+(int(b)&0x0000ffff)); } 算法做的最大改进就是采用位运算中的与运算“int(a)&0x0000ffff”，因为地址中高16位为段地址，后16位为位移地址，将它和0x0000ffff进行与运算后，段地址被屏蔽，只保留位移地址。这样就原始算法吻合，从而得到正确的结果。 此算法同样没有使用第三变量就完成了值的交换，与算术算法比较它显得不好理解，但是它有它的优点即在交换很大的数据类型时，它的执行速度比算术算法快。因为它交换的时地址，而变量值在内存中是没有移动过的。（以下称为地址算法）  2） 位运算 int a=10,b=12; //a=1010^b=1100; a=a^b; //a=0110^b=1100; b=a^b; //a=0110^b=1010; a=a^b; //a=1100=12;b=1010; 此算法能够实现是由异或运算的特点决定的，通过异或运算能够使数据中的某些位翻转，其他位不变。这就意味着任意一个数与任意一个给定的值连续异或两次，值不变。   3）栈实现。不多解释了，栈和相关函数定义省去。 int exchange(int x,int y)  {   stack S;   push(S,x);   push(S,y);   x=pop(S);   y=pop(S);  }  以上算法均实现了不借助其他变量来完成两个变量值的交换，相比较而言算术算法和位算法计算量相当，地址算法中计算较复杂，却可以很轻松的实现大类型（比如自定义的类或结构）的交换，而前两种只能进行整形数据的交换（理论上重载“^”运算符，也可以实现任意结构的交换）。 介绍这三种算法并不是要应用到实践当中，而是为了探讨技术，展示程序设计的魅力。

user: user-6
content: 只用一个变量，和一个数组的冒泡排序。  char a[]="abcdiopwerqefgabcd"; int n;  #define swap(a,b) do{a^=b;b^=a;a^=b;}while(0)   void sort() { 	do{ 		for(n =0;n< strlen(a)-1;n++) 		{ 			if(a[n]>a[n+1])  				swap(a[n],a[n+1]); 		}  		for(n =0;n< strlen(a)-1;n++){ 			if(a[n]>a[n+1])break; 		} 		printf("n= %d",n);  	}while(n < strlen(a)-1 ); 	 	 //	for(n= 0;n<strlen(a);n++){ // //		printf("%c",a[n]); //			    //a[n]='\0';  //	}  }

user: user-7
content: 第一，二个变量互换不用其它变量这是可以做到的，四楼已经有写。 第二，指针a，变量n和a[n]='\0'这三个条件只要保持二个不变，是可以换算出第三个条件的。 所以可以将n腾出来做临时变量。  根据上面二个条件完成一次冒泡是不难的，中间不需要用到任何其它变量。  剩下的还需要记录什么时候停止冒泡，当然如果死循环到最后也是能有序的。 其它的方法之一就是放弃a永远指向数组首地址，让它通过自加的形式参加到记录循环次数。  这样就一个变量也不用了。  太夜了，明天再想想代码

user: user-6
content: 另外 可以从后向前冒泡排序 n做循环变量 代码大致如下  char a[]="iopwerqefgabcd"; int n;  #define swap(a,b) do{a^=b;b^=a;a^=b;}while(0) void sort() { 	do{ 		for(n =0;n< strlen(a)-1;n++) 		{ 			if(a[n]< a[n+1])  				swap(a[n],a[n+1]); 		}  		for(n =0;n< strlen(a)-1;n++){ 			if(a[n]<a[n+1])break; 		} 		//printf("n= %d",n); 		if(n >= strlen(a)-1){  			for(n= strlen(a)-1;n>=0;n--){  				printf("%c",a[n]); 			    a[n]='\0';  			} 		} 	} 	while(strlen(a)>0); }

user: user-7
content: 补充一下， if(a[n]||a[n+1])  return;//这鼐逻辑写错了 改为 if(!a[n+1]) return; //当a[n+1]为'\0'时退出

user: user-7
content: 引用 7 楼 lovesmiles 的回复:第一，二个变量互换不用其它变量这是可以做到的，四楼已经有写。 第二，指针a，变量n和a[n]='\0'这三个条件只要保持二个不变，是可以换算出第三个条件的。 所以可以将n腾出来做临时变量。  根据上面二个条件完成一次冒泡是不难的，中间不需要用到任何其它变量。  剩下的还需要记录什么时候停止冒泡，当然如果死循环到最后也是能有序的。 其它的方法之一就是放弃a永远指向数组首地址，让它通过自加的形式参加到记录循环次数。  这样就一个变量也不用了。  太夜了，明天再想想代码  好了，早上醒了，想了一下，想到了用n记录停止冒泡的问题。 代码如下  char a[]="abcdiopwerqefgabcd"; int n;  sort() {    while(1) {//这是一个死循环，唯一满足退出的条件就是遍历一次后，没有发生交换的情况。否则，一直冒泡下去。    n=0;    while(a[n]&&a[n+1])//这个while遍历一次，以确认是否还需要冒泡;      {        if(a[n]>a[n+1])break;         n++;      }   if(a[n]||a[n+1]) return;//n到了数组尾，说明没有需要交换的了。可以退出。  else  {//还有需要交换的情况，则进行一次冒泡。    n=0;        while(a[n]&&a[n+1])      {//这个while循环完成一次冒泡        if(a[n]>a[n+1])          {//交换                a[n] =a[n]^a[n+1];                a[n+1]=a[n]^a[n+1];                 a[n] =a[n]^a[n+1];          }         n++;      }   }  } }    呵呵，我的代码怎么样？连一个strlen也没有用到，没有形参，没有递归调用。啦啦，哈哈

user: user-8
content: 引用 19 楼 vnvlyp 的回复:“不定义任何变量(注意 是任何，嵌入汇编的AX都不行哈)”  难道CPU可以直接在内存中运算？如果不能那肯定要用寄存器，如果用到寄存器那为什么程序不允许用运算无论如何都要用的寄存器？你想太多了。。。

user: user-9
content: “不定义任何变量(注意 是任何，嵌入汇编的AX都不行哈)”  难道CPU可以直接在内存中运算？如果不能那肯定要用寄存器，如果用到寄存器那为什么程序不允许用运算无论如何都要用的寄存器？

user: user-10
content: void swap(char &a, char &b) {     a ^= b;     b ^= a;     a ^= b; }  void asc2(char *s) {     if(*s && *(s + 1))     {         asc2(s + 1);         while(*(s + 1))         {             if(*s > *(s + 1))                 swap(*s, *(s + 1));             ++s;         }     } }  看了下。。有人说n是常量。。所以重写了一个。。

user: user-6
content: 难道要的是这个？ C : //除了数组a， 程序里，没额外定义任何变量 //只是借用一些，库里的外部变量，这个算没定义变量么？？？ #include<stdlib.h> char a[] = "abcdiopwerqefgabcd"; #define n (sizeof(a)/sizof(a[0])-1)          //常量 n  void sort_str() {         //_asm push errno 	do{ 		for(errno=0;errno< n;errno++) 		{ 			if(a[errno]>a[errno+1])  				swap(a[errno],a[errno+1]); 		}  		for(errno =0;errno< n-1;errno++){ 			if(a[errno]>a[errno+1])break; 		} 				 	}while(errno < n - 1 ); 	//_asm pop errno       //  printf("%s",a);            }

user: user-8
content: 引用 13 楼 u010867462 的回复:我想说的是 n可以用 但是这个n是常量啊 人家字符串里面定义的！！！ 你们全部拿来当变量用。。。照你这么说，只能这样了 非递归实现 #include <stdio.h> #include <string.h>  #define N strlen(a)  void swap(char* a, char* b) { 	*a ^= *b; 	*b ^= *a; 	*a ^= *b; }  void sort_str(char str[],int len) { 	len=0; 	while (len < strlen(str)) 	{ 		if (len == 0 || str[len - 1] <= str[len]) 		{ 			len++; 		} 		else 		{ 			swap(str+len,str+len - 1); 			len--; 		} 	} }  int main() { 	char a[]="abcdiopwerqefgabcd"; 	sort_str(a,N); 	puts(a); 	return 0; }

user: user-8
content: 引用 13 楼 u010867462 的回复:我想说的是 n可以用 但是这个n是常量啊 人家字符串里面定义的！！！ 你们全部拿来当变量用。。。照你这么说，只能这样了 非递归实现 void swap(char &a, char &b) { 	a ^= b; 	b ^= a; 	a ^= b; }  void sort(char str[],int len) { 	len=0; 	while (len < strlen(str)) 	{ 		if (len == 0 || str[len - 1] <= str[i]) 		{ 			len++; 		} 		else 		{ 			swap(str+i,str+i - 1]); 			len--; 		} 	} }

user: user-6
content: C++: 你要的是这个？  sort(a,a+n); cout<<a<<endl;

user: user-11
content: 引用 4 楼 qq506657335 的回复: void swap(char &a, char &b) {     a ^= b;     b ^= a;     a ^= b; }  void asc(char *s, int n) {     if(n > 1)     {         asc(s, n - 1);         --n; //避免处理掉'\0'         while(n > 0)         {             if(s[n] < s[n - 1])                 swap(s[n], s[n - 1]);             --n;         }     } }   应该是这样吧=。=

user: user-0
content: 我想说的是 n可以用 但是这个n是常量啊 人家字符串里面定义的！！！ 你们全部拿来当变量用。。。

user: user-12
content: 这个根本不需要排列好吗？？？        根据n的值，推算出A的个数，在推算出数字（1,2.。。。。。。。）的个数   'A'>'数字'，从你上面的来看。。。整个数组后面部分全是‘A’，前面部分项依次为数字。。。。。。。。。。