就业培训学习记录-day009

课堂任务

方法

在前面使用的 System.out.println(),具体是什么呢?这句话的意思是调用系统类 System 中的标准输出对象 out 中的方法 println()。方法是语句的集合,它们在一起执行一个功能。

  • 方法是解决一类问题的步骤的有序组合
  • 方法包含于类或对象中
  • 方法在程序中被创建,在其他地方被引用

方法的优点

  1. 使程序变得更简短而清晰。
  2. 有利于程序维护。
  3. 可以提高程序开发的效率。
  4. 提高了代码的重用性。

方法的命名规则

  1. 方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。
  2. 下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。一个典型的模式是:test_,例如 testPop_emptyStack。

方法的定义

一般情况下,定义一个方法包含以下语法:

修饰符 返回值类型 方法名(参数类型 参数名){
    ...
    方法体
    ...
    return 返回值;
}

方法包含一个方法头和一个方法体。下面是一个方法的所有部分:

  • 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
  • 返回值类型:方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
  • 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
  • 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
  • 方法体:方法体包含具体的语句,定义该方法的功能。

无参方法

public class Run {
	public static void main(String[] args) {
		System.out.println("before say hello");
		sayHello();
		System.out.println("after say hello");
	}

	public static void sayHello() {
		System.out.println("Hello");
	}
}

运行结果如下,方法在被调用时,会先把方法内的流程执行完毕,然后再回到被调用处,接着运行后面的代码。

有参方法

public class Run {
	public static void main(String[] args) {
		System.out.println("before say something");
		say("有参方法");
		System.out.println("after say someting");
	}

	public static void say(String content) {
		System.out.println(content);
	}
}

也可以这样写:

public class Run {
	public static void main(String[] args) {
		System.out.println("before say something");
		String word = "有参方法"; // 先定义一个字符串,并赋值
		say(word); // 将刚刚定义的字符串传到say方法中
		System.out.println("after say someting");
	}

	public static void say(String content) {
		System.out.println(content);
	}
}

方法返回值

public class Run {
	public static void main(String[] args) {
		int numA = 6, numB = 8, numC;
		numC = add(numA, numB); // 用int型的numC来接受add方法的返回值
		System.out.println("numC="+numC);
	}

	public static int add(int num1, int num2) { // 第一个int代表返回值的类型是int,当返回值类型是void时,表示没有返回值
		return num1 + num2;
	}
}

方法重载

上面使用的add方法仅仅适用于int型数据。但如果你想得到两个浮点类型数据的和呢?解决方法是创建另一个有相同名字但参数不同的方法,如下面代码所示:

public class Run {
	public static void main(String[] args) {
		int numA = 6, numB = 8, numC;
		double numD = 3.14, numE = 6.78, numF;
		numC = add(numA, numB); // 用int型的numC来接受add方法的返回值
		System.out.println("numC="+numC);
		numF = add(numD, numE); // 用double型的numF来接受add方法的返回值
		System.out.println("numF="+numF);
	}

	public static int add(int num1, int num2) {
		return num1 + num2;
	}
	public static double add(double num1, double num2) {
		return num1 + num2;
	}
}

如果调用add方法时传递的是int型参数,则int型参数的addd方法就会被调用;如果传递的是double型参数,则double类型的add方法体会被调用,这叫做方法重载;就是说一个类的两个方法拥有相同的名字,但是有不同的参数列表。Java编译器根据方法签名判断哪个方法应该被调用。方法重载可以让程序更清晰易读。执行密切相关任务的方法应该使用相同的名字。重载的方法必须拥有不同的参数列表。不能仅仅依据修饰符或者返回类型的不同来重载方法。

数组

Java 提供的数组是用来存储固定大小的同类型元素。声明一个数组变量,如 arr[5] 来代替直接声明 5 个独立变量 arr0,arr1,....,arr4。想要获取数组中的元素值,可以通过元素的下标来获取,数组的下标是从0开始的。

arr[5] 33 21 18 42 86
下标 0 1 2 3 4

