目录
无重叠区间一leetcode56
如果没有融合,当前区间就变成新的previous,下一个区间成为新的current
- 先将所有的区间按照起始时间的先后顺序排序,从头到尾扫描一遍
- 定义两个变量 previous 和 current,分别表示前一个区间和当前的区间
- 如果没有融合,那么当前区间就变成了新的前一个区间,下一个区间成为新的当前区间
- 如果发生了融合,更新前一个区间的结束时间。
这个就是贪婪算法。
class Solution {
public int[][] merge(int[][] intervals) {
//将所有区间按照起始时间的先后顺序排序
Arrays.sort(intervals, new Comparator<int[]>(){
public int compare(int[] a, int[] b){
return a[0] - b[0];
}
});
int[] previous = null;
List<int[]> result = new ArrayList<>();
for(int[] current : intervals){
//如果是第一个区间,或者当前区间和前一个区间没有重叠
//那么将当前区间加入到结果中
if(previous == null || current[0] > previous[1]){
result.add(previous = current);
}
//否则,两个区间发生了重叠,更新前一个区间的结束时间
else{
previous[1] = Math.max(previous[1], current[1]);
}
}
//把list转换为二维数组的方法
return result.toArray(new int[result.size()][]);
}
}
无重叠区间二leetcode 435
解题思路一
主体函数中,先将区间按照起始时间的先后顺序排序,然后调用递归函数
递归函数中,先检查所有区间是否处理完,是的话,表明不需要删除操作,直接返回
定义taken与nottaken变量,用来记录:
-如果保留当前区间的话,最少需要删除多少其他区间
-如果删除当前区间的话,最少需要删除多少区间
int eraseOverlapIntervals(int prev, int curr, int[][] intervals){
if(curr == intervals.length){
return 0;
}
int taken = Integer.MAX_VALUE, notaken;
if(prev == -1 || intervals[curr][0] >= intervals[prev][1]){
//只有当prev,curr没有发生重叠的时候,才可以选择保留当前的区间
taken = eraseOverlapIntervals(curr, curr + 1, intervals);
}
//其他情况,可以考虑删除curr区间,看看删除了它之后会不会产生最好的结果
notaken = eraseOverlapIntervals(prev, curr + 1, intervals) + 1;
return Math.min(taken, notaken);
}
解题思路二:贪婪法:
将所有的区间按照起始时间的先后顺序排序
定义一个end变量记录当前的最小结束时间点
定义一个count变量记录到目前为止删除了多少区间
从第二个区间开始
判断一下当前区间和前一个区间的结束
如果发现当前区间与前一个区间有重叠
即当前区间的起始时间小于上一个区间的结束时间
则end变量记录下两个结束时间的最小值
意味着把结束时间晚的区间删除,计数加一
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if(intervals.length == 0){
return 0;
}
Arrays.sort(intervals, new Comparator<int[]>(){
public int compare(int[] a, int[] b){
return a[0] - b[0];
}
});
int end = intervals[0][1], count = 0;
for(int i = 1; i < intervals.length; i++){
//表示有重叠,end保留最小值
if(intervals[i][0] < end){
end = Math.min(end, intervals[i][1]);
count ++;
}
else{
end = intervals[i][1];
}
}
return count;
}
}
解题思路三:按照结束时间排序
将所有的区间按照起始时间的先后顺序排序
定义一个end变量记录当前的最小结束时间点
定义一个count变量记录有多少个没有重叠的区间
从第二个区间开始遍历剩下的区间
如果发现当前区间与前一个区间结束时间没有重叠,则计数加一,同时更新一下新的结束时间
最后,用总区间的个数减去没有重叠的区间个数,得到最少要删除的区间个数
int eraseOverlapIntervals(int[][] intervals){
if(intervals.length == 0){
return 0;
}
Arrays.sort(intervals, new Comparator<int[]>(){
public int compare(int[] a, int[] b){
return a[0] - b[0];
}
});
int end = intervals[0][1],
int count = 1;
for(int i = 1; i < intervals.length; i++){
if(intervals[i][0] >= end){
end = intervals[i][1];
count ++;
}
}
return intervals.length - count;
}
火星字典LeetCode269
如果组合得到最终的关系呢?利用拓扑排序,得到有向图的一个拓扑排序
用深度优先的方法来进行拓扑排序
-visited集合:用来记录已访问过的顶点
-stack堆栈:
当从某顶点出发,访问完其他所有顶点
最后才把当前顶点加入到堆栈:即要想把该点加入stack里,必须先把其他跟它有联系的顶点都处理完毕
-loop集合:有效防止有向图中出现环的情况,每一轮访问完,都必须把loop集合清空完,如果不清空的话,会误把非环当成环,而提前终止了程序,无法将其加入堆栈中。
- 将当前结点u加入visited集合以及loop集合中
- 逐个访问与顶点u相邻的其他顶点v
- 如果在此轮访问过程中,v其实早已被访问过,则此处有环出现,返回false
- 否则,如果顶点v还没有被访问过,就递归地访问它,如果在访问顶点v时发现了环,则返回false
- 当这一轮访问结束后,即从顶点u出发访问完所有能访问的点,将u从loop集合里删除
- 把u加入到堆栈中
代码实现
包括两大步骤,第一步是根据输入构建一个有向图;第二步是对这个有向图进行拓扑排序。
String alienOrder(String[] words){
if(words == null || words.length == 0){
return null;
}
if(words.le == 1){
return words[0];
}
//定义了一个邻接链表adjList,表示有向图也可以使用邻接矩阵
Map<Character, List<Character>> adjList= new HashMap<>();
for(int i = 1; i < words.length; i++){
String w1 = words[i-1];
String w2 = words[i];
int len = Math.min(w1.length(), w2.length());
int diffIndex = 0;
while((diffIndex < n) && (w1.charAt(diffIndex) == w2.charAt(diffIndex)){
diffIndex ++;
}
//找到了对应的位置应该为 s1.charAt(diffIndex) -> s2.charAt(d)
//一旦出现两个字母不同,就处理好顶点之间的关系
if(diffIndex < n){
char c1 = s1.charAt(diffIndex);
char c2 = s2.charAt(diffIndex);
if(!map.containsKey(c1)){
map.put(c1, new ArrayList<Character>());
}
if(!map.containsKey(c2)){
map.put(c2, new ArrayList<Character>());
}
map.get(c1).add(c2);
}
}
Set<Character> visited = new HashSet<>();
Set<Character> loop = new HashSet<>();
Stack<Character> stack = new Stack<Character>();
for(Character key : adList.keySet){
if(!visited.contains(key)){
if(!topologicalSort(adList, key, visited, loop, stack)){
return "";
}
}
}
StringBuilder sb = new StringBuilder();
while(!stack.isEmpty()){
sb.append(stack.pop());
}
return sb.toString();
}
boolean topologicalSort<Mat<Character, List<Character>> adList, char u,
Set<Character> visited, Set<Character> loop, Set<Character> stack){
visited.add(u);
loop.add(u);
if(adList.containsKey(u)){
for(int i = 0; i < adList.get(u).size(); i++){
char v = adList.get(u).get(i);
if(loop.contains(v)){
return false;
}
if(!visited.contains(v)){
if(!topologicalSort(adList, v, visited, loop, stack)){
return false;
}
}
}
}
loop.remove(u);
stack.push(u);
return true;
}
leetCode772 基本计算器
class Solution {
public int calculate(String s) {
Queue<Character> queue = new LinkedList<>();
for(char c: s.toCharArray()){
if(c != ' '){
queue.offer(c);
}
}
queue.add('+');//在末尾额外添加一个+
return calculate(queue);//在主函数中调用一个递归函数
}
public int calculate(Queue<Character> queue){
char sign = '+';
int num = 0;
//定义一个新变量来记录要被处理的数
Stack<Integer> stack = new Stack<>();
while(!queue.isEmpty()){
char c = queue.poll();
if(Character.isDigit(c)){
num = 10 * num + c - '0';
}
//如果遇到了符号,表明我们要开始统计一下当前的结果
else{
//如果遇到了一个左括号,开始递归调用,求得括号中的计算结果,将它赋值给当前的num
if(c == '('){
num = calculate(queue);
}
else {
if(sign == '+'){
//当遇到加号,把当前的数压入堆栈中
stack.push(num);
}
else if(sign == '-'){
//当遇到的是减号,把当前数的相反数压入堆栈中
stack.push(-num);
}
//当遇到的是乘号,把前一个数从堆栈中取出,然后和当前的数相乘,相乘完毕之后再放回堆栈
else if(sign == '*'){
stack.push(stack.pop() * num);
}
//当遇到的是除号,把前一个数从堆栈中取出,然后和当前的数相除,相除完毕之后再放回堆栈
else if(sign == '/'){
stack.push(stack.pop() / num);
}
//对num清零,并且更新当前符号位
num = 0;
sign = c;
if(c == ')'){
break;//当遇到一个右括号时,就可以结束循环,直接返回当前总和
}
}
}
}
int sum = 0;
//堆栈里存储的都是需要相加的结果,将它们相加,返回总和即可
while(!stack.isEmpty()){
sum += stack.pop();
}
return sum;
}
}