user-0 提问: C——请问我写的这两种算法哪种更高效，以及各自的优点。求老师解答

回复:
user: user-0
content: 引用 42 楼 user-4 的回复:long  num[256]={0}; unsigned char    ch;  while(1) {      ch=读文件1      if(文件结束)  break;      num[ch]++; } while(1) {      ch=读文件2      if(文件结束)  break;      num[ch]++; } for(ch=0;ch<256;ch++) {       for(int i=0;i<num[ch];i++)       {             write(FILE3, &ch, 1);      } }这样就对了嘛，你两个ch++。。。幸好我还知道点

user: user-1
content: 引用 楼主 u014711608 的回复:题目：在磁盘文件FILE1和FILE2中，各存放着一行字母，现要求把两个文件中的信息合并（按字母顺序排序），输出到一个新文件FILE3中去。 我写了两种算法，请问我写的这两种算法哪种更高效，以及各自的优点。 第一种：#define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE //题目：在磁盘文件FILE1和FILE2中，各存放着一行字母，现要求把两个文件中的信息合并（按字母顺序排序），输出到一个新文件FILE3中去。 #include<io.h> #include<stdio.h> #include<fcntl.h> #include<malloc.h> struct Stack { 	char character; 	Stack *next; }; void main() { 	//注意：已知 FILE1 中的字符已经顺序存储在 FILE3 中，这是前提，就当已经在此函数里处理过了。请忽略。 	int FILE2, FILE3; 	char CharacterA, CharacterB; 	int Position = NULL; 	FILE3 = open("FILE3", O_RDWR | O_TEXT); 	FILE2 = open("FILE2", O_RDONLY | O_TEXT); 	//取出FILE2中的字符一个一个与FILE3中的字符比较； 	while (read(FILE2, &CharacterA, 1) > 0) 	{ 		//取出FILE3中的字符； 		while (read(FILE3, &CharacterB, 1) > 0) 		{  			if (CharacterA < CharacterB) 			{ 				//如果FILE2中的字符小于FILE3中的字符 				lseek(FILE3, Position, SEEK_SET);//还原FILE3的位置到Position 				Stack *StackA = NULL, *StackB = NULL; 				char CharacterC; 				//取出FILE3中Position之后的字符，存储在栈中； 				while (read(FILE3, &CharacterC, 1) > 0) 				{ 					StackA = (Stack *)malloc(sizeof(Stack)); 					StackA->character = CharacterC; 					StackA->next = StackB; 					StackB = StackA; 				} 				lseek(FILE3, Position, SEEK_SET);//还原FILE3的位置到Position 				write(FILE3, &CharacterA, 1); 				//把栈中的字符，存入FILE3中 				while (StackA != NULL) 				{ 					write(FILE3, &StackA->character, 1); 					StackB = StackA; 					StackA = StackA->next; 					free(StackB); 				} 				lseek(FILE3, 0, SEEK_SET);//还原FILE3的位置到 0 				break;//退出循环 			} 			if (eof(FILE3) != 0)//如果 FILE3 读到了末尾，还没有把 FILE2 中取出的字符写入，则强制写入到FILE3 			{ 				write(FILE3, &CharacterA, 1); 				break;//退出循环 			} 			Position = tell(FILE3);//取出FILE3位置到Position 		} 	} 	close(FILE2); 	close(FILE3); } 第二种： #include<malloc.h> struct Stack { 	char character; 	Stack *next; }; void main() { 	Stack *StackA = NULL, *StackB = NULL; 	char Character; 	//从FILE1中取得字符，存储在栈中； 	int FILE1; 	FILE1 = open("FILE1", O_RDONLY | O_TEXT); 	while (read(FILE1, &Character, 1) > 0) 	{ 		StackA = (Stack *)malloc(sizeof(Stack)); 		StackA->character = Character; 		StackA->next = StackB; 		StackB = StackA; 	} 	close(FILE1); 	//从FILE2中取得字符，存储在栈中； 	int FILE2; 	FILE2 = open("FILE2", O_RDONLY | O_TEXT); 	while (read(FILE2, &Character, 1) > 0) 	{ 		StackA = (Stack *)malloc(sizeof(Stack)); 		StackA->character = Character; 		StackA->next = StackB; 		StackB = StackA; 	} 	close(FILE2); 	//对栈中的字符进行排序； 	Stack *Stack_Head = StackA; 	for (; StackA != NULL; StackA = StackA->next) 	{ 		for (StackB = StackA->next; StackB != NULL; StackB = StackB->next) 		{ 			if (StackA->character > StackB->character) 			{ 				Character = StackA->character; 				StackA->character = StackB->character; 				StackB->character = Character; 			} 		} 	} 	//把排完序的字符写入FILE3文件中； 	int FILE3; 	FILE3 = open("FILE3", O_WRONLY | O_TRUNC); 	while (Stack_Head != NULL) 	{ 		write(FILE3, &Stack_Head->character, 1); 		StackA = Stack_Head; 		Stack_Head = Stack_Head->next; 		free(StackA); 	} 	close(FILE3); }  先想想磁盘是如何工作的，cpu是如何与磁盘通信的，再来考虑这个问题

