每日算法训练(持续更新)

博主从2020.5.9下定决心,每天两道算法题,写博客是为了有动力坚持下去,望各位博友监督与鼓励    (ง •_•)ง
2020.5.17 学校开始上课了,博主忙于课设,从今天开始每天一道
算法题来自微信公众号:数据结构和算法

1、最大质因数

13195的所有质因数为5、7、13和29。600851最大的质因数是多少?

分析:通过不断的递归调用,判断number是否为质数。

public class Algorithe01 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("600851的最大质因数:"+primeFactor(600851));
	}

	public static long primeFactor(long num) {
		if(num==1)
			return 1;
		for(int i=2;i<num;i++) {
			if(num%i==0) {
				return primeFactor(num/i);
			}
		}
		return num;
	}
}

2、最大回文乘积

回文数就是从前往后和从后往前读都一样的数。由两个2位数相乘得到的最大回文乘积是 9009 = 91 × 99。找出由两个3位数相乘得到的最大回文乘积。

分析:通过循环一个个判断。

public class Algorithe02 {

	public static void main(String[] args) {
		System.out.println("两个3位数相乘得到的最大回文乘积是"+palindrome());

	}
	public static long palindrome() {
		long num=0;
		for(int j=999;j>100;j--) {
			for(int i=999;i>100;i--) {
				long result=i*j;
				if(result>num &&palindrome(result))
					num=result;
			}
		}
		return num;
	}
	private static boolean palindrome(long result) {
		int count=0;
		long[] a=new long[10];
		while(result!=0) {
			a[count]=result%10;
			count++;
			result=result/10;
		}
		for(int i=0;i<=count/2;i++) {
			if(a[i]!=a[count-i-1])
				return false;
		}
		return true;
	}
}

3、最小倍数

2520是最小的能够被1到10整除的数。最小的能够被1到20整除的正数是多少

分析:其实就是求1到20的最小公倍数,讲道理直接写个for,计算1到n的乘积即可,这样未免太简单。这道算法题应该要展现整除的过程,令product=n*(n-1)可以减少循环次数

public class Algorithe03 {

	public static void main(String[] args) {
		System.out.println("求最小的能被1到n整除的数,请输入n:");
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int product=n*(n-1);
		int num = 0;
		for(int j=1;j<=Integer.MAX_VALUE;j++) {
			num=product*j;
			int count=0;
			for(int i=1;i<=n;i++) {
				if(num%i!=0) {
					break;
				}
				count++;
			}
			if(count==n) {
				break;
			}
		}
		System.out.println(num);
	
	}
}

4、第10001个素数

列出前6个素数,它们分别是2、3、5、7、11和13。我们可以看出,第6个素数是13。第10001个素数是多少?

分析:判断n是否为素数,根据定义直接判断从2到n-1是否存在约数即可

public class Algorithe04 {
	public static void main(String[] args) {
		System.out.println(prime(10001));
	}
	public static int prime(int num) {
		int i=0;
		for(int j=2;j<Integer.MAX_VALUE;j++) {
			if(isPrime(j)) {
				i++;
				if(i==num)
					return j;
			}
		}
		return -1;
	}
	private static boolean isPrime(int j) {
		if(j==2) {
			return true;
		}
		for(int i= 2;i<=j-1;i++) {
		    if(j%i== 0)
		       return false;
		}
		return true;
	}
}

5、连续数字最大乘积

在下面这个1000位正整数中,连续4个数字的最大乘积是
9 × 9 × 8 × 9 = 5832。
73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450
找出这个1000位正整数中乘积最大的连续13个数字。它们的乘积是多少?

分析:相邻的13个数相乘,直到循环完,然后返回最大的。注意的是如果乘积为0就跳出循环,charAt()取值,如0的ASCII码是48,减去48,就是对应的int值

