【剑指Offer】高质量的代码(上)

数值的整数次方

题目描述

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

分析

这种题有一种很直观的求解方法,累乘,时间复杂度为O(n)。
使用递归求解,时间复杂度只有O(logn)。可以利用下面的公式来求解 a a a n n n次方:
a n = { a n / 2 × a n / 2 , if  n 为偶数 a ( n − 1 ) / 2 × a ( n − 1 ) / 2 × a , if  n 为奇数 , a^n= \begin{cases} a^{n/2}\times a^{n/2}, &\text{if $n$为偶数}\\ a^{(n-1)/2}\times a^{(n-1)/2}\times a, &\text{if $n$为奇数}, \end{cases} an={an/2×an/2,a(n1)/2×a(n1)/2×a,if n为偶数if n为奇数,

求解的时候要考虑周全,

  • 当指数为0时,不管底数是多少,都得1;
  • 当指数为1时,不管底数为多少,结果等于底数;
  • 当底数为0时,如果指数为正数,结果为0;
  • 当底数为0时,如果指数为非正数,则抛出异常;
  • 当指数为正数的时候,考虑奇偶两种情况,按上面的公式计算;
  • 当指数为负数的时候,即 a − n a^{-n} an,即计算 ( a n ) − 1 = 1 / a n (a^n)^{-1}=1/a^n (an)1=1/an

Java代码

public class Solution {
    public double Power(double base, int exponent) {
    	// 指数特殊情况
        if(exponent == 0)
            return 1;
        if(exponent == 1)
            return base;
        // 底数为0的情况
        if(base == 0 && exponent > 0)
            return 0;
        if(base == 0 && exponent <= 0)
            throw new RuntimeException();
            
        // 指数为负数和正数的情况要分开考虑
        int exp = exponent>0 ? exponent : -exponent;
        
        double res = Power(base, exp>>1);
        res *= res;
        if((exp & 1) == 1)
            res *= base;
        if(exponent < 0)
            res = 1 / res;
        return res;
    }
}

打印1到最大的n位数

题目描述

输入数字n,按顺序打印出从1到最大的n位十进制数。比如输入3,则打印出1、2、3一致到最大的3位数999。

分析

这道题看似简单,首先找到n位的最大的十进制数,例如n=5,最大的数字就是99999。然后利用循环从1开始打印出来。
但是要意识到这是一个大数问题。若这个n很大的时候,大到不管用int还是long都会溢出,就不能使用循环打印的方式求解。
使用字符串模拟数字,加上递归的方法,将问题转换成数字排列的解法。举例说明解法:

若n=3,可以先定义一个长度为4的字符串number[]

  • number[0]存放的是百位数;
  • number[1]存放的十位数;
  • number[2]存放的是个位数;
  • number[3]存放的是换行符’\n’。

先将百位数设为0,即number[0]=0,再令十位数为0,即number[1]=0,然后分别令个位(number[2])设为0,1,2,3,4,5,6,7,8,9,即可得到:
000 / 001 / 002 / …… / 008 / 009
循环结束后,再设置十位(number[1]=1),同理可得到:
010 / 011 / 012 / …… / 018 / 019
待得到 090 / 091 / 092 / …… / 098 / 099 的时候,再设置百位(number[0])为1。循环往复。最终即可打印出1 ~ 3位最大值999

其次,字符串输出为数字的形式,要注意高位为0不能输出,例如:001,如果直接输出不符合我们常规的做法,应该输出为1,即高位的0不输出,从不为0的最高位开始输出。还要注意不要把0打印出来。

Java代码

import java.util.*;
import java.util.regex.*;

public class Main{
  public static void main(String args[]){
    Scanner sc=new Scanner(System.in);
    while(sc.hasNext()){
    	int n = sc.nextInt();
    	Print1toMaxOfNDigits(n);
    }
    sc.close();
  }
  
  public static void Print1toMaxOfNDigits(int n) {
	  if(n<=0) // n一定是正整数
		  return;
	  // 字符数组number用来存放数值的各位数字
	  char[] number = new char[n];
	  // 每一位数字都是从0-9的循环
	  for(int i=0; i<10; i++) {
		  number[0] = (char)(i+48);
		  // 最高位number[0]固定之后,处理后面的低位数字
		  Print1ToMaxOfNDigitsRecursively(number, n, 0);
	  }
  }
  
  public static void Print1ToMaxOfNDigitsRecursively(char[] number, int length, int idx) {
	  // 如果已经处理到最后一位,则利用PrintNumber将字符数组以数字的格式输出
	  if(idx == length-1) {
		  PrintNumber(number);
		  return;
	  }
	  // 递归处理每一位,从0-9
	  for(int i=0; i<10; i++) {
		  number[idx+1] = (char)(i+48);
		  Print1ToMaxOfNDigitsRecursively(number, length, idx+1);
	  }
  }
  
  public static void PrintNumber(char[] number) {
	  int i=0;
	  while(i<number.length && number[i]=='0')
		  i++;
	  for(; i<number.length; i++)
		  System.out.print(number[i]);
	  System.out.println();
  }
}

调整数组顺序时奇数位于偶数前面

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

分析

因为要保证相对位置不变,所以要采用类似于稳定排序的方法
方法1
冒泡法
从前向后遍历,每次发现两个相邻的数满足“前面一个是偶数,后面一个奇数”的条件,就两两进行交换。遍历 n n n次后可保证最后 n n n个数字是偶数。由于之后奇数和偶数之间进行了交换,奇数和奇数或者偶数和偶数之间没有交换,所以其相对位置保持不变。

方法2
选择排序法
从第1 ( i = 1 ) (i=1) (i=1)个元素开始判断,如果array[ i i i]是偶数,则向后找到第一个奇数,假设是array[ j j j]。则将array[ j j j]取出,将 i i i ~ ( j − 1 ) (j-1) (j1)的元素依次向后移一位,然后将array[j]放到下标为 i i i的位置。

方法3
用空间换时间
新建一个数组保存奇数,然后将原数组中偶数依次排在后面(代码不展示)

Java代码

方法1

public class Solution {
    public void reOrderArray(int [] array) {
        for(int i=0; i<array.length; i++) {
			for(int j=0; j<array.length-1-i; j++) {
				if((array[j]&1)==0 && (array[j+1]&1)==1) {
					int temp = array[j];
					array[j] = array[j+1];
					array[j+1] = temp;
				}
			}
		}
    }
}

方法2

public class Solution {
    public void reOrderArray(int [] array) {
        for(int i=0; i<array.length; i++){
            if((array[i]&1)==1)
                continue;
            for(int j=i+1; j<array.length; j++){
                if((array[j]&1)==0)
                    continue;
                else{
                    int temp = array[i];
                    array[i] = array[j];
                    for(int k=j; k>i+1; k--){
                        array[k] = array[k-1];
                    }
                    array[i+1] = temp;
                    break;
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值