一维数组
一维数组的扩容
- 数组长度一旦确定不可变。
- 那数组应该如何扩容?
-
- 只能创建一个更大的数组将原数组中的数据全部拷贝到新数组中
- 可以使用System.arraycopy()方法完成数组的拷贝。
- 数组扩容会影响程序的执行效率,因此尽可能预测数据量,创建一个接近数量的数组,减少扩容次数。
JUnit单元测试
常见注解:
@BeforeAll @AfterAll 主要用于在测试开始之前/之后执行必要的代码。被标注的方法需要是静态的。
@BeforeEach @AfterEach 主要用于在每个测试方法执行前/后执行必要的代码。
// 断言(断言机制)
Assertions.assertEquals(expected, actual);
数据结构与算法
时间复杂度
什么叫做时间复杂度?
我们用T(n)来表示算法中基本操作(例如比较、赋值、运算等)的重复执行次数。n是程序需要处理的数据量(专业术语叫做问题规模n)。若有某个趋势函数f(n)【f(n)代表了时间复杂度的趋势】,使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),我们称O(f(n))为时间复杂度。我们也把它叫做大O表示法。
时间复杂度计算步骤:
1. 计算基本操作的执行次数T(n)
在做算法分析时,一般默认考虑最坏的情况。因为最坏的情况下,基本操作执行的次数是最多的。对算法效率的评估会更加准确。
2. 通过T(n)得到f(n)求T(n)的数量级f(n),只需要将T(n)做两个操作:
(一)忽略常数项、低次幂项和最高次幂项的系数。
(二)例如,在T(n)=4n2+2n+2中,T(n)的数量级函数f(n)=n2。
计算T(n)的数量级f(n),我们只要保证T(n)中的最高次幂正确即可,可以忽略所有常数项、低次幂项和最高次幂的系数。这样能够简化算法分析,将注意力集中在最重要的一点上:增长率。
3. 用大O表示时间复杂度
得到f(n)的结果是n2,所以时间复杂度是:O(n2)。
切记,时间频度不相同,时间复杂度有可能相同,如T(n)=n2+3n+4与T(n)=4n2+2n+1它们的时间频度不同,但时间复杂度相同,都为O(n2)。
常见的时间复杂度
- 常数阶O(1)
无论代码执行了多少行,只要没有循环等复杂结构,那这个代码的时间复杂度就都是O(1)
int num1 = 3, num2 = 5;
int temp = num1;
num1 = num2;
num2 = temp;
System.out.println("num1:" + num1 + " num2:" + num2);
在上述代码中,没有循环等复杂结构,它消耗的时间并不随着某个变量的增长而增长,那么无论这类代码有多长,即使有几万几十万行,都可以用O(1)来表示它的时间复杂度
- 对数阶O(log2n)
O(log2n)指的就是:在循环中,每趟循环执行完毕后,循环变量都放大两倍。
int n = 1024;
for(int i = 1; i < n; i *= 2) {
System.out.println("hello powernode");
}
推算过程:假设该循环的执行次数为x次(也就是i的取值为2x),就满足了循环的结束条件,即满足了2x等于n,通过数学公式转换后,即得到了x = log2n,也就是说最多循环log2n次以后,这个代码就结束了,因此这个代码的时间复杂度为:O(log2n) 。
同理,如果每趟循环执行完毕后,循环变量都放大3倍,那么时间复杂度就为:O(log3n) 。
- 线性阶O(n)
int n = 100;
for(int i = 0; i < n; i++) {
System.out.println("hello powernode");
}
在上述代码中,for循环会执行n趟,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度 。
- 线性对数阶 O(nlog2n)
int n = 100;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j *= 2) {
System.out.println("hello powernode");
}
}
线性对数阶O(nlog2n) 其实非常容易理解,将时间复杂度为O(log2n)的代码循环n遍的话,那么它的时间复杂度就是n*O(log2n),也就是了O(nlog2n)。
- 平方阶 O(n2)
int n = 100;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
System.out.println("hello powernode");
}
}
外层i的循环执行一次,内层j的循环就要执行n次。因为外层执行n次,总的就需要执行n*n次,也就是需要执行n2次。因此这个代码时间复杂度为:O(n2)。平方阶的另外一个例子:
int n = 100;
for(int i = 1; i <= n; i++) {
for(int j = i; j <= n; j++) {
System.out.println("hello powernode");
}
}
当i=1的时候,内侧循环执行n次,当i=2的时候,内侧循环执行(n-1)次,......一直这样子下去就可以构造出一个等差数列:n+(n-1)+(n-2)+......+2+1 ≈ (n2)/2。根据大O表示法,去掉最高次幂的系数,就可以得到时间复杂度为:O(n2)。
同理,立方阶 O(n3),参考上面的O(n2)去理解,也就是需要用到3层循环
Arrays工具类
// 凡事自定义的类型要做比较的话,这个自定义类型必须实现一个接口:Comparable接口,并且实现compareTo方法,在这个方法中编写比较规则。
Arrays.toString()方法:将数组转换成字符串
Arrays.deepToString()方法:可以将二维数组转换成字符串
Arrays.equals(int[] arr1, int[] arr2)方法:判断两个数组是否相等
Arrays.equals(Object[] arr1, Object[] arr2)方法
Arrays.deepEquals(Object[] arr1, Object[] arr2)方法:判断两个二维数组是否相等
Arrays.sort(int[] arr)方法:基于快速排序算法,适合小型数据量排序。
Arrays.sort(String[] arr)方法
Arrays.parallelSort(int[] arr)方法:基于分治的归并排序算法,支持多核CPU排序,适合大数据量排序。
int binarySearch(int[] arr, int elt)方法:二分法查找
Arrays.fill(int[] arr, int data)方法:填充数组
Arrays.fill(int[] a, int fromIndex, int toIndex, int val)方法
int[] Arrays.copyOf(int[] original, int newLength)方法:数组拷贝
int[] Arrays.copyOfRange(int[] original, int from, int to)
Arrays.asList(T... data)方法:将一组数据转换成List集合。
//找5的下标
@Test
public void testBinarySearch(){
int[] arr = {1,2,3,4,5,6,7};
System.out.println(Arrays.binarySearch(arr, 5));
}
//填充
@Test
public void testFill(){
int[] arr = new int[5]; // 5个0
Arrays.fill(arr, 10);
System.out.println(Arrays.toString(arr));
// 不包含toIndex
Arrays.fill(arr, 1, 3, 100);
System.out.println(Arrays.toString(arr));
}
//拷贝
@Test
public void testCopyOf(){
// 数组拷贝
int[] arr = {1,2,3,4,5,6,7,8,9};
int[] newArr = Arrays.copyOf(arr, 3);
System.out.println(Arrays.toString(newArr));
// to不包含
int[] newArr2 = Arrays.copyOfRange(arr, 2, 4);
System.out.println(Arrays.toString(newArr2));
}
@Test
public void testAsList(){
// 将一串数字转换成List集合。
List list = Arrays.asList(1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}