user: user-2
content: 无profiler不要谈效率！！ 尤其在这个云计算、虚拟机、模拟器、CUDA、多核 、多级cache、指令流水线、多种存储介质、……满天飞的时代！

user: user-3
content: 另外，LZ还需要补一补文件系统的概念。 首先LZ每次读一个字节，实际上VFS（Virtual File System）已经把文件的其他一个页，或者块缓存在内存中，供下次取得，当然这个是文件系统的优化，所以每次你读一个字节，当你需要读的数据不再缓存中，再去读实际的磁盘。所以何不一次性读多一些Block呢？  凡是具备： (1)有效性 (2)确定性 (3)有穷性 这样要素的都可称为算法。  可LZ得两个算法效率过低，主要来源不再插入排序算法，而是在于文件IO，90%时间花在这上面了。

user: user-3
content: 1. 按照字节读难免造成太多的时间花在磁盘IO上，建议成块的的读取数据。 2. 因为题目所说按照字母顺序排序，是否说明只有可能存在 ascii 表内的内容呢？采用鸽巢排序（计数排序）算法，分配256个unsigned long long(64 bit)进行排序，随后成块输出到另外一个文件即可。 3. 时间复杂度应该是 O(N+M) ， 空间复杂度也只是一个常数。其中N是F1的字节个数，M是F2的字节个数。  LZ的两个算法忽略文件IO的延迟，单纯的之看待算法，LZ采用的是插入排序，且LZ假设前提两个文件都是排序好了的。最坏的情况是F1 中左右的数字都比F2中的答，算法复杂度是 O(N*M)。并没有实质上的差别。思想一样，都属于插入排序，只是一个全部加载到内存，一个慢慢读一个处理一个。  LZ可以把你的两个算法时间复杂度优化到O(M+N) ,采用Merge（归并），自行补学。  LZ可以思考一个问题：假设F1,F2 是20GB的文件呢（实际内存给你2G运行空间）？无序的呢？又该如何提高效率/重新设计你的算法。

user: user-0
content: 引用 40 楼 user-4 的回复:我说的方法就这几句话 我从来不把问题复杂化而是简单化 你没看懂不是我没写明白 是你自己的心态在做怪 for(ch=0;ch<255;ch++) {       for(int i=0;i<num[ch];ch++)       {             向文件三写ch值      } }我来还原你的这部分算法 for(ch=0;ch<255;ch++) {       for(int i=0;i<num[ch];ch++)       {             write(FILE3, &ch, sizeof(char));      } }是这样吧

user: user-4
content: 我说的方法就这几句话 我从来不把问题复杂化而是简单化 你没看懂不是我没写明白 是你自己的心态在做怪

user: user-0
content: 引用 38 楼 user-4 的回复:[quote=引用 37 楼 u014711608 的回复:] [quote=引用 36 楼 user-4 的回复:] 一个单字节还有这么多道道,正鸡巴蛋疼 long  num[255]={0};  unsigned char    ch  寻环从文件1读取数据到ch num[ch]++ 寻环从文件1读取数据到ch num[ch]++  for(ch=0;ch<255;ch++) {       for(int i=0;i<num[ch];ch++)       {             向文件三写ch值      } }如果num[ch] = 2; 很可惜，你弄丢了一个ch。[/quote]  你就颠狂吧,就是如果,假设,你试过吗? 没看懂乱鸡巴说 如果没弄丢呢? 自己抽自己两嘴巴[/quote]如果你说我没看懂，那你就应该把你的思想写明白点。

