数组
求两数之和
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] result=new int[2];
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[i]+nums[j]==target){
return new int[]{i,j};
}
}
}
throw new RuntimeException();
}
}
时间复杂度:O(n^2)
对于每个元素,我们试图通过遍历数组的其余部分来寻找它所对应的目标元素,这将耗费 O(n)O(n) 的时间。
因此时间复杂度为 O(n^2)
需要一种方法,寻找符合要求的元素(target-nums[i])并返回其索引
元素与索引对应最好的方法:哈希表
通过“以空间换时间”的方式,可以将查找时间从O(n)转换为O(1)
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
时间复杂度:O(n)O(n),
我们把包含有 n个元素的列表遍历两次。由于哈希表将查找时间缩短到 O(1),所以时间复杂度为 O(n)。
事实证明:我们用一次遍历就可以完成:在将元素插入表的同时,同时在表中 查找当前元素所对应的目标元素
# 插入表的同时,目标元素一定在当前表么
2357
target 10
##因为此假设:假设每种输入只会对应一个答案,所以数组元素只会存在唯一符合条件的两个元素
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
//查找两个有关联的数字
//爸爸妈妈问题: 一群人查找爸爸妈妈,来一个人从房间看没有对方则进房间,直到一个人看见对方
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
//链式思维
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
}
删除排序数组重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
经验:动态改变数组的下标不可行 移动后指针回退—错误
public int removeDuplicates(int[] nums) {
//快慢双指针法 把和前一位不同的复制到数组前面
int j=0;
for(int i=1;i<nums.length;i++){
if(nums[i] != nums[i-1]){
nums[j+1]=nums[i];
j++;
}
}
return j+1;
}
最大子序和
暴力法
计算所有连续子数组的和,求最大值
分治法
分治法的精髓在于:
分-----将问题分解为规模更小的子问题;
治-----将这些规模更小的子问题逐个击破;
合-----将已解决的子问题合并,最终得出“母”问题的解;
取数组中心点为中心,最大子序要么全在中心左边,要么在右边,要么跨中心
跨中心的情况:分治成中心点左侧和右侧最大子序和 的问题,再分别用贪心算法
从哪边算意味着(中心算):从哪边连续计数
贪心法
每一步都选最优解,最后就是全局最优方案
遍历数组并更新1. 当期元素 2. 当前元素位置的最大和(currentSum=nums[0]) 3.总的最大和(maxSum=nums[0])
特点:
- 局部最优解推到全局最优解。
- 只会保留上一步的最优解
步骤: - 从第二个元素开始遍历,
动态规划
修改数组跟踪当前位置的最大和
遍历数组,更新当前位置的最大和nums[i],全局的最大和maxSum=nums[0]
特点:
- 局部最优解推到全局最优解。
- 保留之前所有最优解—(以i结尾的最大子序和)
步骤:
- 从第二个元素遍历,若前一个(子序和)为正数,才会累加当前数字——>当前最大和
- 根据当前最大和求出全局最大和
[-2,1,-3,4,-1,2,1,-5,4]
dp[i]表示以i结尾的最大子序和 dp[i]=max{nums[i],dp[i-1]+nums[i]}
nums[0]>0 nums[1]=nums[1]+nums[0]
nums[1]>0 nums[2]=nums[2]+nums[1]
nums[2]>0 nums[3]=nums[3]+nums[2]
动态规划和贪心算法的异同点:
相同点:
都是通过递推的方式,通过局部最优解来推导全局最优解
贪心算法是动态规划的一种特殊形式
不同点:
贪心算法:
特点是每一步都是局部最优解,只保留上一步的最优解
- 动态规划算法:
特点是每一步
2.状态转移方程:局部最优解===>全局最优解
3.边界条件:可直接得出的局部最优解。
## 每次拿能拿的最大的,就是贪心。
贪心算法最经典的例子,给钱问题。
比如中国的货币,只看元,有1元2元5元10元20、50、100
如果我要16元,可以拿16个1元,8个2元,但是怎么最少呢?
如果用贪心算,就是我每一次拿那张可能拿的最大的。
比如16,我第一次拿20拿不起,拿10元,OK,剩下6元,再拿个5元,剩下1元
也就是3张 10、5、1。