小白入门中:TCPL习题3-1——3-6

在上面二分法查找的例子中,while循环语句内共执行了两次测试,其实一次就足够。重写该函数,使得在内部只执行一次测试,比较两种的运行时间

int binsearch(int x , int v[] , int n)
{
	int low , high , mid;
	low = 0 ;
	high = n - 1;
	while (low <= high )
	{
		mid = (low + high) / 2;
		if(x < v[mid]) 
			high = mid - 1 ;
		else if(x > v[mid])
			low = mid + 1 ;
		else 
			return mid ;
	}
	return -1 ;
}

在这里插入图片描述
这个是正常的二分法,需要v升序排列,不然会出错

int binsearch(int x , int v[] , int n)
{
	int low , high , mid;
	low = 0 ;
	high = n - 1;
	mid = (low + high) / 2 ;
	while (low <= high && x != v[mid] )
	{
		if(x < v [mid])
			high = mid - 1 ;
		else
			low  = mid + 1 ;
		mid = (low + high ) / 2 ;
	}
	if(x == v[mid]) 
		return mid ; 
	return -1 ;
}

这是重写后的代码,实际上时间复杂度最多都是O(log2N) ,差距是不大。而且重改后阅读起比较烦人,不便于理解

3-2:编写一个函数escape(s ,t),将字符串t复制到字符串s中,并在复制过程中将换行符、制表符等不可见字符分别转换为\n,\t等相应可见的转义字符序列,要求用switch语句。在编写一个具有

相反功能的函数,在复制过程中将转义字符转换为实际字符

#include <iostream>
#include <stdio.h>
#include <time.h>
using namespace std;
/*编写一个函数escape(s ,t),将字符串t复制到字符串s中,并在复制过程中将换行符、制表符
等不可见字符分别转换为\n,\t等相应可见的转义字符序列,要求用switch语句。在编写一个具有
相反功能的函数,在复制过程中将转义字符转换为实际字符
*/
void escape(char s[] , char t[]);
int getline(char line []);
int main()
{
	char s[100] , t[100];
	int length;
	while ((length = getline(t)) > 0)	
	{
		escape(s , t);
		printf("%s\n" , s);
	}
	system("pause");
	return 0 ;
}
//t --> s
void escape(char s[] , char t[])
{
	int i , j ;
	for(i = j =0; t[i] != '\0'; i++)
	{
		switch (t[i])
		{
			case '\n': //t[i] == '\n'
				s[j++] = '\\';
				s[j++] = 'n' ;
				break;
			case '\t': //t[i] == '\t'
				s[j++] = '\\';
				s[j++] = 't' ;
				break;
			default :
				s[j++] = t[i];
				break ;
		}
	}
	s[j] = '\0';
}
//getline函数:获取字符串
int getline(char line[])
{
	int c , i ;
	for(i = 0; (c = getchar()) != EOF && c != '\n'; i++)
		line[i] = c ;
	if(c == '\n')
		line[i++] = '\n';
	line[i] = '\0';
	return i ;
}

相反功能函数oppositr_escape(s , t):

void opposite_escape(char s[] , char t[])
{
	int i , j ;
	for(i = j = 0; t[i] != '\0'; i++)
	{
		if(t[i] != '\\')
		{
			s[j++] = t[i] ;
		}	
		else
		{
			switch (t[++i])
			{
			case 't':
				s[j++] = '\t';
				break;
			case 'n':
				s[j++] = '\n';
				break;
			
			default:
				s[j++] = '\\';
				s[j++] = t[i];
				break;
			}
		}
		
	}
	s[j] = '\0' ;
}

上面这个是修改后的,原本的写法如下

//函数的初始版本
void oppositr_escape(char s[] , char t[])
{
	int i , j ;
	for(i = j = 0; t[i] != '\0'; i++)
	{
		if(t[i++] == '\\')		//i++,即switch里的t[i]是"\"后的字符
		{
			switch (t[++i])
			{
			case 't':
				s[j++] = '\t';
				break;
			case 'n':
				s[j++] = '\n';
				break;
			default:
				s[j++] = '\\';	
				s[j++] = t[i]  ;
				break;
			}
		}
		else
		{
			s[j++] = t[i] ;
		}
		
	}
	s[j] = '\0' ;
}

一直出错,缺少许多字符串,后来发现是这句if(t[i++] == '\\')出了问题,因为在每一次判断的时候i自增1。
其实在今天在学习控制流的时候就有所启发,K&R一直在强调代码的规范与简洁,即如何在保证阅读顺利的前提下编写最简洁明了的代码在此例题中,我这个写法看似简洁,实际上存在很大的bug,是我忘记了if的逻辑:先执行括号里的表达式,若为真,则执行程序块的语句。即判断时,括号里的表达式是一定执行。而我误解为若括号里表达式为真,才会执行此条表达式。

3-3:编写函数expand(s1 , s2),将字符串s1中类似于a-z一类多的速记符号在字符串s2中扩展为等价的完整列表abc…xyz。该函数可以处理大小写字母和数字,并可以处理a-b-c、a-z0-9与-a-z等类似的情况。作为前导和尾随的-字符原样排印