创建数组

一般分为动态初始化和静态初始化。

// 动态初始化,新建int数组,长度为5,int类型默认值为0
int[] arr1 = new int[5];
// 静态初始化1
int[] arr2 = {1,2,3,4,5};
// 静态初始化2
int[] arr3 = new int[]{1,2,3,4,5};

通过下标访问数组元素

import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		char[] ary = new char[5];
		ary[0]='h'; // 通过下标访问数组元素
		ary[1]='e';
		ary[2]='l';
		ary[3]='l';
		ary[4]='o';
		// 遍历数组
		for (int i = 0; i < 5; i++) {
			System.out.println("字符数组ary的第"+(i+1)+"个元素是"+ary[i]);
		}
		System.out.println(Arrays.toString(ary));
	}
}

遍历数组

  • 数组的length属性是数组的长度
  • 数组一旦创建,长度不可变
  • 允许长度为0的数组
public class Run {
	public static void main(String[] args) {
		int[] ary = {1,2,3,4,5};
		// 遍历数组
		for (int i = 0; i < ary.length; i++) {
			System.out.println(ary[i]);
		}
	}
}

数组工具类Arrays

Arrays类位于 java.util 包中,主要包含了操纵数组的各种方法。使用时需要导包import java.util.Arrays;

  1. Arrays.toString(Object[] array)
    功能:返回数组的字符串形式
import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int[] arr = {1,3,5,7,9};
		char[] arr2 = {'h','e','l','l','o'};
		System.out.println(Arrays.toString(arr));
		// 结果为:[1, 3, 5, 7, 9]
		System.out.println(Arrays.toString(arr2));
		// 结果为:[h, e, l, l, o]
	}
}
  1. Array.sort(Object[] array)
    功能:对数组按照升序排序
import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int[] arr = {10,3,25,-8,1};
		System.out.println("排序前:"+Arrays.toString(arr));
		// 排序前:[10, 3, 25, -8, 1]
		Arrays.sort(arr);
		System.out.println("排序后:"+Arrays.toString(arr));
		// 排序后:[-8, 1, 3, 10, 25]
	}
}
  1. Arrays.copyOf(Object[] array, int to_index)
    返回一个数组,这个数组就等于数组 array 的前 to_index 个数,也就是 array[0] ~ array[to_index - 1] 。
import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int[] arr = {4,6,8,1,3};
		int[] arr2 = Arrays.copyOf(arr, 3);
		System.out.println(Arrays.toString(arr2)); // [4, 6, 8]
	}
}

数组常见操作

  1. 生成随机数组(值的范围在[1,100],包含边界)
    Math.random() 方法用于返回一个随机数,随机数范围为 0.0 <= Math.random < 1.0。
import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int arrSize = 10; // 指定数组长度
		int[] arr = getRandomArray(arrSize); // 用数组arr接收方法返回的数组
		System.out.println(Arrays.toString(arr));
	}

	public static int[] getRandomArray(int size) {
		int[] arr = new int[size]; // 将传入的参数指定为数组的长度
		for (int i = 0; i < arr.length; i++) {
			// Math.random()生成的随机数范围是[0.0,1.0),乘100再加1,然后取整数部分,即是[1,100]
			arr[i] = (int) (Math.random() * 100) + 1;
		}
		return arr;
	}
}
  1. 数组求和
public class Run {
	public static void main(String[] args) {
		int[] arr = {4,6,8,1,3};
		System.out.println(getSum(arr)); // 21
	}

	public static int getSum(int[] arr) {
		int res = -1;
		for (int i = 0; i < arr.length; i++) {
			res += arr[i];
		}
		return res;
	}
}
  1. 数组求最大、最小值
    方法1:一个个比较,然后取值
public class Run {
	public static void main(String[] args) {
		int[] arr = {4,6,8,1,3};
		System.out.println("数组最大值是"+getMax(arr)); // 8
		System.out.println("数组最小值是"+getMin(arr)); // 1
	}
	