public class Algorithe05 {
	public static void main(String[] args) {
		String num="73167176531330624919225119674426574742355349194934" + 
				"96983520312774506326239578318016984801869478851843" + 
				"85861560789112949495459501737958331952853208805511" + 
				"12540698747158523863050715693290963295227443043557" + 
				"66896648950445244523161731856403098711121722383113" + 
				"62229893423380308135336276614282806444486645238749" + 
				"30358907296290491560440772390713810515859307960866" + 
				"70172427121883998797908792274921901699720888093776" + 
				"65727333001053367881220235421809751254540594752243" + 
				"52584907711670556013604839586446706324415722155397" + 
				"53697817977846174064955149290862569321978468622482" + 
				"83972241375657056057490261407972968652414535100474" + 
				"82166370484403199890008895243450658541227588666881" + 
				"16427171479924442928230863465674813919123162824586" + 
				"17866458359124566529476545682848912883142607690042" + 
				"24219022671055626321111109370544217506941658960408" + 
				"07198403850962455444362981230987879927244284909188" + 
				"84580156166097919133875499200524063689912560717606" + 
				"05886116467109405077541002256983155200055935729725" + 
				"71636269561882670428252483600823257530420752963450";
		int max=0;
		for(int i=0;i<num.length()-13;i++) {
			int result=1;
			for(int j=i;j<i+13;j++) {
				int digit=num.charAt(j)-48;
				result*=digit;
				if(result==0)
					break;
			}
			if(result>max) {
				max=result;
			}
		}
		System.out.println("连续13个数字最大乘积是"+max);
	}
}

6、特殊毕达哥拉斯三元组

毕达哥拉斯三元组是三个自然数a < b < c组成的集合,并满足a2 + b2 = c2。例如,32 + 42 = 9 + 16 = 25 = 52。有且只有一个毕达哥拉斯三元组满足 a + b + c = 1000。求这个三元组的乘积abc。

分析:满足毕达哥拉斯定理,可以把它想象成为一个直角三角形的三条边,a是小的直角边,b是大的直角边,c是斜边。b如果等于a,那么c就是无理数了,相加不可能等于1000,所以b不能等于a。下面的循环要满足直角三角形的几个条件,1:直角边要小于斜边,2:两边之和大于第三边,3:两边之差小于第三边。如果查找到就直接返回。

public class Algorithe06 {

	public static void main(String[] args) {
		System.out.println(pythagorean(1000));

	}
	public static int pythagorean(int sum) {
		for(int a=1;a<sum/3;a++) {
			for(int b=a+1;b<sum/2;b++) {
				int c=sum-a-b;
				if(b<c &&(a*a+b*b==c*c)) {
					return a*b*c;
				}
			}
		}
		return -1;
	}
}

7、素数的和

所有小于10的素数的和是2 + 3 + 5 + 7 = 17。求所有小于两百万的素数的和。

分析:通过循环遍历,能提高算法效率的点有,素数除了2以外都是奇数。判断是否为素数时,可以从2到i的平方根之间循环,至于为什么这样简化,可以拿计算器试一试就明白了,这样极大缩短了运行时间

public class Algorithe07 {

	public static void main(String[] args) {
		long sum=2;
		for(int i=3;i<2000000;i+=2) {
			if(isPrime(i)) {
				sum+=i;
			}
		}
		System.out.println(sum);
	}

	private static  boolean isPrime(int i) {
		for(int j=2;j<=Math.sqrt(i);j++) {
			if(i%j==0) 
				return false;
		}
		return true;
	}

}

8、高度可约的三角形数

三角形数数列是通过逐个加上自然数来生成的。例如,第7个三角形数是 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28。三角形数数列的前十项分别是:
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, …
让我们列举出前七个三角形数的所有约数:
1: 1
3: 1,3
6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28
我们可以看出,28是第一个拥有超过5个约数的三角形数。
第一个拥有超过500个约数的三角形数是多少?

分析:列举三角形数,判断他的约数是否大于500,如果大于直接返回。注意下面的判断是否是平方数,如果是要减1,比如9的约数是1,3,9不是1,3,3,9。

public class Algorithe08 {

