- 复原IP地址:回溯法,ip总共由三个点分割,在一个ip定下之后 下一个ip只可能有3个位置。用回溯法找出所有使ip符合规则的点的组合。放置完三个点之后,如果当前的位置已经是字符串结尾了,说明ip第四部分没值,这时候不会有合法组合,需要删除刚添加的部分并回溯。如果第四部分合法则当前ip合法, 添加第四部分之后将当前ip加入ans里,并删除末尾部分。用parseInt判断范围的时候,字符串里可能会有超出int范围的数,所以先判断长度筛选一波,长度大于3的直接忽略。需要注意的是什么时候需要删除possibleAns的末尾。
-
class Solution { public List<String> restoreIpAddresses(String s) { List<String> ans = new ArrayList<String>(); //长度小于4的时候不可能有合法IP地址 if(s.length() < 4){ return ans; } backTrack(s, ans, 0, 0, new StringBuilder("")); return ans; } public void backTrack(String s, List<String> ans, int step, int curPos, StringBuilder possibleAns){ if(step == 3){ //ip地址的最后一部分是不是符合要求 String temp1 = s.substring(curPos, s.length()); if(temp1.length() > 3){ return; } if(Integer.parseInt(temp1) < 0 || Integer.parseInt(temp1) > 255 || (temp1.length() > 1 && temp1.charAt(0) == '0')){ return; } else{ //符合要求,当前结果加入到list中,且删除末尾部分,回溯。 possibleAns.append(s.substring(curPos, s.length())); ans.add(possibleAns.toString()); possibleAns.delete(possibleAns.length() - s.substring(curPos, s.length()).length(), possibleAns.length()); } }else{ for(int i = curPos; i < curPos + 3; i++){ //如果到达了结尾且只完成了ip的三个部分,这个方法不可取,回溯。 if(i + 1 == s.length()) return; String temp = s.substring(curPos, i + 1); //不合法就跳过 if(Integer.parseInt(temp) < 0 || Integer.parseInt(temp) > 255 || (temp.length() > 1 && temp.charAt(0) == '0')){ continue; }else{ //符合规则就添加,然后再找ip的下一个部分,尝试完后删除末尾。 possibleAns.append(temp + "."); backTrack(s, ans, step + 1, i + 1, possibleAns); possibleAns.delete(possibleAns.length() - temp.length() - 1, possibleAns.length()); } } return; } } }
- 括号生成:输入括号的对数,生成所有有效的括号组合。回溯法实现。首先添加一对括号之后,下一对括号就有三个位置可放,在下一个就有当前长度加1个位置可放。尝试所有位置。当所有括号都添加完之后,把结果加热set里,就不会有重复的结果出现。 最后把set一个个赋值给list完成。
-
class Solution { public List<String> generateParenthesis(int n) { List<String> ansReal = new ArrayList<String>(); HashSet<String> ans = new HashSet<String>(); backTrack(n, ans, new StringBuilder()); for(String s : ans){ ansReal.add(s); } return ansReal; } public void backTrack(int n, HashSet<String> ans, StringBuilder possibleAns){ if(possibleAns.length() == 2 * n){ ans.add(possibleAns.toString()); return; }else{ for(int i = 0; i < possibleAns.length() + 1; i++){ possibleAns.insert(i, "()"); backTrack(n, ans, possibleAns); possibleAns.delete(i, i + 2); } } } }
Java 创建线程的两种方式:
public class createThred {
static class Thread1 extends Thread {
@Override
public void run() {
System.out.println("Thread1");
}
}
static class Runnable1 implements Runnable {
public void run() {
System.out.println("Thread2");
}
}
public static void main(String[] args) {
// 第一种:写一个类继承Thread类,重写它的run方法
Thread1 thread1 = new Thread1();
thread1.start();
// 第二种:写一个类实现Runnable接口,然后使用新类作为参数来创建新线程
Thread thread2 = new Thread(new Runnable1());
thread2.start();
// 第二种的拓展,在Jdk8后使用lambda表达式
Thread thread3 = new Thread(() -> System.out.println("Thread3"));
thread3.start();
}
}
Synchronized同步机制: 为什么需要同步机制?
这是为了预防有两个以上的线程同时访问同一个资源。
- 注意1: 如果一个对象有多个Synchronized修饰的方法, 当一个线程访问这个对象的同步方法时,在这一线程执行结束之前,其他线程无法再访问该对象的任何Synchronized方法。因为实例的同步方法是对其对象上锁, 该对象的锁已经被其他线程获取了。
- 注意2: 如果是一个静态同步方法,当有线程进入这一方法的时候需要获取的是这个静态方法所在类的锁,因为静态方法不需要实例对象,它是属于类的。所以当线程访问同一个类的两个静态同步方法(static Synchronized)时,执行也是按顺序的, 因为类的锁已经被获取了,另一个只能等前一个执行完毕。
- 供暖器:思路是找每个房子左右边最近的暖气,去最小,最后所有房子的最小里取最大 就是最后结果。 这里我们只要找房子右边的暖气位置 i 就行了,左边就是i-1。然后进行比较。 需要注意第一个暖气和最后一个暖气的情况:房子右边的暖气是第一个暖气的时候,半径就是他们的距离差。遇到最后一个暖气时,需要判断是在最后一个house的左边还是右边。
-
class Solution { public int findRadius(int[] houses, int[] heaters) { Arrays.sort(heaters); Arrays.sort(houses); int i = 0; int radius = 0; for(int house : houses){ //还有暖气,且暖气在房子左边的时候继续往下找 while(i < heaters.length && heaters[i] < house ){ i++; } if(i == 0){ radius = Math.max(radius, heaters[i] - house); //只有当暖气在房子左边,且是最后一个暖气的时候i才会等于暖气总数否则i最后应该是 //heaters.length - 1 }else if(i == heaters.length){ radius = Math.max(radius, houses[houses.length - 1] - heaters[i - 1]); }else{ radius = Math.max(radius, Math.min(heaters[i] - house, house - heaters[i - 1])); } } return radius; } }