重刷leedcode(11-20)
11.盛最多水的容器
分析
解题思路是用双指针去遍历数组,height小的指针进行移动。如果是左右指针的height相等的情况,那么双指针就同时移动。
class Solution {
public int maxArea(int[] height) {
if(height==null||height.length<2) return 0;
int area=0;
int left=0;
int right=height.length-1;
while(left<right){
area=Math.max(area,Math.min(height[left],height[right])*(right-left));
if(height[left]<height[right]) left++;
else if(height[left]>height[right]) right--;
else {left++;right--;}
}
return area;
}
}
12.整数转罗马数字
ps
这题的思路就是用贪心算法来做。技巧就是num/arb[index]得到最高位。num % arb[index]得到除最高位后的数
class Solution {
public String intToRoman(int num) {
StringBuilder sb=new StringBuilder();
String[] roman={"M","CM","D","CD","C", "XC", "L", "XL" , "X", "IX", "V","IV","I"};
int[] alb= {1000,900,500,400,100,90,50,40,10,9,5,4,1};
int index=0;
int count=0;
while(num>0){
count= num/alb[index];
while(count-- > 0){
sb.append(roman[index]);
}
num=num % alb[index];
index++;
}
return sb.toString();
}
}
13.罗马数字转整数
ps
这题主要就是用到了Map和string.substring(startIndex,endIndex)来实现的
class Solution {
public int romanToInt(String s) {
int result=0;
Map<String,Integer>map=new HashMap<>();
map.put("M",1000);
map.put("CM",900);
map.put("D",500);
map.put("CD",400);
map.put("C",100);
map.put("XC",90);
map.put("L",50);
map.put("XL",40);
map.put("X",10);
map.put("IX",9);
map.put("V",5);
map.put("IV",4);
map.put("I",1);
for(int i=0;i<s.length();){
//substring(startIndex,endIndex){从下标startIndex到endIndex下标的结束}
if(i+1<s.length()&&map.containsKey(s.substring(i,i+2))){
result+= map.get(s.substring(i,i+2));
i+=2;
}
else{
result+= map.get(s.substring(i,i+1));
i++;
}
}
return result;
}
}
14. 最长公共前缀
ps
string.indexOf(String x){//查找字符x在string中首次出现的索引值} 找到则返回索引,没找到则返回-1
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs.length==0) return "";
String prefixStr=strs[0];
for(String s:strs){
//indexOf()
//只要前缀不是s字符串的子串就一直循环
while(s.indexOf(prefixStr)!=0){
//["abc","ab","a"] 如果是abc和abc,则不会进循环,因为前缀abc是abc的字串
//abc和ab会进循环,因为前缀abc不是ab的字串。
prefixStr=prefixStr.substring(0,prefixStr.length()-1);
// if(prefixStr=="") return "";
}
}
return prefixStr;
}
}
15.三数之和
ps
排序+三指针(基指针+左指针+右指针)
左指针=基指针+1;
然后再考虑一下:以下几种特殊情况的处理
- 如果 nums[i]nums[i]大于 00,则三数之和必然无法等于 00,结束循环
- 如果 nums[i]nums[i] == nums[i-1]nums[i−1],则说明该数字重复,会导致结果重复,所以应该跳过
- 当 sumsum == 00 时,nums[L]nums[L] == nums[L+1]nums[L+1] 则会导致结果重复,应该跳过,L++L++
- 当 sumsum == 00 时,nums[R]nums[R] == nums[R-1]nums[R−1] 则会导致结果重复,应该跳过,R–R−−
作者:guanpengchn
链接:https://leetcode-cn.com/problems/3sum/solution/hua-jie-suan-fa-15-san-shu-zhi-he-by-guanpengchn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>>result=new ArrayList<>();
int len=nums.length;
if(nums==null&&len<3) return result;
int left=0;
int right=len-1;
//排序时间复杂度是nlogn
Arrays.sort(nums);
int temp=0;
int sum=0;
for(int i=0;i<len;i++){
if(nums[i]>0) break;
//满足条件就不执行下面的的代码,直接进入下一轮循环。
if(i>0&&nums[i]==nums[i-1]) continue;
left=i+1;
right=len-1;
while(left<right){
sum=nums[i]+nums[left]+nums[right];
if(sum==0) {
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
while(left<right&&nums[right]==nums[right-1]) right--;
while(left<right&&nums[left]==nums[left+1]) left++;
left++;right--;
}
else if(sum<0) left++;
else right--;
}
}
return result;
}
}
16. 最接近的三数之和
ps
这里的关键点就是:使用Maths.abs()计算绝对值,如果当前的绝对值小于之前的绝对值,那么就更新绝对值。
class Solution {
public int threeSumClosest(int[] nums, int target) {
int L=0;
int R=0;
int len=nums.length;
int tempResult=Integer.MAX_VALUE;
int result=0;
if(nums==null&&len<3) return Integer.MIN_VALUE;
int sum=0;
int abs=0;
Arrays.sort(nums);
//这里i是基base指针,所以i<len-2;
for(int i=0;i<len-2;i++){
L=i+1;
R=len-1;
while(L<R){
sum=nums[L]+nums[R]+nums[i];
abs=Math.abs(sum-target);
if(abs==0) return sum;
if(abs<tempResult){
tempResult=abs;
result=sum;
}
//先排序是一个值偏大造成的,剩下就可以只讨论值偏小造成的这种可能了。
//这里不能写成if(abs>target){}
if(sum>target) R--;
else L++;
}
}
return result;
}
}
17. 电话号码的字母组合
ps
主要用到了substring(int startIndex)方法去遍历字符串:例如happy,for(){“happy”.substring(1);“happy”.substring(0,1);},那么在每轮循环中字符变为:appy—>ppy–>py–>y—>length==0.我们获取到的字母依次是 a,p,p,y
class Solution {
//这里需要重写实现添加,使其map为类属性,不能在函数里面实现map的put
Map<String,String>map=new HashMap<String,String>(){{
put("2","abc");
put("3","def");
put("4","ghi");
put("5","jkl");
put("6","mno");
put("7","pqrs");
put("8","tuv");
put("9","wxyz");
}};
List<String>result=new ArrayList<String>();
public List<String> letterCombinations(String digits) {
//注意这里判空时要用digits.length()==0,不能使用digits==""来判断。
if(digits.length()==0||digits==null) return result;
backtrack("",digits);
return result;
}
//回溯法
//1. 函数有两个参数:一个参数值在不断增加,一个参数值在不断减少(一般不断减少的那个参数值常常作为每轮回溯的总结条件)
//2. 终结条件+递推条件(回溯法可以看作是暴力枚举的改良升级版)
//3. 虽然计算机在处理回溯时时层层跳出来执行的,但是编写回溯法时,站在层层深入的角度去编写递推条件---终结条件时跳出逻辑,一般在循环体的第一句就开始判断,在每次跳入之前就判断一次是否可以跳出。
//可将回溯看作是暴力穷举法的改良升级版
public void backtrack(String strAdd,String strRemv){
//如果走到最底层了,那么for循环的每个值都会进入该分支口,通过该口跳出递归
//如果没有走到最底层,则继续进入下一层,继续递归
if(strRemv.length()==0){ result.add(strAdd);}
else{
String record= strRemv.substring(0,1);
String temp=map.get(record);
for(int i=0;i<temp.length();i++){
String letter=temp.substring(i,i+1);
//这里用递归掩盖一重循环。这重循环是一套组合拳来实现的。for(){}+backstrack(s.substring(1))+s.substring(0,1)+s.substring(i,i+1);
//可理解成ad,ae,af,而不是adaeaf
backtrack(strAdd+letter,strRemv.substring(1));
}
}
}
}
18. 四数之和
ps
参考这位大神的思路写的。我写出来代码执行效率低了一点。可以参照大神的进行代码优化
力扣题解
写一下算法执行的思路:
- 总体思路:求四数之和。自然想到用4个指针。为了简化思考,可以先假定:固定前两个指针,然后后两个指针按照二分查找的代码框架进行编码。然后再回来二重遍历固定的两个指针
- 遍历i----->简化思维:先把 i 固定 (根据题目要求,每轮开始循环前,注意 i 的去重)
- 遍历j----->再把 j 固定 (每轮开始循环前,注意 j 的去重,同时注意 j 的初始位置的调整。每轮还要重新调整 L ,R 左右指针的初始位置)
- 按照二分法的思想移动左右指针(同时注意左右指针的去重)
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>>result=new ArrayList<>();
if(nums==null||nums.length<4) return result;
int i=0,j=0,L=0,R=0;
Arrays.sort(nums);
int len=nums.length;
for(;i<len-3;i++){
if(i>0&&nums[i]==nums[i-1]) continue;
for(j=i+1;j<len-2;j++){
if(j>i+1&&nums[j]==nums[j-1]) continue;
L=j+1;
R=len-1;
while(L<R){
int sum=nums[i]+nums[j]+nums[L]+nums[R];
if(sum==target){
result.add(Arrays.asList(nums[i],nums[j],nums[L],nums[R]));
++L;
--R;
while(R<len-2&&nums[R]==nums[R+1]) R--;
while(L<len-1&&nums[L]==nums[L-1]) L++;
}
else if(sum>target) {
R--;
while(R<len-2&&nums[R]==nums[R+1]) R--;
}
else{
L++;
while(L<len-1&&nums[L]==nums[L-1]) L++;
}
}
}
}
return result;
}
}
19. 删除链表的倒数第N个节点
ps
用双指针就能实现一趟遍历完成
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//先构造一个哑节点(虚拟节点)
ListNode temp=new ListNode(0);
temp.next=head;
ListNode slow=temp;
ListNode fast=temp;
//我写的是i<n-1,但是这样( slow=slow.next;)总是会出现null空指针
//官方题解的解决办法是用n+1
for(int i=0;i<n+1;i++){
fast=fast.next;
}
while(fast!=null){
fast=fast.next;
slow=slow.next;
}
slow.next=slow.next.next;
return temp.next;
}
}
20. 有效的括号
ps
用到了栈的知识
class Solution {
Map<Character,Integer>map=new HashMap<Character,Integer>(){{
//官方处理是put('[',']')
//这样在下文就可以使用map.get(stack.pop())==c来判断,可以减少hash查找次数
put('(',1);
put(')',1);
put('[',2);
put(']',2);
put('{',3);
put('}',3);
}};
public boolean isValid(String s) {
if(s==null&&s.length()==0) return true;
Stack<Character> stack=new Stack<>();
char[] cs=s.toCharArray();
for(char c:cs){
if(c=='('||c=='{'||c=='[') stack.push(c);
//if(c==')'||c=='}'||c==']'){
else{
if(!stack.empty()&& map.get(stack.pop())==map.get(c)) continue;
else return false;
}
}
if(stack.empty()) return true;
else return false;
}
}