user: user-4
content: 引用 37 楼 u014711608 的回复:[quote=引用 36 楼 user-4 的回复:] 一个单字节还有这么多道道,正鸡巴蛋疼 long  num[255]={0};  unsigned char    ch  寻环从文件1读取数据到ch num[ch]++ 寻环从文件1读取数据到ch num[ch]++  for(ch=0;ch<255;ch++) {       for(int i=0;i<num[ch];ch++)       {             向文件三写ch值      } }如果num[ch] = 2; 很可惜，你弄丢了一个ch。[/quote]  你就颠狂吧,就是如果,假设,你试过吗? 没看懂乱鸡巴说 如果没弄丢呢? 自己抽自己两嘴巴

user: user-4
content: long  num[256]={0}; unsigned char    ch;  while(1) {      ch=读文件1      if(文件结束)  break;      num[ch]++; } while(1) {      ch=读文件2      if(文件结束)  break;      num[ch]++; } for(ch=0;ch<256;ch++) {       for(int i=0;i<num[ch];i++)       {             write(FILE3, &ch, 1);      } }

user: user-0
content: 引用 32 楼 zhao4zhong1 的回复:我真身其实就是那个传说中卖矛盾的小贩啊！哈哈。。老师谦虚了。我个人觉得 好的代码不是因为一个人知道的知识量的多，而是一个人去想怎么能最简单解决这个问题。也就是说一个圣人的知识再多也比不上一个白痴的一个想法。

user: user-2
content: 推荐楼主学习使用profiler  VC6：   Run the Profiler from the Development Environment Home |  Overview |  Details  Feature Only in Professional and Enterprise Editions   Profiling is supported only in Visual C++ Professional and Enterprise Editions. For more information, see Visual C++ Editions.  The development environment will profile the active project’s target. To profile more than one project, you should use custom batch files; see Profiling Multiple .DLL and .EXE Files for more information.  See Select Code to Profile for information on how to specify which code shall be profiled.  To run the profiler from the development environment  On the Build menu, choose Profile to display the Profile dialog box, as shown in the figure below.  In the Profile Type box, click one of the following option buttons:  Function Timing   Function Coverage   Line Coverage   Merge   Use Custom and the Custom Settings text box to run a batch file that contains profiler commands. See Run the Profiler from the Command Line for information on why you might want to batch and execute your own profiler commands. See Running a Custom Batch File for more information on specifying a batch file from the development environment.   Advanced settings is where you can specify PREP Phase I options.  Click OK.  Profile Dialog Box    Note   If you haven’t built your current project with profiling enabled, an error message lets you know that you need to rebuild your project. See Enable Profiling for more information.

user: user-2
content: 我真身其实就是那个传说中卖矛盾的小贩啊！

user: user-0
content: 引用 30 楼 u014711608 的回复:[quote=引用 29 楼 zhao4zhong1 的回复:] [quote=引用 21 楼 zhao4zhong1 的回复:] 提醒：无profiler不谈效率。  使用电脑计时有时误差会很大，因为待测程序段的运行会影响电脑时钟。 将待测程序段循环足够多次，手动掐秒表计时可能更准确。  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！ 讨论算法的效率、优劣、……之前， 请先验证其正确性！ 再实测其针对几组典型输入/输出数据的运行时间！ 最后才是在实测数据的基础上，讨论该算法的效率、优劣、…… 否则我认为都是空谈！  比如这个： C++ String Toolkit (StrTk) Tokenizer http://www.codeproject.com/Articles/23198/C-String-Toolkit-StrTk-Tokenizer[/quote]我运行了，正常。实测，正常。剩下....老师。。是你说不要迷信你的[/quote]对于特殊的情况。。。。老师我还是学生呢。不可能现在就去对一个简单的算法去思考它的特殊情况的

user: user-0
content: 引用 29 楼 zhao4zhong1 的回复:[quote=引用 21 楼 zhao4zhong1 的回复:] 提醒：无profiler不谈效率。  使用电脑计时有时误差会很大，因为待测程序段的运行会影响电脑时钟。 将待测程序段循环足够多次，手动掐秒表计时可能更准确。  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！ 讨论算法的效率、优劣、……之前， 请先验证其正确性！ 再实测其针对几组典型输入/输出数据的运行时间！ 最后才是在实测数据的基础上，讨论该算法的效率、优劣、…… 否则我认为都是空谈！  比如这个： C++ String Toolkit (StrTk) Tokenizer http://www.codeproject.com/Articles/23198/C-String-Toolkit-StrTk-Tokenizer[/quote]我运行了，正常。实测，正常。剩下....老师。。是你说不要迷信你的