	public static int getMax(int[] arr) {
		int max = arr[0]; // 最大值初始化为数组第一个元素
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] > max) {
				// 如果某个元素比max还大,那么该元素就是新的最大值
				max = arr[i];
			}
		}
		return max;
	}
	
	public static int getMin(int[] arr) {
		int min = arr[0]; // 最小值初始化为数组第一个元素
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] < min) {
				// 如果某个元素比min还小,那么该元素就是新的最小值
				min = arr[i];
			}
		}
		return min;
	}
}

方法2:先排序,然后取值

import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int[] arr = {4,6,8,1,3};
		Arrays.sort(arr);
		System.out.println("数组最大值是"+arr[arr.length-1]); // 8
		System.out.println("数组最小值是"+arr[0]); // 1
	}
}
  1. 数组求平均值
public class Run {
	public static void main(String[] args) {
		int[] arr = {4,6,8,1,3};
		double avg = getSum(arr) / arr.length; // 21 ÷ 5 = 4.0
		double avg1 = getSum(arr) * 1.0 / arr.length; // 21.0 ÷ 5 = 4.2
		System.out.println("数组平均值是"+avg1); // 4.2
	}
	
	public static int getSum(int[] arr) {
		int res = -1;
		for (int i = 0; i < arr.length; i++) {
			res += arr[i];
		}
		return res;
	}
}
  1. 数组去重
    去除数组中的重复元素,例如:数组 [0,1,1,2,2,3,3,4,4,0] 去重后数组变为 [0,1,2,3,4] 。
import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int[] arr = {0, 1, 1, 2, 2, 3, 3, 4, 4, 0};
		arr = unique(arr);
		System.out.println(Arrays.toString(arr));
	}

	public static int[] unique(int[] arr) {
		// 最好的情况是,所有的元素都不重复,新数组(要返回的数组)与原数组长度相同
		int[] temp = new int[arr.length];
		// 默认第一个元素不重复
		temp[0] = arr[0];
		// 记录不重复数据的个数
		int count = 1;
		// 遍历原数组中的每一项,在此基础上用遍历的第i项去遍历新数组
		for (int i = 0; i < arr.length; i++) {
			// 标记,表示是否在新数组中找到重复的元素
			boolean flag = false;
			for (int j = 0; j < temp.length; j++) {
				if (arr[i] == temp[j]) {
					// 如果元素重复,则更改标记
					flag = true;
					break;
				}
			}
			// 如果没有元素重复,则往新数组中添加
			if (!flag) {
				temp[count++] = arr[i];
			}
		}
		// 如果不重复数据的个数与原数组长度不同,即原数组中存在重复元素,则缩短新数组的长度
		if (count != arr.length) {
			temp = Arrays.copyOf(temp, count);
		}
		return temp;
	}
}
  1. 删除数组中第n个元素
    例如数组{1,2,3,4,5},删除位置为2的元素后,数组变为
import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int[] arr = {1, 2, 3, 4, 5};
		arr = remove(arr, 2);
		System.out.println(Arrays.toString(arr));
	}

	public static int[] remove(int[] arr, int index) {
		// 如果index范围非法,则返回原数组
		if (index < 0 || index > arr.length) {
			return arr;
		}
		// 创建一个数组,长度为原数组长度-1
		int[] temp = new int[arr.length - 1];
		// 选择性复制元素到新数组中
		for (int i = 0; i < arr.length - 1; i++) {
			if (i < index) {
				temp[i] = arr[i];
			} else {
				temp[i] = arr[i + 1];
			}
		}
		return temp;
	}
}
  1. 往数组中第n个位置新增元素
    例如数组{1,2,3,4,5},往位置2添加666,数组变为
