剑指offer34
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
- list容器(数组线性表):List<List<Integer>> list=new ArrayList<>()
- list.add(obj)向尾部添加元素,list.add(int index,Object obj)向index索引位置添加元素。特别要注意的是,add()方法加入的是对象的地址,要加入不同对象需要new新的对象。add()允许添加重复元素。
- list.remove(int i)删除索引i位置的元素,可以这样子写remove(list.indexOf(Object a))。另一种是remove(Object a),如果是对象引用则直接传对象引用作为参数,若传的是基本类型,则需要先转化成包装类remove(Integer.valueOf(250))。
- list.size()获取list的长度
- list.get(int i)返回索引下标为i的元素
- list.contains(int i)容器中含有i则返回true,可以用来去重。
- List list=new ArrayList<>(Arrays.asList(object a,object b,object c…)):一种初始化写法
-
注意找到正确的路径记录进list时,若直接执行list.add(path) ,则是将 path 对象加入了list ;后续 path 改变时,list 中的 path 对象也会随之改变。因此每次添加一个新的容器进入到容器,都要新实例化一个新的容器。list.add(new ArrayList<>(path)),相当于复制了一个path加入到list。
-
深搜过程中,如果是叶子结点则判断当前路径能不能加入到list当中然后退出当前搜索,否则继续遍历左子树和右子树。难点在于路径记录,path记录时如何保证当前父节点路径不变而只改变后加入的子节点路径。令每个结点进入搜索时当前val值加入到path,退出搜索回溯父节点前,再从path中退出来。
public void dfs( List<List<Integer>> list,List<Integer> path,TreeNode root,int target) //注意整形Integer首字母要大写 { if(root.right==null&&root.left==null)//叶子节点 { if(target==root.val) { path.add(target); list.add(new ArrayList<>(path)); path.remove(path.size()-1); return; } } path.add(root.val); //先进 if(root.left!=null) //不用root判空 dfs(list,path,root.left,target-root.val); if(root.right!=null) dfs(list,path,root.right,target-root.val); path.remove(path.size()-1); //后退 } }
剑指offer38
输入字符串,打印出该字符串中字符的所有排列。你可以以任意顺序返回这个字符串数组,里面不能有重复元素。
输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]
- String.valueOf(char[] data):将char[]数组转换成String
- list.toArray(new String[list.size()])把list容器转化为指定类型的数组
- Set容器(不容许有重复元素的集合):HashSet site=new HashSet<>()
- 方法:add(),remove(String),contains(String)
- dfs+回溯。用一个全局的visit数组来记录下使用过的字符 ,回溯前再置为0。 一开始去重用的是!list.contains(String),后面发现超时了。然后又改成HashSet容器来装字符串数组,居然能AC,可能这就是HashSet的魅力吧。减少时间的关键在于剪支,同一个位置(意味着同一个dfs内的循环)如果是先前使用过的字符则可直接continue,所以在每一个dfs都要开辟一个HashSet来记录当前位置使用过的字符。剪枝过后就不需要去重。
class Solution {
int[] visit=new int[8];
HashSet<String> list=new HashSet<>();
public String[] permutation(String s){
char[] temp=new char[s.length()];
dfs(s,temp,0);
return list.toArray(new String[list.size()]);
}
public void dfs(String s,char[] temp,int charindex)
{
if(s.length()==charindex)
{
list.add(new String(temp,0,s.length()));
return;
}
HashSet<Character> v=new HashSet<>(); //真正的剪枝
for(int i=0;i<s.length();i++)
{
if(visit[i]==0&&!v.contains(s.charAt(i)))
{
visit[i]=1;
v.add(s.charAt(i));
temp[charindex]=s.charAt(i);
dfs(s,temp,charindex+1);
visit[i]=0;
if(charindex==s.length()-1) //假的剪枝
return;
}
}
}
}
剑指offer39
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
- HashMap:HashMap<Integer,Integer> hashmap=new HashMap<>()
- hashmap.put(i,j):添加元素key=i对应value为j
- hashmap.get(i):访问i对应的value,若不存在key则返回null,可以在初次插入用来判空
- for(Integer(key类型) i:hashmap.keySet()) hashmap.get(i)
- for(Integer(value类型) i:hashmap.values())
- containsKey()/containsValue() 容器含有对应的key值或者value则返回true,用来判空
- 摩尔投票法,前a个数字的票数和为0,则剩余(n-a)个数的票数之和一定仍然>0(题目说明一定存在大于一半数量的数),剩余数组的众数一定不变。vote==0时,当前的num值即为当前这一轮投票区间的众数。
剑指offer42
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
- 动规题,cur记录下以当前索引位置为最右端的最大子数组。cur = Math.max(cur+nums[i],nums[i]);以当前索引位置为右端点的最大子数组中,当前索引位置左边的值之和一定为正数,否则只有它自身。
今日总结
刷题