	public static void main(String[] args) {
		System.out.println(triangleNumber(500));
	}
	public static int triangleNumber(int number) {
		for(int i=1;i<Integer.MAX_VALUE;i++) {
			int count=0;
			int result=(1+i)*i/2;
			int prime=(int) Math.sqrt(result);
			for(int j=1;j<=prime;j++) {
				if(result%j==0)
					count++;
			}
			count= (prime*prime==result?2*count-1:2*count);
			if(count>number)
				return result;
		}
		return -1;
	}
}

9、硬币求和

英国的货币单位包括英镑£和便士p,在流通中的硬币一共有八种:
1p, 2p, 5p, 10p, 20p, 50p, £1 (100p), £2 (200p)
以下是组成£2的其中一种可行方式:
1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p
不限定使用的硬币数目,组成£2有多少种不同的方式?

分析:通过递归计算,只有相减等于0的时候sum才会加1,否则通过循环递归计算sum的值。

public class Algorithe09 {

	public static void main(String[] args) {
		System.out.println(coinSum(200, 0));
	}
	public static int coinSum(int totalMoney,int index) {
		int money[]= {1,2,5,10,20,50,100,200};
		int sum=0;
		for(int i=index;i<money.length;i++) {
			if(totalMoney-money[i]==0) {
				sum++;
				break;
			}
			if(totalMoney-money[i]>0)
				sum+=coinSum(totalMoney-money[i],i);
		}
		return sum;
	}
}

10、最长考拉兹序列

在正整数集上定义如下的迭代序列:
n → n/2 (若n为偶数)
n → 3n + 1 (若n为奇数)
从13开始应用上述规则,我们可以生成如下的序列:
13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
可以看出这个序列(从13开始到1结束)共有10项。尽管还没有被证明,但我们普遍认为,从任何数开始最终都能迭代至1(“考拉兹猜想”)。
从小于一百万的哪个数开始,能够生成最长的序列呢?
注: 序列开始生成后允许其中的项超过一百万。

分析:返回最长的序列即可。

public class Algorithe10 {

	public static void main(String[] args) {
		System.out.println(longestCollatz(1000000));
	}
	public static int longestCollatz(int num) {
		int max=0;
		int j=0;
		for(int i=2;i<num;i++) {
			int length=collatzLength(i);
			if(max<length) {
				max=length;
				j=i;
			}
		}
		return j;
	}
	private static int collatzLength(int num) {
		int i=1;
		while(num!=1) {
			if(num%2==0)
				num=num>>>1;
			else
				num=num*3+1;
			i++;
		}
		return i;
	}
}

11、幂的数字和

215 = 32768,而32768的各位数字之和是 3 + 2 + 7 + 6 + 8 = 26。
21000的各位数字之和是多少(2的1000次方)?

分析:我们知道2的64次方就已经很大了,那么2的1000次方就更大了,常规的算法是不行的,换个思路。2的1000次方,相当于把1往左移动1000位,然后转化为BIgInteger在计算各位数字之和。digit就是表示每个位置上的数字的字符串,遍历相加即可。

public class Algorithe11 {

	public static void main(String[] args) {
		System.out.println(powerDigit(1000));
	}
	public static int powerDigit(int max) {
		int sum=0;
		StringBuilder sb=new StringBuilder(max+1);
		sb.append(1);
		for(int j=0;j<max;j++) {
			sb.append(0);
		}
		BigInteger num=new BigInteger(sb.toString(),2);
		String digit=num.toString(10);
		for(int i=0;i<digit.length();i++) {
			sum+=digit.charAt(i)-48;
		}
		return sum;
	}
}

12、表达数字的英文字母计数

如果把1到5写成英文单词,分别是:one, two, three, four, five,这些单词一共用了3 + 3 + 5 + 4 + 4 = 19个字母。
如果把1到1000都写成英文单词,一共要用多少个字母?
注意: 不要算上空格和连字符。例如,342(three hundred and forty-two)包含23个字母,而115(one hundred and fifteen)包含20个字母。单词“and”的使用方式遵循英式英语的规则。