#include <iostream>
#include <stdio.h>
#include <time.h>
using namespace std;
/*编写函数expand(s1 , s2),将字符串s1中类似于a-z一类多的速记符号在字符
串s2中扩展为等价的完整列表abc…xyz。该函数可以处理大小写字母和数字,并可
以处理a-b-c、a-z0-9与-a-z等类似的情况。作为前导和尾随的-字符原样排印
*/
void expand(char s1[] , char s2[]);
int getline(char line []);
int main()
{
	char s[100] , t[100];
	int length;
	while ((length = getline(s)) > 0)	
	{
		expand(s , t);
		printf("%s\n" , t);
	}
	system("pause");
	return 0 ;
}
void expand(char s1[] , char s2[])
{
	char c ;
	int i , j ;
	i = j = 0 ;
	while ((c = s1[i++]) != '\0')
	{
		if(s1[i] == '-' && s1[i+1] >= c )
		{
			i++;
			while (c < s1[i])	//进行扩展
			{
				s2[j++] = c++;
			}
		}
		else
		{
			s2[j++] = c ; 	   //复制字符(最后一个)
		}
		
	}
	s2[j] = '\0' ;
}
int getline(char line[])
{
	int c , i ;
	for(i = 0; (c = getchar()) != EOF && c != '\n'; i++)
		line[i] = c ;
	if(c == '\n')
		line[i++] = '\n';
	line[i] = '\0';
	return i ;
}

答案思路太清晰了,while用的十分nice。

3-4:在数的二进制补码表示中,我们编写的itoa函数不能处理最大的负数,即n=-2^(字长-1)。请说明原因,修改该函数,使他在任何机器上运行时都能打印出正确的值

原因:二进制补码表示中,正整数的范围比多1,因为0只要全0就可以表示,便把符号位为1的全0表示为-2^(字长-1);
若n= -n,那么就会爆掉哦
所以思路就是在直接对负数进行取余

void re_itoa(int n , char s[])
{
	int i , sign ;
	i = 0 ;
	sign = n ;
	while(n != 0)
	{
		s[i++] = abs(n % 10) + '0'; 
		n /= 10 ;
	}
	if(sign < 0)
		s[i++] = '-' ;
	s[i] = '\0';
	reverse(s) ;
	
}

3-5:编写itob(n , s ,b)将整数n转化为以b为进制的数,并将转化结果以字符串的形式保存到字符串s。例如itob(n, s , b)把整数n化为十六进制整数保存在s中

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
using namespace std;
/*编写itob(n , s ,b)将整数n转化为以b为进制的数,并将转化结果
以字符串的形式保存到字符串s。例如itob(n, s , b)把整数n化为十六进制
整数保存在s中
*/
void itob(int n , char s[] , int b);//将数字转换字符串
int getline(char line []);	//获取字符串
void reverse(char s[]);		//将字符串倒置
int main()
{
	char s[100] , t[100];
	int n  , b;
	int length;
	cout << "请输入整数n、基数b:";
	while (cin >> n >> b)	
	{
		itob(n , s , b);
		printf("%s\n" , s);
	}
	system("pause");
	return 0 ;
}
void itob(int n , char s[] , int b)//b为基数。
{
	int i , sign , a;
	i = 0;
	sign = n ; //记录符号;
	while (n != 0)
	{
		a = abs(n % b);
		s[i++] = ((a < 10) ? a + '0' : a + 'a' - 10) ;
		n /= b ;
	}
	if(sign < 0)
		s[i++] = '-';
	s[i] = '\0';
	reverse(s);
}
int getline(char line[])
{
}
void reverse(char s[])
{
}

s[i++] = ((a < 10) ? a + '0' : a + 'a' - 10) ;秒啊秒啊,这个表达式我在第一次写的时候应该想不到。learning!
不过十进制转其他进制原来这门简单吗,想之前我可是被困扰很久。。。

3-6:改写itoa,可以获得参数lim,使得转换后所得的结果至少具有lim的长度必要时在左边填充一定的空格(为了便于观察,用 ~ )

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
using namespace std;
/*改写itoa,可以获得参数lim,使得转换后所得的结果至少具有lim的长度
必要时在左边填充一定的空格(为了便于观察,用 ~ )
*/
void itob(int n , char s[] , int b , int lim);//将数字转换字符串
int getline(char line []);	//获取字符串
void reverse(char s[]);		//将字符串倒置
int main()
{
	char s[100] , t[100];
	int n  , b , lim ;
	int length;
	cout << "请输入整数n、基数b、最小宽度:";
	while (cin >> n >> b >> lim)	
	{
		itob(n , s , b , lim);
		printf("%s\n" , s);
	}
	system("pause");
	return 0 ;
}
void itob(int n , char s[] , int b , int lim)//b为基数。
{
	int i , sign , a;
	i = 0;
	sign = n ; //记录符号;
	while (n != 0)
	{
		a = abs(n % b);
		s[i++] = ((a < 10) ? a + '0' : a + 'a' - 10) ;
		n /= b ;
	}
	if(sign < 0)
		s[i++] = '-';
	while (i < lim )
	{
		s[i++] = '~' ;//为了便于观察,用~代替空格
	}
	s[i] = '\0';
	reverse(s);
}
int getline(char line[])
{
}
void reverse(char s[])
{
}

总结:第三章难度还行,但是想要强调的思想很nice,如何写简洁明了、不影响阅读的代码?
在什么情况用for , while , do-while ,
"?:",可以用来简化一个if-else语句
还学了进制转换、数字转字符串等blabla的东西。
春季赛决赛开始了,今晚划水摸鱼,明天第四章函数与程序结构咯

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值