1. 概述
所谓的算法,就是计算方法即在进行某种运算时所使用的方式方法,在计算机中的算法指的是,在计算机中进行运算时提前为计算机设定的计算方法。
2. 算法的两个关键指标
2.1 运行时间
2.2 内存消耗
3. 预备知识
3.1 异或运算符^
3.1 特点
异或运算的特点是,一个数据A异对一个数据B异或两次的结果是数据A
例如:
System.out.println(3 ^ 4 ^ 4);
4. 经典的算法题
4.1 题目1
题目:现有0-99,共计100个整数,各不相同,将这100个数据放入一个数组中,然后,在这个数组中添加一个0-99的任意一个整数数值(唯一重复的数值),把这101个数据随机排列,然后将这个重复的数字找出来。
public static void test01(){
// 1. 定义数组
int[] arr = new int[101];
for(int index = 0 ; index < arr.length ; index ++){
arr[index] = index;
}
// 随机添加一个重复的数据
arr[100] = new Random().nextInt(99) + 1;
printArray(arr);
System.out.println();
System.out.println("---------------------------------------------");
// 2. 将数组随机排列
/*
异或方式做交换效率较低
Long start = System.nanoTime();
for(int i = 0 ; i < 1000 ; i++){
int index1 = new Random().nextInt(101);
int index2 = new Random().nextInt(100) + 1;
arr[index1] = arr[index1] ^ arr[index2];
arr[index2] = arr[index1] ^ arr[index1];
arr[index1] = arr[index1] ^ arr[index2];
}
Long end = System.nanoTime();
System.out.println((end - start)); // 3568313
*/
Long start1 = System.nanoTime();
for(int i = 0 ; i < 1000 ; i++){
int index1 = new Random().nextInt(101);
int index2 = new Random().nextInt(101);
int temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
Long end1 = System.nanoTime();
System.out.println((end1 - start1)); // 629969
printArray(arr);
System.out.println();
// 3. 找出重复的数字
// 方式一:循环遍历
// 缺点:思路简单,时间消耗较大
loop:for(int i = 0 ; i < arr.length; i++){
for(int j = i + 1 ; j < arr.length ; j++){
if(arr[i] == arr[j]){
System.out.println("重复元素是:" + arr[i]);
break loop;
}
}
}
// 方式二:累加求差
// 思路:因为数组中的数值为101个整数,且随机之前,前100个为不重复的数据,所以两者求和之差即为重复的数据
// 缺点:有数据溢出的风险
int sum = 0 ;
for(int i = 0 ; i < arr.length ; i ++){
sum += arr[i];
}
for(int i = 0 ; i < 100 ; i ++){
sum -= i;
}
System.out.println("重复元素是:" + sum);
// 方式三:利用异或的特性
// 思路:拿数组的0号元素和后面的所有元素进行异或,然后用异或的结果再和0-99的数据做异或,最终保留的结果就是重复的元素
for(int i = 1 ; i < arr.length ; i++){
arr[0] = arr[0] ^ arr[i];
}
for(int i = 0 ; i < 100 ; i ++){
arr[0] = arr[0] ^ i;
}
System.out.println("重复元素是:" + arr[0]);
}
/**
* 打印数组
*/
private static void printArray(int[] arr) {
int count = 0;
// 遍历数组
for(int i = 0 ; i < arr.length ; i ++){
if(++count%10 == 0){
System.out.println(arr[i] + "\t");
continue;
}
System.out.print(arr[i] + "\t");
}
}
4.2 题目二
题目:现有0-99,共计100个整数,各不相同,将这100个整数放到一个数组中随机排列,将其中的任意一个数字替换成0-99另一个数(唯一重复的数字)
问题:将这个重复数字找出来
public static void test02(){
int[] arr = new int[100];
for(int i = 0 ; i < arr.length ; i++){
arr[i] = i;
}
// 设置重复数据
int i = new Random().nextInt(100);
int j = new Random().nextInt(100);
while( i == j){
i = new Random().nextInt(100);
j = new Random().nextInt(100);
}
arr[i] = arr[j];
System.out.println("重复的元素是:" + arr[i]);
// 方式一:循环遍历
loop:for(int index = 0 ; index < arr.length ; index ++){
for(int inner = index + 1 ; inner < arr.length ; inner ++){
if(arr[index] == arr[inner]){
System.out.println("重复元素是:" + arr[index]);
break loop;
}
}
}
// 方式二:新建一个数组来实现,用空间换时间
// 思路:新建一个数组,让其长度等于旧的数组,并让其值都等于0,然后,拿旧数组中的值作为新数组的索引,并将该索引处的值加1
int[] newArr = new int[arr.length];
for(int index = 0 ; index < arr.length ; index ++){
newArr[arr[index]] ++;
if(newArr[arr[index]] == 2){
System.out.println("重复元素是:" + arr[index]);
break;
}
}
}
4.3 题目三
题目:把两个int类型的变量交换
public static void test03(){
int a = 10;
int b = 20;
// 方式一:临时变量法
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a + " b = " + b);
// 方式二:异或方法(时间换空间)
a = a ^ b; // a = a ^ b
b = a ^ b; // b = a ^ b = a ^ b ^ b = a
a = a ^ b; // a = a ^ b = a ^ b ^ a
System.out.println("a = " + a + " b = " + b);
// 方式三:加减法
a = a + b;
b = a - b;
a = a - b;
System.out.println("a = " + a + " b = " + b);
}
注意:这里用异或方式,所耗的时间较多,但是内存消耗比较小。
4.4 裴波那契数列问题
所谓的裴波那契数列指的是这样的一个数列:1、1、2、3、5、8、13、21、34、。。。。。。
求该数列中第30个数据的值。
规律:
从第三个元素开始,每一个数据是前两个数据的和
第一个和第二个数据的值是1
4.4.1 递归算法实现
public static void test04(){
System.out.println(getNum(30));
}
// location表示第几个元素
private static int getNum(int location){
if(location == 1 || location == 2){
return 1;
}
return getNum(location -1 ) + getNum(location - 2);
}
注意:递归算法可以实现,但是,效率太低,因为方法的调用次数过多,会有很多重复调用的情况出现
4.4.2 备忘录算法
使用递归算法,会出现很多相同参数的调用,这样会严重影响性能,为此,我们可以使用缓存,将相同参数的结果缓存在一个hashMap中,这种暂时存储的方式就叫做备忘录算法实现。
public static void test05(){
System.out.println(getNumByHash(30));
}
private static HashMap<Integer,Integer> hm = new HashMap<Integer,Integer>();
private static int getNumByHash(int location){
if(location == 1 || location == 2){
return 1;
}
if(hm.containsKey(location)){
return hm.get(location);
}else {
int value = getNumByHash(location - 1) + getNumByHash(location - 2);
hm.put(location, value);
return value;
}
}
4.4.3 动态规划算法
通过分析,我们知道从第三个元素开始,每一个数据是前两个数据的和,我们拿这个和和它前面的数据,又可以得到一个新的数据。
public static void test06(){
System.out.println(getNum5(30));
}
public static int getNum5(int location){
if(location == 1 || location == 2){
return 1;
}
int a = 1;
int b = 1;
int temp = 0 ;
for(int i = 3 ; i <= location ; i++){
temp = a + b;
a = b;
b = temp;
}
return temp;
}