目录
零、数组
1. 数组的概念
- 数组是一个用于存储多个相同类型数据的集合,创建数组时是把数组的地址(实际上是哈希值)赋值给变量,例如:[I@677327b6。
- 数组是引用数据类型,也是一种特殊的对象。
- 数组一旦创建,长度不可改变。
2. 数组的创建
- 数组的创建有3种语法:
- 静态初始化(2种)
- int[] a = {5,1,2,3,8,9};
直接赋予初始值 - a = new int[]{4,6,1};
为存在的数组变量,重新赋予一个新的数组,并且,直接初始化数组数据
- int[] a = {5,1,2,3,8,9};
- 动态初始化(1种)
- int[] a = new int[5];
新建5个长度的int[]数组,存默认值0,需要后续自己初始化数组数据
- int[] a = new int[5];
- 静态初始化(2种)
3. 创建数组过程分析
以 int[] a = new int[5]为例
- 在内存中开辟连续的空间,用来存放数据,长度是5
- 给数组完成初始化过程,给每个元素赋予默认值,int类型默认值是0
- 数组完成初始化会分配一个唯一的地址值(哈希值)
- 把唯一的地址值交给引用类型的变量 a a a 去保存
4. 常用的数组API
- 1、Arrays.toString(a)
- 把数组中的数据拼接成字符串
- 2、Arrays.sort(a)
- 数组排序,基本类型数组为优化的快速排序,引用类型数组为优化的合并排序
- 3、Arrays.binarySearch(a,t)
- 二分法查找(折半查找),在有序的数组中查找目标值的位置下标,找不到就返回 -(插入点+1)
- 4、Arrays.copyOf(a,指定长度)
- 把数组 a a a 复制成指定长度的新数组(此方法会创建新数组)
- 5、System.arraycopy(原数组,原数组起始位置(即下标),目标数组,目标数组起始位置,复制数量)
- 数组复制方法,此方法复制数组后不会创建新数组
一、33. 搜索旋转排序数组
1.题目
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
2.分析
这道题无用的文字太多,简单来说就是:
- 遍历数组,当遍历到的元素跟目标元素 t a r g e t target target 相同,返回当前下标,遍历结束都没找到返回 − 1 -1 −1
3.代码
class Solution {
public int search(int[] nums, int target) {
int i;
for (i = 0;i < nums.length;i++){
if (nums[i] == target){
return i;
}
}
return -1;
}
}
二、81. 搜索旋转排序数组 II
1.题目
给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false 。
2.分析
跟第一题差不多,只是把方法的返回值改成布尔类型 (true / false)。
3.代码
class Solution {
public boolean search(int[] nums, int target) {
int i;
for (i = 0;i < nums.length;i++){
if (nums[i] == target){
return true;
}
}
return false;
}
}
三、153. 寻找旋转排序数组中的最小值
1.题目
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
提示:
-5000 <= nums[i] <= 5000
2.分析
- 定义一个用于存储最小值的变量 m i n min min,因为提示中提到 n u m s [ i ] < = 5000 nums[i] <= 5000 nums[i]<=5000,所以 m i n min min 可以初始化为5000。
- 遍历数组,因为这里确定要遍历所有元素并且不需要获取下标值,可以使用增强for循环。
- 通过三元运算符来判断,是否将当前遍历的值存入变量 m i n min min
- 最后返回变量 m i n min min
3.代码
class Solution {
public int findMin(int[] nums) {
int min = 5000;
//增强for循环,确定要遍历全部元素并且不需要获取下标值时可以使用
for (int num : nums) {
min = min < num ? min : num;
}
return min;
}
}
四、70. 爬楼梯
1.题目
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
2.分析
- 这道题用到动态规划(DP)。
- 假设爬上
n
n
n 阶楼梯的方法数为
d
p
[
n
]
dp[n]
dp[n]
因为爬上 n n n 阶楼梯的上一步,这个人必定是在 ( n − 1 ) (n-1) (n−1) 或 ( n − 2 ) (n-2) (n−2) 阶楼梯上,所以:
n 阶 楼 梯 的 方 法 数 = ( n − 1 ) 阶 楼 梯 的 方 法 数 + ( n − 2 ) 阶 楼 梯 的 方 法 数 n 阶楼梯的方法数 = (n-1) 阶楼梯的方法数 + (n-2) 阶楼梯的方法数 n阶楼梯的方法数=(n−1)阶楼梯的方法数+(n−2)阶楼梯的方法数 即 : d p [ n ] = d p [ n − 1 ] + d p [ n − 2 ] 即:dp[n]=dp[n-1]+dp[n-2] 即:dp[n]=dp[n−1]+dp[n−2] - 首先确定1阶和2阶的方法数分别为: d p [ 1 ] = 1 , d p [ 2 ] = 2 dp[1]=1,dp[2]=2 dp[1]=1,dp[2]=2
- 最后通过一维数组来存储 n n n 阶以前各阶的方法数,经过遍历就能得到 d p [ n ] dp[n] dp[n]
3.代码
class Solution {
public int climbStairs(int n) {
//这里定义数组长度为n + 3,是为了防止 n<3 时数组越界
int[] dp = new int[n + 3];
dp[1] = 1;
dp[2] = 2;
int i;
for (i = 3;i <= n;i++){
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
五、斐波那契数
1.题目
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
2.分析
- 有两种方法:
- 1、递归(内存消耗低,但用时长)
确定递归的终点: n = 0 或 n = 1 n = 0 或n = 1 n=0或n=1
当前 n n n 的值 = ( n − 1 ) (n - 1) (n−1) 调用该方法得到的值 + ( n − 2 ) (n - 2) (n−2) 调用该方法得到的值 - 2、动态规划
跟第四题一样,利用一维数组将 n n n 之前的数据存储下来,最后返回 d p [ n ] dp[n] dp[n]
- 1、递归(内存消耗低,但用时长)
3.代码
-
1、递归
class Solution { public int fib(int n) { if (n == 0 || n == 1){ return n; } return fib(n - 1) + fib(n - 2); } }
-
2、动态规划
class Solution { public int fib(int n) { //这里定义数组长度为n + 2,是为了防止 n<2 时数组越界 int[] dp = new int[n + 2]; dp[0] = 0; dp[1] = 1; int i; for (i = 2;i <= n;i++){ dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } }
六、1137. 第 N 个泰波那契数
1.题目
泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。
2.分析
- 同样是利用动态规划,这里换一种写法,可以不用定义数组来存储元素。
- 只需要定义多个变量来存储上一步得到的值,就可以用作当前步骤
T
n
T_n
Tn 的求解,如下图所示:
3.代码
class Solution {
public int tribonacci(int n) {
if (n == 0 || n == 1){
return n;
}
if (n == 2){
return 1;
}
int i,target = 0,n1 = 0,n2 = 1,n3 = 1;
for (i = 3;i <= n;i++){
target = n1 + n2 + n3;
n1 = n2;
n2 = n3;
n3 = target;
}
return target;
}
}
七、2006. 差的绝对值为 K 的数对数目
1.题目
给你一个整数数组 nums 和一个整数 k ,请你返回数对 (i, j) 的数目,满足 i < j 且 |nums[i] - nums[j]| == k 。
|x| 的值定义为:
如果 x >= 0 ,那么值为 x 。
如果 x < 0 ,那么值为 -x 。
2.分析
- 这题可以暴力两层循环解决,这里换一种思路:
- 定义一个map,然后遍历数组,记录数组元素以及对应出现的次数
- 遍历map集合,每次在map集合中找是否存在 k e y = k e y + k key=key+k key=key+k ,如果存在,返回 k e y + k key+k key+k 对应的 v a l u e value value,与当前 k e y key key 的 v a l u e value value 相乘,就是这两个元素 k e y key key 、 k e y + k key+k key+k 能组成的所有数对数目
- 最后累加到计数变量 c o u n t count count
3.代码
class Solution {
public int countKDifference(int[] nums, int k) {
int count = 0;
HashMap<Integer,Integer> map = new HashMap<>();
//遍历存储数字以及对应出现的次数
for (int num : nums) {
int value = map.getOrDefault(num, 0);
map.put(num, ++value);
}
//遍历map集合
Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<Integer, Integer> next = iterator.next();
count += map.getOrDefault(next.getKey() + k,0) * next.getValue();
}
return count;
}
}
八、LCP 01. 猜数字
1.题目
小A 和 小B 在玩猜数字。小B 每次从 1, 2, 3 中随机选择一个,小A 每次也从 1, 2, 3 中选择一个猜。他们一共进行三次这个游戏,请返回 小A 猜对了几次?
输入的guess数组为 小A 每次的猜测,answer数组为 小B 每次的选择。guess和answer的长度都等于3。
2.分析
遍历数组,只要 g u e s s [ i ] = = a n s w e r [ i ] guess[i] == answer[i] guess[i]==answer[i],则计数加 1 1 1
3.代码
class Solution {
public int game(int[] guess, int[] answer) {
int i,count = 0;
for (i = 0;i < 3;i++){
if (guess[i] == answer[i]){
count++;
}
}
return count;
}
}
九、LCP 06. 拿硬币
1.题目
桌上有 n 堆力扣币,每堆的数量保存在数组 coins 中。我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。
限制:
1 <= n <= 4
1 <= coins[i] <= 10
2.分析
- 分两种情况:
- 1.偶数个数的直接2个2个拿完
- 2.奇数个数的,先2个2个拿,最后剩下1个再一次拿走
- 代码中把上述过程转换成位运算的写法,当然也可以直接写除号“/”,取余“%”等。
3.代码
class Solution {
public int minCount(int[] coins) {
int count = 0;
for (int coin : coins) {
//coin是偶数
if ((coin & 1) == 0){
count += coin >> 1;
//coin是奇数
} else {
count += (coin >> 1) + 1;
}
}
return count;
}
}