import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int[] arr = {1, 2, 3, 4, 5};
		arr = add(arr, 2, 666);
		System.out.println(Arrays.toString(arr));
	}

	public static int[] add(int[] arr, int index, int element) {
		// 如果index范围非法,则返回原数组
		if (index < 0 || index > arr.length) {
			return arr;
		}
		// 创建一个数组,长度为原数组长度+1
		int[] temp = new int[arr.length + 1];
		// 选择性复制元素到新数组中
		for (int i = 0; i < arr.length + 1; i++) {
			if (i < index) {
				temp[i] = arr[i];
			} else if (i == index) {
				temp[i] = element;
			} else {
				temp[i] = arr[i - 1];
			}
		}
		return temp;
	}
}

递归

递归指的是在函数的定义中使用函数自身的方法。

举个例子:
从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是:“从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是:‘从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是:……’”
 
再举个例子:
假设你在一个电影院,你想知道自己坐在哪一排,但是前面人很多,你懒得去数了。于是你问前一排的人「你坐在哪一排?」,这样前面的人 (代号 A) 回答你以后,你就知道自己在哪一排了,只要把 A 的答案加一,就是自己所在的排了。不料 A 和你一样懒,他也不想数,于是他也问他前面的人 B「你坐在哪一排?」,这样 A 可以用和你一模一样的步骤知道自己所在的排。然后 B 也如法炮制,直到他们这一串人问到了最前面的一排(或者说问到了知道自己是哪一排的人,预示着调用结束),第一排的人告诉问问题的人「我在第一排」,最后大家就都知道自己在哪一排了。

递归的条件

可以通过递归调用来缩小问题规模,且新问题与原问题有着相同的形式(自身调用)。存在一种情况,可以使递归在该情况下退出(递归出口)。

递归三要素

  • 一定有可以退出程序的情况(不能无限递归)
  • 总是在尝试将一个问题化简到更小的规模
  • 父问题与子问题不能有依赖的部分

  1. 阶乘问题
    计算 n 的阶乘,n < 0时返回-1。
public class Run {
	public static void main(String[] args) {
		int n = 4; // 计算n的阶乘,这里设置为4
		System.out.println(fx(n)); // 4!=4×3×2×1=24
	}

	public static int fx(int n) {
		if (n < 0) {
			// 递归边界
			return -1;
		} else if (n == 0) {
			// 递归边界
			return 1;
		} else {
			// 递归,函数自己调用自己
			return n * fx(n-1);
		}
	}
}
  1. 斐波那契数列
    计算斐波那契数列第n项的结果。

斐波那契数列的递推公式为:
F(0) = F(1) = 1        0<=n<=1
F(n) = F(n-1) + F(n-2)        n>=2

public class Run {
	public static void main(String[] args) {
		int n = 4;
		System.out.println(Fibonacci(n)); // 5
	}

	public static int Fibonacci(int n) {
		if (n < 0) {
			// 递归边界
			return -1;
		} else if (n == 0 || n == 1) {
			// 递归边界
			return 1;
		} else {
			// 递推公式
			return Fibonacci(n-1) + Fibonacci(n-2);
		}
	}
}

排序

参考十大经典排序算法(动图演示)

使用Arrays.sort()

import java.util.Arrays;
public class Run {
	public static void main(String[] args) {
		int[] arr = {34,53,12,232,1,20,10,-6,-8,32};
		System.out.println("排序前:"+Arrays.toString(arr));
		// 排序前:[34, 53, 12, 232, 1, 20, 10, -6, -8, 32]
		Arrays.sort(arr);
		System.out.println("排序后:"+Arrays.toString(arr));
		// 排序后:[-8, -6, 1, 10, 12, 20, 32, 34, 53, 232]
	}
}

冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

算法描述

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  3. 针对所有的元素重复以上的步骤,除了最后一个;
  4. 重复步骤1~3,直到排序完成。

动图演示

代码实现

