131.分割回文串
该题目使用的是回溯法,在添加到集合前,我们需要判断一下是否为回文串,如果不是则不添加。
在是否为回文串我们可以采用动态规划的思想,对这个字符串进行一个预处理。
//字符串拆分成回文子串
public List<List<String>> partition(String s) {
List<List<String>> lists = new ArrayList<>();
if(s.length() == 0) {
return lists;
}
//保存子串的回文串状态(动态规划)
boolean[][] flag = new boolean[s.length()][s.length()];
for(int right=0; right<flag.length; right++) {
for(int left=0; left<= right; left++) {
//代表只有一个元素的时候
if(left == right) {
flag[left][right] = true;
continue;
}
//说明可以状态转移
if(s.charAt(left) == s.charAt(right)) {
//如果长度 = 2,那么直接为true
if(right - left + 1 == 2) {
flag[left][right] = true;
}else {
flag[left][right] = flag[left + 1][right - 1];
}
}
}
}
backTrace(lists,new ArrayList<>(),s,flag,0);
return lists;
}
public void backTrace(List<List<String>> lists, List<String> list, String s, boolean[][] flag, int index) {
if(index == s.length()) {
lists.add(new ArrayList<>(list));
return;
}
//index是子串头字符,i为截取的尾巴位置字符
for(int i=index; i<s.length(); i++) {
if(flag[index][i]) {
//右边开区间
list.add(s.substring(index,i + 1));
backTrace(lists, list, s, flag, i + 1);
list.remove(list.size() - 1);
}
}
}
133.克隆图
该题目就是一个深拷贝的题目,需要拷贝一个和原图一模一样,无关联的图。
该题目主要使用缓存的思想,将我们创建过的结点进行保存,如果缓存命中则取出,如果没有命中则重新创建。
public class Solution {
//用于保存克隆后的结点
Map<Integer,Node> map = new HashMap<>();
//克隆图(深拷贝),属性List代表自己的邻居
public Node cloneGraph(Node node) {
if(node == null) {
return null;
}
Node newNode = null;
if(map.containsKey(node.val)) {
return map.get(node.val);
}else {
newNode = new Node(node.val);
map.put(node.val,newNode);
}
//获取与其相邻的结点
List<Node> neighbors = node.neighbors;
List<Node> newNeighbors = new ArrayList<>();
for(Node neighbor : neighbors) {
if(map.containsKey(neighbor.val)) {
newNeighbors.add(map.get(neighbor.val));
}else {
newNeighbors.add(cloneGraph(neighbor));
}
}
newNode.neighbors = newNeighbors;
return newNode;
}
}
134.加油站
该题目就是一个dfs的方式进行搜索,没有特别之处。
//加油站(不能重复访问,并且只能向右边相邻位置进行访问)
public int canCompleteCircuit(int[] gas, int[] cost) {
for(int i=0; i<gas.length; i++) {
// 说明可以从 i 出发
if(gas[i] >= cost[i]) {
if(dfs(gas,cost,i,gas[i],0)) {
return i;
}
}
}
//说明无法找到
return -1;
}
public boolean dfs(int[] gas, int[] cost, int index, int nowGas, int times) {
//说明能够环绕一周
if(times == gas.length) {
return true;
}
//计算当前油量
nowGas += gas[index];
//如果无法访问下一位,则说明失败
if(nowGas < cost[index]) {
return false;
}
//说明可以到达,重新计算剩余油量
nowGas -= cost[index];
//说明到尾巴了,需要从头部继续开始
if(index + 1 >= gas.length) {
index = -1;
}
//说明可以向后面访问
return dfs(gas, cost, index + 1, nowGas, times + 1);
}
136.只出现一次的数字
使用异或进行位运算。
public int singleNumber(int[] nums) {
int single = 0;
for (int num : nums) {
single ^= num;
}
return single;
}
137.只出现一次的数字2
该题目也可以使用常量空间复杂度,可以看题解,我这里使用的HashMap解决的(O(N))
public int singleNumber(int[] nums) {
HashMap<Integer, Integer> hashmap = new HashMap<>();
for (int num : nums)
hashmap.put(num, hashmap.getOrDefault(num, 0) + 1);
for (int k : hashmap.keySet())
if (hashmap.get(k) == 1) return k;
return -1;
}
138.复制带随机指针的链表
该题目和前面的拷贝题目类似,不过该题可能存在重复val,所以我们需要将Key换成对应的旧 node 结点,其他的旧类似,需要注意的就是需要先保存缓存在进行递归。
public class Solution {
//(旧node , 拷贝node)
Map<Node,Node> map = new HashMap<>();
//深拷贝
public Node copyRandomList(Node head) {
if(head == null) {
return null;
}
if(map.containsKey(head)) {
return map.get(head);
}
Node newHead = new Node(head.val);
//先存入缓存,在进行后面的访问(此时为不完整拷贝对象,不过先提前暴露)
map.put(head,newHead);
//这里填充的可能是不完成拷贝的对象,但是我们已经指向了他们对应的内存,之后递归结束就会完成对象的完全拷贝。
newHead.next = copyRandomList(head.next);
newHead.random = copyRandomList(head.random);
return newHead;
}
}
139.单词拆分
提供两种思路吧
1.dfs(超时)
//单词拆分
public boolean wordBreak(String s, List<String> wordDict) {
return dfs(s,wordDict);
}
public boolean dfs(String s, List<String> wordDict) {
if(s.length() == 0) {
return true;
}
for(String word : wordDict) {
if(s.startsWith(word)) {
if(dfs(s.substring(word.length(),s.length()),wordDict)) {
return true;
}
}
}
return false;
}
2.动态规划
//单词拆分
public boolean wordBreak(String s, List<String> wordDict) {
//保存从下标0开始的对应长度是否有一种单词匹配方式
boolean[] dp = new boolean[s.length() + 1];
Set<String> set = new HashSet<>(wordDict);
//初始化
dp[0] = true;
for(int len=1; len<dp.length; len++) {
for(int start=0; start<len; start++) {
//前start长度是否有对应匹配 && 后面部分字符串在词典中是否有匹配
if(dp[start] && set.contains(s.substring(start,len))) {
dp[len] = true;
break;
}
}
}
return dp[s.length()];
}