分析:主要就是找规律,然后总结,注意1000的时候不要忘了。

public class Algorithe12 {

	public static void main(String[] args) {
		System.out.println(NumberLetterCounts());
	}

	private static int NumberLetterCounts() {
		int sum=0;
		for(int i=1;i<21;i++) {
			sum+=LetterCountsOne(i);
		}
		for(int i=21;i<100;i++) {
			sum+=LetterCountsTwo(i/10)+LetterCountsTwo(i%10);
		}
		for(int i=100;i<1000;i++) {
			if(i%100<=20)
				sum+=LetterCountsOne(i/100) + (i%100==0?7:10)+LetterCountsOne(i%100);
			else
				sum+=LetterCountsOne(i/100) + (i%100==0?7:10)+LetterCountsTwo(i%100/10)+LetterCountsOne(i%10);
		}
		//one thousand
		sum+=11;
		return sum;
	}

	private static int LetterCountsTwo(int num) {
		/**
		 * one two three four five six seven eight nine ten
		 * eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty
		 * twenty-one twenty-two twenty-three twenty-four twenty-five twenty-six
		 * twenty-seven twenty-eight twenty-nine
		 * thirty forty fifty sixty seventy eighty ninety 
		 * one hundred
		 */
		if(num==1)
			return 3;
		if(num==2)
			return 3;
		if(num==3)
			return 5;
		if(num==4)
			return 4;
		if(num==5)
			return 4;
		if(num==6)
			return 3;
		if(num==7)
			return 5;
		if(num==8)
			return 5;
		if(num==9)
			return 4;
		if(num==10)
			return 3;
		if(num==11)
			return 6;
		if(num==12)
			return 6;
		if(num==13)
			return 8;
		if(num==14)
			return 8;
		if(num==15)
			return 7;
		if(num==16)
			return 7;
		if(num==17)
			return 9;
		if(num==18)
			return 8;
		if(num==19)
			return 8;
		if(num==20)
			return 6;
		return 0;
	}

	private static int LetterCountsOne(int num) {
		if(num==2)
			return 6;
		if(num==3)
			return 6;
		if(num==4)
			return 5;
		if(num==5)
			return 5;
		if(num==6)
			return 5;
		if(num==7)
			return 7;
		if(num==8)
			return 6;
		if(num==9)
			return 6;
		return 0;
	}

}


13、数星期日

下列信息是已知的,当然你也不妨自己再验证一下。
1900年1月1日是星期一。
三十天在九月中,
四六十一也相同。
剩下都是三十一,
除去二月不统一。
二十八天平常年,
多加一天在闰年。
闰年指的是能够被4整除却不能被100整除的年份,或者能够被400整除的年份。
在二十世纪(1901年1月1日到2000年12月31日)中,有多少个月的1号是星期天?

分析:这个虽然简单但有点麻烦,自己慢慢看,就不在介绍。

public class Algorithe13 {
	public static void main(String[] args) {
		System.out.println(countSundays());
	}
	public static int countSundays() {
		//1900年1月1日是星期一,那么7号就是星期日
		int sum=365-6;
		int count=0;
		for(int i=1901;i<=2000;i++) {
			for(int j=1;j<=12;j++) {
				sum+=daysOfYear(i,j);
				if(sum%7==0)
					count++;
			}
			sum+=30;//最后一个月只加到1号,年循环完要把剩余的30天加上
		}
		return count;
	}
	private static int daysOfYear(int year, int month) {
		if(month==1)
			return 1;
		return days(year,month-1)+daysOfYear(year,month-1);
	}
	private static int days(int year, int month) {
		if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12)
			return 31;
		if(month==4 || month==6 || month==9 || month==11)
			return 30;
		if(month==2 &&(year%4!=0 || year%400!=0))
			return 28;
		return 29;
	}
	
	
}

14、阶乘数字和