user: user-2
content: 引用 21 楼 zhao4zhong1 的回复:提醒：无profiler不谈效率。  使用电脑计时有时误差会很大，因为待测程序段的运行会影响电脑时钟。 将待测程序段循环足够多次，手动掐秒表计时可能更准确。  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！ 讨论算法的效率、优劣、……之前， 请先验证其正确性！ 再实测其针对几组典型输入/输出数据的运行时间！ 最后才是在实测数据的基础上，讨论该算法的效率、优劣、…… 否则我认为都是空谈！  比如这个： C++ String Toolkit (StrTk) Tokenizer http://www.codeproject.com/Articles/23198/C-String-Toolkit-StrTk-Tokenizer

user: user-2
content: 个人意见： 任何算法，如果它不能针对特殊情况定义出具体的、确定的特殊处理步骤和方法，都不能称其为一个完整、可实现的算法；甚至不能称其为一个算法。

user: user-0
content: 引用 24 楼 grf9527 的回复:读就是读，排序就是排序。 写就是写。这样比较快。那就是说第二种算法快一点？还是重新用鸽巢排序法快一点？就拿数据很多的情况算

user: user-0
content: 引用 25 楼 zhao4zhong1 的回复:[quote=引用 22 楼 u014711608 的回复:] [quote=引用 20 楼 zhao4zhong1 的回复:] 这个世界上从来就没有存在过，未来也不会存在在任何情况下都不造成数据丢失的算法！ 除时间和空间可能是无限的以外，其它任何具体事物都是有限的。 任何问题首先是一个哲学（世界观）问题。 哲学（世界观）不同/不正确，即道不同/不正确。 道不同不相为谋。 我们的观念不同，虽然有时候数据的丢失时不可逆转的，包括溢出，传输等等情况。但是能不造成数据丢失的情况下，我觉得就不要让数据丢失。即使我们的思想不同，你的观念也改变不了我，我也不想影响你的观念。但是还是谢谢你，因为你有我没有的东西—经验。[/quote] 针对一个数据处理问题，请先定义清楚要加工的数据规格（格式、规模、限制、……），然后再讨论其上将要应用的算法。不然的话，我会认为和空谈没有什么区别。  在磁盘文件FILE1和FILE2中（最大多大？KB、MB、GB、TB、PB、EB、……），各存放着一行字母（最长多长？KB、MB、GB、TB、PB、EB、……），现要求把两个文件中的信息合并（按字母顺序排序），输出到一个新文件FILE3中去（磁盘剩余空间够不够放？）。 你写了两种算法，请问你写的这两种算法在上面（）列出的各种情况下都能正常工作吗？ [/quote]对特殊情况，所程序员才写出报错机制。或者在数据储存的时候特殊处理，数据库的中心思想就是方便取出。这样才挽救数据丢失的情况。难道几代的程序员的努力都是白费了么。。。。。老师，我会仔细理解你跟我说的每一句话的。

user: user-2
content: 引用 22 楼 u014711608 的回复:[quote=引用 20 楼 zhao4zhong1 的回复:] 这个世界上从来就没有存在过，未来也不会存在在任何情况下都不造成数据丢失的算法！ 除时间和空间可能是无限的以外，其它任何具体事物都是有限的。 任何问题首先是一个哲学（世界观）问题。 哲学（世界观）不同/不正确，即道不同/不正确。 道不同不相为谋。 我们的观念不同，虽然有时候数据的丢失时不可逆转的，包括溢出，传输等等情况。但是能不造成数据丢失的情况下，我觉得就不要让数据丢失。即使我们的思想不同，你的观念也改变不了我，我也不想影响你的观念。但是还是谢谢你，因为你有我没有的东西—经验。[/quote] 针对一个数据处理问题，请先定义清楚要加工的数据规格（格式、规模、限制、……），然后再讨论其上将要应用的算法。不然的话，我会认为和空谈没有什么区别。  在磁盘文件FILE1和FILE2中（最大多大？KB、MB、GB、TB、PB、EB、……），各存放着一行字母（最长多长？KB、MB、GB、TB、PB、EB、……），现要求把两个文件中的信息合并（按字母顺序排序），输出到一个新文件FILE3中去（磁盘剩余空间够不够放？）。 你写了两种算法，请问你写的这两种算法在上面（）列出的各种情况下都能正常工作吗？