进制转换的思路分析与计算器的实现

一、进制转换

思路

  1. 十进制转任意进制:
    求余,例如一个十进制17,求二进制
    (第一次)17/2 = 8 --------余数1
    (第二次)8/2 = 4 ---------余数0
    (第三次)4/2 = 2----------余数0
    (第四次)2/2 = 1----------余数0
    (第五次)1/2 = 0 ----------余数1(最高位)
    输出:10001 即为17对应的二进制数。
  2. 将其他进制数转换为十进制数
    类似于阶乘, 例如一个十六进制数5A3,求十进制
    (第一步)3 * (16 ^ 0) = 3
    (第二步)A * (16^1) = 10 * 16
    (第三步)5 * (16^2) = 5 * 16 * 16
    输出:3+10 * (16 ^ 0)+5 * (16 ^ 1) = 1443

代码

  1. 十进制转换为任何进制:

    递归完毕后回溯第一次的值为最高位,而第一次调用时对应最低位。
    根据这个特性,我们也可以模拟栈来存储进制转换过程的求余数据缓存,最后弹栈。
    
void ten_to_else(int A, int B)//(A为十进制数,B为代表任意进制)
{
        int n;
        if(A == 0)
                return;
        ten_to_else(A/B, B);
        n = A % B;
        if(n < 10){
                printf("%d",n);
        }else
                printf("%c",n%10 + 'a');
}
  1. 将其他进制数转换为十进制数:(a为数值,B表示其进制)

    数组中数组的高位在前,每for循环一次sum翻倍。
    
int TenNum(char a[], int B)
{
	int len, i, num;
	int sum = 0;     
	len = strlen(a);                    //求得字符串长度
	for (i = 0; i < len; i++)
	{
		if (a[i] >= '0' && a[i] <= '9')
			num = a[i] - '0';
		else if (a[i] >= 'A' && a[i] <= 'F')
			num = a[i] - 'A' + 10;
		sum = sum * B + num;
	}
	return sum;

二、计算器

首先从人性化的角度,表达 2*((1*3) + (4-2))对于我们来说很直观,因为我们可以跳过机械化的从数据的头一个一个符号看到尾,直接看优先级。对于计算机来说它就必须得从前到后依次分析符号的匹配以及优先级等等复杂规则。

1.栈模型实现计算机

符号匹配原则
遇到左符号入栈,其他普通字符则忽略,遇到右符号则弹栈,与该左符号匹配,直到栈中全部匹配完毕。

中缀表达式:符号我们使用和阅读的,“ 1 + 3”运算符位于中间。
(逆波兰)后缀表达式:符合计算机的。“ 1 3 +”将运算符符号放于数字后面。

中缀转后缀规则
  1. 遇到数字直接输出
  2. 遇到左括号则压栈
  3. 遇到运算符,与栈顶符号进行优先级比较,如果优先级比栈顶的符号低则继续压栈,否则将栈顶符号弹出再进行压栈。
  4. 遇到右括号,将栈顶元素弹出,直到匹配到左括号。
  5. 若表达式已遍历接受,则弹出栈中所有符号

例子: 8 + (3 - 1)* 5 ==> 8 3 1 - 5 * +

代码细节部分:符号在栈中进行压栈弹栈操作,而操作数则直接输出,逆波兰表达式中每个符号都要用空格隔开,避免操作数误解,例如8 3 1 - 5 * +,可被理解成 83 1 - 5 * +。
细节处理:

  1. 运算符的弹栈先输出空格后输出弹出的运算符,入栈先输出空格后再进行入栈操作。
  2. 左括号弹栈入栈不输出,也不输出空格。

附上代码:tmp指向中缀表达式的字符串

void nibolan(char *s, char *tmp)
{
	stack *st = NULL;
	st = stack_init(NUM);
	char *top = NULL;
	int ps = 0;
	int ptmp = 0;
	//中缀表达式遍历
	while(s[ps] != 0){
		//遇到数字直接输出
		if( is_digital(s[ps]) ){
			tmp[ptmp++] = s[ps++];
		}
		//遇到左括号压栈
		else if(s[ps] == '('){
			stack_push(st, &s[ps++]);
		}
		//遇到运算符
		else if( is_symbol(s[ps]) ){
				if( stack_top(st) > 0){
					while(1){
						top = (char *)get_top(st);
						if(top == NULL)
							break;
						//与栈顶符号优先级比较
						if( priority(s[ps]) < priority(*top) ){
							stack_pop(st);
							tmp[ptmp++] = ' ';
							tmp[ptmp++] = *top;
						}else
							break;	
					}
				}
			tmp[ptmp++] = ' ';
			stack_push(st, &s[ps++]);
		}
		//遇到右括号
		else if( s[ps] == ')'){
			while(1){
				top = (char *)get_top(st);
				if(top == NULL)
					perr_exit("表达式有误,漏了左括号\n");
				else if(*top == '('){
					stack_pop(st);
					ps++;
					break;
				}
				else{
					stack_pop(st);
					tmp[ptmp++] = ' ';
					tmp[ptmp++] = *top;
				}
			}
		}
	}
	//中缀表达式遍历结束
	while(1){
		top = get_top(st);
		if(top == NULL)
			break;
		stack_pop(st);
		tmp[ptmp++] = ' ';
		tmp[ptmp++] = *top;
	}
	tmp[ptmp] = 0;
}
计算机对后缀表达式的运算规则:
  1. 遇到数字压栈
  2. 遇到符号,栈中弹出右操作数和左操作数,运算后结果压入栈。
  3. 表达式遍历结束后,栈底的元素即为结果

代码细节部分

  1. 每个空格都作为一个操作符和运算符的结束标志
  2. 难点在于操作数的转换,即’1’‘2’'3’需转换为int类型123在入栈,需要定义一个二维数组对每个进行进行存放。

附上代码:s指向后缀表达式的字符串

int deal_nbl(char *s)
{
	int ret = 0;
	stack *st = NULL;
	st = stack_init(NUM);
	int R,L;
	char tmp[NUM][NUM] = {{0}};
	int t[NUM] = {0};
	int ptmp = 0;
	int pnum = 0;
	int ps = 0;
	//遍历逆波兰表达式
	while(s[ps] != 0){
		//每个操作数先经整形转换在入栈,空格为结束标志
		if( is_digital(s[ps]) ){
			while( 1 ){
				tmp[pnum][ptmp++] = s[ps++];
				if(s[ps] == ' '){
					//tmp[pnum][ptmp] = 0;
					t[pnum] = atoi(tmp[pnum]);
					stack_push(st, &t[pnum]);
					pnum++;
					ptmp = 0;
					ps++;
					break;
				}
			}
		}
		else if( is_symbol(s[ps]) ){
			R = *(int *)stack_pop(st);
			L = *(int *)stack_pop(st);
			ret = do_mul(s[ps], L, R);
			stack_push(st, &ret);
			ps += 2;
		}
	}
	ret = *(int *)stack_pop(st);
	return ret;
}

2.二叉树实现计算机(优化)

中序遍历可生成中缀表达式,后序遍历生成后缀表达式。
中缀表达式 1 + 2 * (66 / (2 * 3) + 7),后缀:1 2 66 2 3 * / 7 + * +
这里本人未作实验,大家可以动动手去试试。肯定比栈模型容易。
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值