n! 的意思是 n × (n − 1) × … × 3 × 2 × 1
例如,10! = 10 × 9 × … × 3 × 2 × 1 = 3628800,所以10!的各位数字和是 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27。
求出100!的各位数字和。

分析:int和long都不能表示,所以要使用BinInteger,也很简单就不在介绍。

public class Algorithe14 {
	public static void main(String[] args) {
		System.out.println(factorialDigitSum(100));
	}
	public static int factorialDigitSum(int num) {
		int i=1;
		BigInteger bigInteger=new BigInteger("1");
		while(i<=num) {
			bigInteger=bigInteger.multiply(new BigInteger(i+""));
			i++;
		}
		String numString=bigInteger.toString();
		int sum=0;
		for(int j=0;j<numString.length();j++) {
			sum+=Character.digit(numString.charAt(j), 10);
		}
		return sum;
	}
}

15、亲和数

记d(n)为n的所有真因数(小于n且整除n的正整数)之和。
如果d(a) = b且d(b) = a,且a ≠ b,那么a和b构成一个亲和数对,a和b被称为亲和数。
例如,220的真因数包括1、2、4、5、10、11、20、22、44、55和100,因此d(220) = 284;而284的真因数包括1、2、4、71和142,因此d(284) = 220。
求所有小于10000的亲和数的和。

分析:先判断是否是亲和数,然后相加,但要防止加两次。

public class Algorithe15 {

	public static void main(String[] args) {
		System.out.println(amicableNumberSum(10000));
	}

	public static int amicableNumberSum(int num) {
		int sum=0;
		for(int i=1;i<num;i++) {
			int amicable=amicableNumbers(i);
			if(amicable>i) {
				sum+=i;
				sum+=amicable;
			}
		}
		return sum;
	}

	private static int amicableNumbers(int num) {
		int sum1=0;
		for(int i=1;i<num;i++) {
			if(num%i==0)
				sum1+=i;
		}
		int sum2=sum1;
		sum1=0;
		for(int i=1;i<sum2;i++) {
			if(sum2%i==0)
				sum1+=i;
		}
		if(num==sum1)
			return sum2;
		return -1;
	}

}


16、全数字的倍数

将192分别与1、2、3相乘:得到192,384,576
连接这些乘积,我们得到一个1至9全数字的数192384576。我们称192384576为192和(1,2,3)的连接乘积。同样地,将9分别与1、2、3、4、5相乘,得到1至9全数字的数918273645,即是9和(1,2,3,4,5)的连接乘积。对于n > 1,所有某个整数和(1,2, … ,n)的连接乘积所构成的数中,最大的1至9全数字的数是多少?

分析:判断是否是全数字,然后返回最大的。

public class Algorithe16 {

	public static void main(String[] args) {
		System.out.println(pandigitalMultiples());
	}

	public static int pandigitalMultiples() {
		int max=0; //因为n>1,所以i<10000
		for(int i=1;i<10000;i++) {
			String product="";
			for(int j=1;j<10;j++) {
				product=product+(i*j);
				if(product.length()>9)
					break;
				if(repetition(product))
					continue;
				if(product.length()==9) {
					if(max<Integer.parseInt(product)) {
						max=Integer.parseInt(product);
					}
				}
			}
		}
		return max;
	}

	private static boolean repetition(String i) {
		List<String> list=new ArrayList<String>();
		for(int j=0;j<i.length();j++) {
			if(i.charAt(j)=='0' || list.contains(i.charAt(j)+""))
				return true;
			list.add(i.charAt(j)+"");
		}
		return false;
	}

}


17、字典序排列

排列指的是将一组物体进行有顺序的放置。例如,3124是数字1、2、3、4的一个排列。如果把所有排列按照数字大小或字母先后进行排序,我们称之为字典序排列。0、1、2的字典序排列是:
012 021 102 120 201 210
数字0、1、2、3、4、5、6、7、8、9的字典序排列中第一百万位的排列是什么?