public static int[] bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = 0; j < arr.length - 1 - i; j++) {
            // 相邻元素两两对比
            if (arr[j] > arr[j+1]) {
                // 元素交换
                int temp = arr[j+1];
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}

选择排序

选择排序(Selection Sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

算法描述
n 个记录的直接选择排序可经过 n-1 趟直接选择排序得到有序结果。具体算法描述如下:

  1. 初始状态:无序区为 R[1..n] ,有序区为空;
  2. 第 i 趟排序 (i=1,2,3…n-1) 开始时,当前有序区和无序区分别为 R[1..i-1] 和 R(i..n)。该趟排序从当前无序区中,选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使 R[1..i] 和 R[i+1..n) 分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
  3. n-1 趟结束,数组有序化了。

动图演示

代码实现

public static int[] selectionSort(int[] arr) {
    int minIndex, temp;
    for (int i = 0; i < arr.length - 1; i++) {
        minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
            // 寻找最小的数
            if (arr[j] < arr[minIndex]) {
                // 将最小数的索引保存
                minIndex = j;
            }
        }
        // 元素交换
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}

算法分析
表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。

插入排序

插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

算法描述
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 将新元素插入到该位置后;
  6. 重复步骤2~5。

动图演示

代码实现

public static int[] insertionSort(int[] arr) {
	// current代表当前(待排序)项的值,preIndex代表在当前项之前的某个下标
	int current, preIndex;
	for (int i = 1; i < arr.length; i++) {
		// 循环开始时,默认第0项是已经排好的,需要排的是第1项,所以i初值为1
		preIndex = i - 1;
		current = arr[i];
		// 当前项的前一项大于当前项时,交换这两项的位置
		while (preIndex >= 0 && arr[preIndex] > current) {
			arr[preIndex + 1] = arr[preIndex];
			preIndex--;
		}
		// 交换完毕或者没交换,代表当前项之前都是有序的,即将插入的值不会破坏序列
		arr[preIndex + 1] = current;
	}
	return arr;
}

算法分析
插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

快速排序

参考快速排序

查找

顺序查找

顺序查找适合于存储结构为顺序存储或链接存储的线性表。顺序查找的时间复杂度为O(n)。

基本思想
顺序查找也称为线形查找,属于无序查找算法。从数据结构线形表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值 key 相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于 key 的结点,表示查找失败。

代码实现

public static int sequenceSearch(int[] arr, int key) {
	for (int i = 0; i < arr.length; i++) {
		// 如果找到,则返回所在下标
		if (arr[i] == key) {
			return i;
		}
	}
	// 如果没找到,则返回-1
	return -1;
}

二分查找

参考算法——二分法查找(binarySearch)
二分法查找,也称为折半法,是一种在有序数组中查找特定元素的搜索算法,时间复杂度O(logn)。

算法描述
二分法查找的思路如下:

  1. 首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
  2. 如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤 1 的操作。
  3. 如果某一步数组为空,则表示找不到目标元素。

代码实现

public static int binarySearch(int[] arr, int key, int low, int high) {
	// 中间值
	int mid = (low + high) / 2;
	if (key == arr[mid]) {
		// 如果刚好在中间位置,则返回所在下标
		return mid;
	}
	if (key < arr[mid]) {
		// 如果大于,则key只可能在后半部分,递归调用
		return binarySearch(arr, key, low, mid - 1);
	} 
	if (key > arr[mid]) {
		// 如果大于,则key只可能在后半部分,递归调用
		return binarySearch(arr, key, mid + 1, high);
	}
	// 如果没找到,则返回-1
	return -1;
}

课后任务

编码实现一个猜字符的游戏

要求如下:

  1. 系统预先生成好一组(5个)不重复的小写英文字符,游戏开始,初始分值100分
  2. 用户输入自己猜测的字符数据(不考虑非法输入,如数字、特殊字符等)
  3. 系统根据用户的输入给出提示:位置对的个数以及字符对的个数
  4. 如果第3步中,用户输入完全正确(位置和字符),则游戏结束,系统给出猜测的次数和得分。如果没有完全正确,得分每次-5分,重复第2步

效果展示

import java.util.Arrays;
import java.util.Scanner;

public class GuessingCharacters {
	public static void main(String[] args) {
		// 设置大小
		int i, j, size = 5;
		char[] characters = new char[size];
		characters = generateCharacters(size);
		// 显示生成的字符
		System.out.println(Arrays.toString(characters));
		
		boolean win = false;
		int score = 100, guessTimes = 0, correctPosition, correctCharacters;
		// 记录用户输入的数组
		char[] guess = new char[size];
		Scanner scan = new Scanner(System.in);
		
		while (!win) {
			correctPosition = 0;
			correctCharacters = 0;
			System.out.println("请输入" + size + "个字符(a~z):");
			// 接收输入并去重
			guess = scan.nextLine().toCharArray();
			guess = unique(guess);
			// 更新猜的次数
			guessTimes++;
			
			// 遍历生成的数组,判断位置和字符是否正确
			for (i = 0; i < characters.length; i++) {
				// 如果完全正确,则直接退出循环
				if (Arrays.toString(characters).equals(Arrays.toString(guess))) {
					correctPosition = characters.length;
					correctCharacters = characters.length;
					win = true;
					break;
				}
				// 遍历输入的数组
				for (j = 0; j < guess.length; j++) {
					// 如果字符相同,则字符正确数自增
					if (characters[i] == guess[j]) {
						// 字符相同并且位置相同,则位置正确数自增
						if (i == j) {
							correctPosition++;
						}
						correctCharacters++;
					}
				}

			}
			if (!win) {
				// 没完全猜对,更新得分并给出相应提示
				score -= 5;
				System.out.println("字符对:" + correctCharacters + "个,位置对:" + correctPosition + "个");
			}
		}
		// 关闭流并且显示得分
		scan.close();
		System.out.println("猜对了,猜了" + guessTimes + "次,得分:" + score);
	}

	/**
	 * 随机生成size大小的不重复小写字符数组,size的合理范围为[1,26]
	 * @param size
	 * @return char[] 
	 */
	public static char[] generateCharacters(int size) {
		if (size < 0 || size > 26) {
			return null;
		}
		char[] characters = new char[size];
		int i, random, counts = 0;
		char a = 'a', newChar;
		
		while (counts != size) {
			// 生成[0,100)的整数
			random = (int) (Math.random() * 100);
			// 小写字母有26个,生成的数大于26就会出错,所以大于26时取余 但是这样会导致生成每个字母的概率不是平等的
			if (random >= 26) {
				random %= 26;
			}
			newChar = (char) (a + random);
			// 遍历生成的字符,确保生成的字符不重复
			for (i = 0; i < counts; i++) {
				if (characters[i] == newChar) {
					break;
				}
			}
			// 如果循环顺利结束,即没有重复字符时,i=counts
			if (i == counts) {
				characters[counts++] = newChar;
			}
		}
		return characters;
	}

	/**
	 * 字符数组去重
	 * @param arr
	 * @return char[]
	 */
	public static char[] unique(char[] arr) {
		// 最好的情况是,所有的元素都不重复,新数组(要返回的数组)与原数组长度相同
		char[] temp = new char[arr.length];
		// 默认第一个元素不重复
		temp[0] = arr[0];
		// 记录不重复数据的个数
		int count = 1;
		
		// 遍历原数组中的每一项,在此基础上用遍历的第i项去遍历新数组
		for (int i = 0; i < arr.length; i++) {
			// 标记,表示是否在新数组中找到重复的元素
			boolean flag = false;
			for (int j = 0; j < temp.length; j++) {
				if (arr[i] == temp[j]) {
					// 如果元素重复,则更改标记
					flag = true;
					break;
				}
			}
			// 如果没有元素重复,则往新数组中添加
			if (!flag) {
				temp[count++] = arr[i];
			}
		}
		// 如果不重复数据的个数与原数组长度不同,即原数组中存在重复元素,则缩短新数组的长度
		if (count != arr.length) {
			temp = Arrays.copyOf(temp, count);
		}
		return temp;
	}
}
posted @ 2021-06-14 15:52  吃猫的鱼℘  阅读(121)  评论(0编辑  收藏  举报
Document