分析:这里用到阶乘的概念,比如n可以组合成n*(n-1)*(n-2)**21中方式。只需要计算(n-1)的阶乘即可,就可以知道除了n有多少种形式,然后把i放到数的最前面,i是list中第几个元素,list的初始元素相当于已经排序好的,然后在通过不断的递归即可求出。

public class Algorithe17 {

	public static void main(String[] args) {
		List<Integer> list=new ArrayList<Integer>();
		for(int i=0;i<10;i++) {
			list.add(i);
		}
		System.out.println(lexicographicPermutations(10, 999999, list));
	}
	public static String lexicographicPermutations(int num,int max,List<Integer> list) {
		if(num>0) {
			int prexLeicographicNum=1;
			if(num!=1)
				prexLeicographicNum=recursion(num-1);
			int quotients=max/prexLeicographicNum;
			int mod=max%prexLeicographicNum;
			int i=list.remove(quotients);
			return i+""+lexicographicPermutations(num-1, mod, list);
		}
		return "";
	}
	private static int recursion(int num) {
		if(num==1)
			return 1;
		return num*recursion(num-1);
	}
	
}

18、一千位斐波那契数

斐波那契数列是按如下递归关系定义的数列:
F1 = 1 F2 = 1
Fn = Fn−1 + Fn−2
因此斐波那契数列的前12项分别是:
F1 = 1
F2 = 1
F3 = 2
F4 = 3
F5 = 5
F6 = 8
F7 = 13
F8 = 21
F9 = 34
F10 = 55
F11 = 89
F12 = 144
第一个有三位数字的项是第12项F12。
在斐波那契数列中,第一个有1000位数字的是第几项?

分析:用BigInteger计算即可。

public class Algorithe18 {

	public static void main(String[] args) {
		System.out.println(digitFibonacciNumber());
	}
	public static String digitFibonacciNumber() {
		BigInteger first=new BigInteger("1");
		BigInteger second=new BigInteger("1");
		for(int i=3;i<Integer.MAX_VALUE;i++) {
			BigInteger corren=first.add(second);
			if(corren.toString().length()>=1000)
				return i+"";
			first=second;
			second=corren;
		}
		return "";
	}
}

19、倒数的循环节

单位分数指分子为1的分数。分母为2至10的单位分数的十进制表示如下所示:
1/2= 0.5
1/3= 0.(3)
1/4= 0.25
1/5= 0.2
1/6= 0.1(6)
1/7= 0.(142857)
1/8= 0.125
1/9= 0.(1)
1/10= 0.1
这里0.1(6)表示0.166666…,括号内表示有一位循环节。可以看出,1/7有六位循环节。
找出正整数d < 1000,其倒数的十进制表示小数部分有最长的循环节。

分析:只需要求出循环节的长度即可,任何分数都可以写成有限小数或者无限循环小数,如果是有限的好说,如果是无限的,通过不断的求余运算,计算出mod,然后直到找到第一个出现重复的即可。

public class Algorithe19 {

	public static void main(String[] args) {
		System.out.println(reciprocalCycles());
	}

	private static int reciprocalCycles() {
		int max=0;
		int target=0;
		for(int i=1;i<1000;i++) {
			int circulationCount=circulation(1,i);
			if(circulationCount>max) {
				target=i;
				max=circulationCount;
			}
		}
		return target;
	}

	private static int circulation(int dividend, int divisor) {
		List quoientList=new ArrayList<>();
		List moList=new ArrayList<>();
		int index=-1;
		while(true) {
			quoientList.add(dividend/divisor);
			int mod=dividend%divisor;
			if(mod==0)
				break;
			index=moList.indexOf(mod);
			if(index>=0)
				break;
			else
				moList.add(mod);
			dividend=mod*10;
		}
		if(index>=0)
			return quoientList.size()-(index+1);
		else
			return 0;
	}
	
}

更新至2020.5.20      23:45
PS:最近比较忙,停更,更新日期待定o(╥﹏╥)o

你可能感兴趣的:(数据结构)