leetcode

1 两数之和

hashmap方法是目前最优解法。
思路:
一遍循环,存储到hashmap前查看是否有target-nums[i]
(返回一个结果可以提前break)
如果返回多个结果就用list存一下。

class Solution {
    public int[] twoSum(int[] nums, int target) {
     int[] res = new int[2];
      HashMap<Integer,Integer> map = new HashMap<>();
      for(int i =0;i<nums.length;i++){
          if(map.containsKey(target-nums[i])){
                res[0]=i;
                res[1]=map.get(target-nums[i]);
          }
          map.put(nums[i],i);
      }
      return res;
    }
}

2 两数相加

链表模拟,注意ListNode从头开始写也要会写。

这个题逆序链表比较简单。
l1l2只要有一个不为空就继续,对为空的val赋值0,然后求和结果+进位取余作为当前位。
循环结束要检查进位,如果不为0要部进位位。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = null, tail = null;
        int carry = 0;
        while (l1 != null || l2 != null) {
            int n1 = l1 != null ? l1.val : 0;
            int n2 = l2 != null ? l2.val : 0;
            int sum = n1 + n2 + carry;
            if (head == null) {
                head = tail = new ListNode(sum % 10);
            } else {
                tail.next = new ListNode(sum % 10);
                tail = tail.next;
            }
            carry = sum / 10;
            if (l1 != null) {
                l1 = l1.next;
            }
            if (l2 != null) {
                l2 = l2.next;
            }
        }
        if (carry > 0) {
            tail.next = new ListNode(carry);
        }
        return head;
    }
}

3 无重复字符的最长子串

其实也是一个模拟
用hashmap存一下比较好写,也可以用charflag[128]存一下。

思路就是两个指针left和right,保证两指针之间没有重复字符即可。
不断更新max

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashMap <Character,Integer> map = new HashMap<>();
        int right=0,left=0;
        int res= 0;
        while(right<s.length()){
            while(map.containsKey(s.charAt(right))){
                map.remove(s.charAt(left));
                left++;
            }
            map.put(s.charAt(right),right);
            right++;
            res = Math.max(res,right-left);
        }
        return res;
    }
}

5 最长回文子串

暴力解了一下
这里暴力的思路比较清晰(实际上可以写的更简洁,但是没啥必要搞复杂直观比较重要)
遍历的是中心索引,就是索引为i的时候,有两种情况,一种是偶数长度,认为i和i+1应该相等,一种是奇数长度,认为i是中心,i-1和i+1相等。都求一下。

class Solution {
    String res ="";
    public String longestPalindrome(String s) {
        if(s.length()==0)return res;
        for(int i = 0; i<s.length();i++){
            String tempRes1;
            String tempRes2;
            tempRes1 = getReverseLenth(s,i);
            tempRes2 = getReverseLenth2(s,i);
            if(tempRes1.length()>res.length())
                res = tempRes1;
            if(tempRes2.length()>res.length())
                res = tempRes2;
        }
        return res;
    }
    public String getReverseLenth(String s, int i){
        int left =i,right =i;
        while(left>=0&&right<s.length()&&s.charAt(left)==s.charAt(right)){
            left--;
            right++;
        }
        return s.substring(left+1,right);
    }    
    public String getReverseLenth2(String s, int i){
        int left =i,right =i+1;
        while(left>=0&&right<s.length()&&s.charAt(left)==s.charAt(right)){
            left--;
            right++;
        }
        return s.substring(left+1,right);
    }
}
 

区间DP的思路,基本是一个完全的模版题.
dp[i][j]表示下标i到j是否是一个回文串。
初始化的话len=0都是true,j-i=0,表示单独一个字母是回文串。
len=1 i=j为true,表示两个相邻的相等为true;(这是上一种方法提到的两种情况各自的起始情况)
其余dp[i,j]=dp[i+1][j-1]&&ij字母相等
先对区间长度循环,然后对起点循环。
循环中处理len为0和len为1的特殊情况
len>=2之后都走dp状态转移方程即可。
每轮循环判断一下dp[i,j]如果为true,l+1是否比当前res.length大。

class Solution {
    public String longestPalindrome(String s) {
        boolean[][]dp  = new boolean [s.length()][s.length()];
        String res ="";
        for( int l = 0;l<s.length();l++){
            for(int i=0;i+l<s.length();i++){
                int j = i+l;
                if(l==0){
                    dp[i][j] = true;
                }else if(l==1){
                    if(s.charAt(i)==s.charAt(j)){
                        dp[i][j]=true;
                    }
                }else{
                    dp[i][j]=(s.charAt(i)==s.charAt(j))&&dp[i+1][j-1];
                }
                if(dp[i][j]==true && l>=res.length()){
                    res = s.substring(i,j+1);
                }
            }
        }
        return res;
    }
}

6 Z字形变

使用stringBuilder 避免string重复建对象。
注意numRows=1的时候,getindex逻辑会出问题,单独拉出来。
(每一个字符归属于哪一行是固定的,也可以用index判断的方式直接分配)

首先创建一个StringBuffer数组,存储每一行的string
把每个字符扔进它应该去的行的末尾,通过getIndex函数获取行。
是一个完全的模拟过程。(这道题有数学方法,如果有必要后续扩展一下)

class Solution {
    boolean flag= false;
    public String convert(String s, int numRows) {
        ArrayList<StringBuilder> resArray = new ArrayList<>();
        for(int i =0;i<numRows;i++){
            resArray.add(new StringBuilder());
        }
        int index= 0;
        boolean flag =false;
        //如果只有一行,不进循环,进了找index会报错,因为0 == numRows-1;
        if(numRows==1)return s;
        for(int i =0;i<s.length();i++){
            resArray.get(index).append(s.charAt(i));
            index = getNextIndex(index,numRows);
        }
        StringBuilder res = new StringBuilder();
        for(int i = 0;i < resArray.size();i++){
            res.append(resArray.get(i));
        }
        return res.toString();
    }
    public int getNextIndex(int curIndex, int numRows){
        if(flag == false){
            if(curIndex == numRows-1){
                flag = true;
                return curIndex-1;   
            }else{
                return curIndex+1;
            }
        }
        if(flag == true){
            if(curIndex == 0){
                flag = false;
                return curIndex+1;   
            }else{
                return curIndex-1;
            } 
        }
        return curIndex;
    }
}

9 回文数

反转一半,注意末尾为0的时候单独处理
这个方法切记!小于0和以0结尾的数字要提前处理。但是x=0是可以进循环处理的。
方法就是用数学方法逐位计算后i位reverse的值,一直到x<=reverse。

如果是相等或者x==reverse/10说明偶数回文或者奇数回文。
其他情况x小于reverse切不是奇数回文到情况,就是不回文

class Solution {
    public boolean isPalindrome(int x) {
        int reverse = 0;
        if(x<0||(x%10==0&&x!=0))return false;
        while(x>reverse){
            int num = x%10;
            x=(x-num)/10;
            reverse=reverse*10+num;
        }
        if(x==reverse||x==reverse/10)return true;
        else return false;
    }
}

11 盛水最多的容器

双指针,这个题可以当作一个方法记下来,相似的题目还有很多。
这是一个类似贪心到思路。
每次移动两个端点,只有移动较高的无论如何不能拿到更大的容积。(高固定,宽变小)
所以每次移动较矮的。相等时,移动哪一个都可以。(如果存在更大的容积,端点一定在二者之间而不是二者之一)

class Solution {
    public int maxArea(int[] height) {
        int res= 0;
        int left=0,right = height.length-1;
        while(left<right){
            res = Math.max(res,(right-left)*Math.min(height[right],height[left]));
            if(height[right]<height[left])
                right--;
            else
                left++;
        }
        return res;
    }
}

12 整数转罗马数字

查表。本来觉得需要一个num=0退出的判断,其实不需要,因为for循环中如果小于1直接就i+=出去了。
两个数组,由大到小。
遍历时依次比较,如果num包含一个value,那么就添加相应的字符,并且更新剩余num,如果小于,就向后移动。
更新时如果num被更新,i–。保证仍然对当前value比较,一直减到num为0,自然退出循环。

class Solution {
    public String intToRoman(int num) {
        int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};    
        String[] symbols = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
        StringBuilder res = new StringBuilder();
        for(int i =0;i<values.length;i++){
            if(num>=values[i]){
                res.append(symbols[i]);
                num=num-values[i];
                i--;
            }
        }
        return res.toString();
    }
}

13 罗马数字转整数

弄个hashmap比较好查一些。(数量很少直接写函数switch case判断也可以)
charAt(i)究竟是加是减,取决与i+1处是大是小。
所以分情况,并且i=length-1时可以和正常的加情况合并。

class Solution {
    public int romanToInt(String s) {
        int[] values = {1000, 500, 100, 50, 10, 5,1};    
        char [] symbols = {'M','D','C','L','X','V','I'};
        HashMap <Character,Integer> map = new HashMap<>();
        int res = 0;
        for(int i =0;i<symbols.length;i++){
            map.put(symbols[i],values[i]);
        }
        for(int i =0;i<s.length();i++){
            if(i+1<s.length()&&map.get(s.charAt(i+1))>map.get(s.charAt(i))){
                res-=map.get(s.charAt(i));
            }else{
                res+=map.get(s.charAt(i));
            }
        }
        return res;
    }
}

14 最长公共前缀

纵向考虑。
可以分治,但是时间复杂度没有优化。
用一个res记录最后结果
对str[0]的i到len循环
比较其他str对应位置是否一致,
如果一致,该位添加到res,一旦出现不一致返回结果。

class Solution {
    public String longestCommonPrefix(String[] strs) {
        StringBuilder res = new StringBuilder();
        if(strs.length == 0) return "";
        int len = strs[0].length();
        for(int i =0;i<len;i++){
            char temp = strs[0].charAt(i);
            for(int j=1;j<strs.length;j++){
                if(strs[j].length()<=i||strs[j].charAt(i)!=temp){
                    return res.toString();
                }
            }
            res.append(temp);
        }
        return res.toString();
    }
}

15 三数之和

排序双指针

为了不重复,排序后遍历第一层循环意味着第一个数,然后双指针找其他两个数
双指针起点为i+1终点为len。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        for(int i=0;i<nums.length;i++){
            int left = i+1;
            int right = nums.length-1;
            while(left<right){
                if(nums[left]+nums[right]==-nums[i]){
                    ArrayList<Integer> tempRes = new ArrayList<>();
                    tempRes.add(nums[left]);
                    tempRes.add(nums[right]);
                    tempRes.add(nums[i]);
                    if(!res.contains(tempRes))
                        res.add(tempRes);
                    left++;
                }else{
                    if(nums[left]+nums[right]<-nums[i]){
                        left++;
                    }else{
                        right--;
                    }
                }
            }
        }
        return res;
    }
}

16 最接近的三数之和

基于15 简单修改了一下。

与15的区别在于不需要维护多个结果,只需要维护一个最接近的结果。
如果遇到==target直接返回即可。

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        int res = nums[0]+nums[1]+nums[2];
        Arrays.sort(nums);
        for(int i=0;i<nums.length;i++){
            int left = i+1;
            int right = nums.length-1;
            while(left<right){
                if(nums[left]+nums[right]+nums[i] == target){
                    return target;
                }else{
                    if(Math.abs(target-res)>Math.abs(target-(nums[left]+nums[right]+nums[i]))){
                        res = nums[left]+nums[right]+nums[i];
                    }
                    if(nums[left]+nums[right]+nums[i]<target){
                        left++;
                    }else{
                        right--;
                    }

                }
            }
        }
        return res;
    }
}

17 电话号码的字母组合

DFS,用string恢复现场比较简单,可以优化为stringbuilder,注意一下添加和删除。
正常的DFS逻辑,搜索的时候每层变化的内容是当前是str和当前数位在digits中的index
每层循环遍历这个字符可能代表的字母。
出口为index或者temp长度等于digits的len

class Solution {
    ArrayList<String> res = new ArrayList<>();
    public List<String> letterCombinations(String digits) {
        if(digits.length()==0)return res;
        String [] table = new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        search(digits,table,0, "");
        return res;
    }
    public void search(String digits,String[]table,int index, String tempRes){
        if(tempRes.length()==digits.length()){
            res.add(tempRes);
            return ;
        }
        for(int i = 0 ;i<table[digits.charAt(index)-'0'].length();i++){
            String newRes = tempRes+table[digits.charAt(index)-'0'].charAt(i);
            search(digits,table,index+1,newRes);
        }
    }
}

18 四数之和

排序+两层循环+双指针
循环中剪枝。
易错的点在于,不适用set和contains来去重。

思路:
排序
两层循环选定第一个数和第二个数(有序的),结果数无序的所以第二个数必须大于第一个数,否则结果会重复。
双指针找另外两个数,左指针也要大于第二个数。
注意的点在于,第一个数的选择要去重,选过1不能再选1,由于排序了,所以挨着。
第二个数也要去重,和第一个数一样。
双指针去重比较容易,只需要在命中target时移动时判断left++一直到不等的位置即可。

另外,剪枝最后考虑,如果最小的可能已经超过,break。(后面只会更大)
如果最大的可能已经小于,continue。(后面还可能继续变大)

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList<>();
        if(nums.length<4) return res;
        //先排序
        Arrays.sort(nums);
        for(int i =0;i<nums.length-3;i++){
            //防止重复
            if(i-1>=0&&nums[i]==nums[i-1])continue;
            //两个剪枝条件
            if(nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target)break;
            if(nums[i]+nums[nums.length-1]+nums[nums.length-2]+nums[nums.length-3]<target)continue;
            for(int j=i+1;j<nums.length-2;j++){
                //防止重复
                if(j>i+1&&nums[j]==nums[j-1])continue;
                //两个剪枝条件
                if(nums[i]+nums[j]+nums[j+1]+nums[j+2]>target)break;
                if(nums[i]+nums[j]+nums[nums.length-2]+nums[nums.length-1]<target)continue;
                //双指针
                int left=j+1,right=nums.length-1;
                while(left<right){
                    int temp =nums[i]+nums[j]+nums[right]+nums[left];
                    if(temp<target){
                        left++;
                    }else if(temp>target){
                        right--;
                    }else{
                        res.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                        //防止重复
                        while(left+1<right&&nums[left+1]==nums[left])
                            left++;
                        left++;
                    }
                }
            }
        }
        return res;
    }
}

19 删除链表的倒数第N个结点

快慢指针,一次遍历。
快指针先走n步,
慢指针和快指针一起走。
当快指针的下一个节点为空时
慢指针的下一个节点就是倒数第N个节点,删除即可

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next=head;
        ListNode fast = dummy,slow=dummy;
        while(n>0){
            fast=fast.next;
            n--;
        }
        while(fast.next!=null){
            fast=fast.next;
            slow=slow.next;
        }
        slow.next=slow.next.next;
        return dummy.next;
    }
}

20 有效的括号

如果括号再多几种就用hashmap存一下键值对比较容易写。
思路简单。
左括号进栈,右括号检查是否匹配。
如果s遍历完了,stack里面应该清空才是有效,否则无效。

class Solution {
    public boolean isValid(String s) {  
        Stack<Character> stk = new Stack<>();
        for(int i = 0;i<s.length();i++){
            char tmp = s.charAt(i);
            if(tmp=='('||tmp=='{'||tmp=='['){
                stk.push(tmp);
            }else{
                if(stk.size()==0)return false;
                char tmpPop = stk.pop();
                if(!(tmpPop=='('&&tmp==')'||tmpPop=='{'&&tmp=='}'||tmpPop=='['&&tmp==']')){
                    return false;
                }
            }
        }
        if(stk.size()==0)return true;
        return false;
    }
}

21 合并两个有序链表

写来写去还是常规思路写的最舒服
合并就是这么写的,add有一些写法上的技巧避免写两个while。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode();
        ListNode cur = dummy;
        while(l1!=null&&l2!=null){
            if(l1.val<=l2.val){
                cur.next = l1;
                l1=l1.next;
            }else{
                cur.next = l2;
                l2=l2.next;
            }
            cur = cur.next;
        }
        if(l1==null){
            cur.next=l2;
        }else{
            cur.next = l1;
        }
        return dummy.next;
    }
}

22 括号生成

dfs
注意递归出口的判断
这里用了stringbuilder而不用string,可以避免频繁复制
用回溯+恢复现场的思路

思路就是记录左右括号的num

每层可以加做括号也可以加右括号。

在前面剪枝,left>n直接返回,left<right 直接返回。
如果temp的长度到了2n 添加到结果中即可。

StringBuilder 的这个方法用的比较少,记一下。deleteCharAt()

class Solution {
    ArrayList<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        generater(n ,new StringBuilder(),0, 0);
        return res;
    }
    public void generater(int n, StringBuilder tempStr, int leftNum, int rightNum){
        if(leftNum>n) return ;
        if(leftNum<rightNum) return ;
        if(tempStr.length()==n*2){
            res.add(tempStr.toString());
            return ;
        }
        tempStr.append('(');
        generater(n,tempStr,leftNum+1,rightNum);
        tempStr.deleteCharAt(tempStr.length()-1);

        tempStr.append(')');
        generater(n,tempStr,leftNum,rightNum+1);
        tempStr.deleteCharAt(tempStr.length()-1);
    }
}

23 合并K个有序链表

基于合并两个有序链表做了一下
首先写一个合并两个有序链表。
然后基于这个用分治的方式合并K个。
K分奇偶情况,但是可以统一。
两两合并,唯一差异在于size为奇数,最后一轮没有i+1合并,直接保留下来。
合并之后,新的newlist[]作为参数递归调用。
如果newlistsize为1可以返回了。
用一个全局的res存储结果。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    ListNode res = new ListNode(0);
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists.length==0)return null;
        if(lists.length==1){
            res= lists[0];
            return res;
        }
        int size = lists.length;
        int newSize=0;
        if(size%2==0) newSize=size/2;
        else newSize=size/2+1;
        ListNode [] newList = new ListNode[newSize];
        for(int i =0;i<size;i+=2){
            if(size%2==1&&i==size-1){
                newList[i/2]=lists[i];
            }else{
                newList[i/2]=mergeTwoLists(lists[i],lists[i+1]);
            }
        }
        mergeKLists(newList);
        return res;
    }
   public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode();
        ListNode cur = dummy;
        while(l1!=null&&l2!=null){
            if(l1.val<=l2.val){
                cur.next = l1;
                l1=l1.next;
            }else{
                cur.next = l2;
                l2=l2.next;
            }
            cur = cur.next;
        }
        if(l1==null){
            cur.next=l2;
        }else{
            cur.next = l1;
        }
        return dummy.next;
    }
}

24 两两交换链表中的节点

三个指针,做好交换交换和移动的顺序即可,最好画个图理解一下。
需要注意的是先找到next位置
然后cur.next.next先变化,然后cur.next,然后pre找位置,cur找位置。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) { 
        ListNode dummy = new ListNode(0);
        dummy.next=head;
        if(head == null)return null;
        ListNode cur = dummy.next;
        ListNode pre = dummy;
        ListNode next;
        while(cur!=null&&cur.next!=null){
            next = cur.next.next;
            pre.next=cur.next;
            cur.next.next=cur;
            cur.next=next;

            pre=cur;
            cur=pre.next;
        }
        return dummy.next;
    }
}

25 K个一组翻转链表

首先依旧有一个dummy节点,叫hair是因为题解这么给的,有点意思。
首先K个一组翻转,可以想到需要两个指针,一个pre一个next,用于翻转后重新连接回来。
然后每组翻转的时候,就需要给一个head给一个tail,返回新的head和新的tail和外面的pre和next相连即可。
每轮循环先找到tail,这个过程可以判断是否够K个,不够直接返回即可。
找到tail之后,next也就找到了。
循环条件就是head不为空,继续往后走。

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode hair = new ListNode(0);
        hair.next = head;
        ListNode pre = hair;

        while (head != null) {
            ListNode tail = pre;
            // 查看剩余部分长度是否大于等于 k
            for (int i = 0; i < k; ++i) {
                tail = tail.next;
                if (tail == null) {
                    return hair.next;
                }
            }
            ListNode nex = tail.next;
            ListNode[] reverse = myReverse(head, tail);
            head = reverse[0];
            tail = reverse[1];
            // 把子链表重新接回原链表
            pre.next = head;
            tail.next = nex;
            pre = tail;
            head = tail.next;
        }

        return hair.next;
    }

    public ListNode[] myReverse(ListNode head, ListNode tail) {
        ListNode prev = tail.next;
        ListNode p = head;
        while (prev != tail) {
            ListNode nex = p.next;
            p.next = prev;
            prev = p;
            p = nex;
        }
        return new ListNode[]{tail, head};
    }
}

26 删除排序数组中的重复项

原地。
原地的思路可以考虑双指针。

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length==0)return 0;
        int i,j=1;
        for(i=1,j=1;i<nums.length;i++){
            if(nums[i]!=nums[i-1]){
                nums[j]=nums[i];
                j++;
            }
        }
        return j;
    }
}

27 移除元素

注意原地双指针即可

class Solution {
    public int removeElement(int[] nums, int val) {
        int i=0,j=0;
        for(i=0,j=0;i<nums.length;i++){
            if(nums[i]!=val){
                nums[j]=nums[i];
                j++;
            }
        }
        return j;
    }
}

28 strstr()

TODO 后续补充一下kmp

class Solution {
    public int strStr(String haystack, String needle) {
        int res= -1;
        if(haystack.length()<needle.length())return res;
        if(needle.equals("")) return 0;
        for(int i =0;i<=haystack.length()-needle.length()&&i<haystack.length();i++){
            int j =0;
            int ii=i;
            while(ii<haystack.length()&&j<needle.length()&&haystack.charAt(ii)==needle.charAt(j)){
                if(j==needle.length()-1){
                    return i;
                }
                ii++;
                j++;
            }
        }
        return res;
    }
}

29 两数相除

这里有两个需要积累的方法
一个是全部转换为负数防止溢出时的特殊判断
一个是减法时指数增加的思路(快速幂)

class Solution {
    public int divide(int dividend, int divisor) {
        int res = 0;
        if(dividend==Integer.MIN_VALUE&&divisor==-1)return Integer.MAX_VALUE;
        boolean flag = (dividend<0&&divisor<0||dividend>0&&divisor>0);
        dividend =-Math.abs(dividend);
        divisor = -Math.abs(divisor);
        while(dividend<=divisor){
            int temp = divisor;
            int c=1;
            while(dividend-temp<=temp){
                temp=temp<<1;
                c= c<<1;
            }
            res+=c;
            dividend-=temp;
        }
        return flag==true?res:-res;
    }
}

31 下一个排列

思路清晰之后再动笔会比较好,这次再调试过程中矫正思路花费时间较多

class Solution {
    public void nextPermutation(int[] nums) {
        //用于记录当前被交换的位置
        int [] temp = new int[2];
        //从最后一位开始考虑能否被交换
        for(int k=nums.length-1;k>=0;k--){
            temp[0]=nums[k];
            temp[1]=k;
            //从后向前依次考虑是否比被交换的数大,如果大就交换
            for(int i =nums.length-1;i>k;i--){
                if(nums[i]>temp[0]){
                    int tmpNum = nums[i];
                    nums[i]=temp[0];
                    nums[temp[1]]=tmpNum;
                    //交换之后数必变大,将交换位置之后的排序成为最小
                    int []newArray = Arrays.copyOfRange(nums,temp[1]+1,nums.length);
                    Arrays.sort(newArray);
                    //得到的结果就是比原数大,但是最小的数
                    for(int j=temp[1]+1;j<nums.length;j++){
                        nums[j]=newArray[j-temp[1]-1];
                    }
                    return ;
                }
            }
        }
        //如果没找到就排序返回
        Arrays.sort(nums);
        return ;
    }
}

32 最长有效括号

暴力

class Solution {
    int res = 0;
    public int longestValidParentheses(String s) {
        for(int i =0;i<s.length()-1;i++){
            if(s.length()-i<res){
                return res;
            }
            getLongest(s,i);
        }
        return res;
    }
    public void getLongest(String s,int index){
        int left=0,right=0,count=0;
        for(int i =index;i<s.length();i++){
            if(s.charAt(i)=='('){
                left++;
                count++;
            }else{
                if(right==left){
                    return ;
                }
                right++;
                count++;
            }
            if(left==right){
                res = Math.max(count,res);
            }
        }
    }
}

两侧贪心,双侧的目的是解决((()这类情况

class Solution {
    public int longestValidParentheses(String s) {
        int left = 0,right =0,res=0;
        for(int i =0;i<s.length();i++){
            if(s.charAt(i)=='(')left++;
            if(s.charAt(i)==')')right++;
            if(left==right)res = Math.max(res,left+right);
            if(right>left){
                left=0;
                right=0;
            }
        }
        left=0;
        right=0;
        for(int i =s.length()-1;i>=0;i--){
            if(s.charAt(i)=='(')left++;
            if(s.charAt(i)==')')right++;
            if(left==right)res = Math.max(res,left+right);
            if(right<left){
                left=0;
                right=0;
            }
        }
        return res;
    }
}

DP
状态转移方程比较难想
主要是当上一个字符为’)'时要向前追溯dp[i-1]长度
s.charAt(i - dp[i - 1] - 1) == '('保证了dp[i-1]是一个有效的区间 且dp[i]有前置左括号匹配.
所有不满足条件的默认dp[i]为0

class Solution {
    public int longestValidParentheses(String s) {
        int res = 0;
        int [] dp = new int[s.length()];
        for(int i =1;i<s.length();i++){
            if(s.charAt(i)==')'){
                if(s.charAt(i-1)=='('){
                    dp[i]=(i-2>=0?dp[i-2]:0)+2;
                }else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                    dp[i]=dp[i-1]+(i-dp[i-1]>=2?dp[i-dp[i-1]-2]:0)+2;
                }
            }
            res = Math.max(res,dp[i]);
        }
        return res;
    }
}

33 搜索旋转排序数组

二分法,好像和一个旋转数组很像.
二分法最后还是用了提前结束然后两个候选判断的方式
虽然麻烦一点,但是比较通用

class Solution {
    public int search(int[] nums, int target) {
        int finalMid = findMid(nums);
        int left = 0, right = nums.length-1;
        if(target==nums[right])return right;
        //在前半段找
        if(target<nums[right]){
            left = finalMid;
            while(left+1<right){
                int mid = left+(right-left)/2;
                if(target==nums[mid])return mid;
                if(target>nums[mid])
                    left=mid;
                else
                    right=mid;
            }
            if(target==nums[left])return left;
            if(target==nums[right])return right;
        //在后半段找
        }else{
            right = finalMid;
            while(left+1<right){
                int mid = left+(right-left)/2;
                if(target==nums[mid])return mid;
                if(target>nums[mid])
                    left=mid;
                else
                    right=mid;
            }
            if(target==nums[left])return left;
            if(target==nums[right])return right;
        }
        return -1;
    }
    //二分法找到右侧的起点
    public int findMid(int[]nums){
        int left =0,right = nums.length-1;
        while(left+1<right){
            int mid = left+(right-left)/2;
            if(nums[mid]<nums[right])
                right=mid;
            else if(nums[mid]>nums[right])
                left=mid;
            else if(nums[mid] == nums[right])
                right--;
        }
        if(nums[left]>nums[right]){
            return right;
        }else{
            return left;
        }
    }
}

34 在排序数组中查找第一个和最后一个位置

注意特殊case的情况

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0,right = nums.length-1;
        if(nums.length==0)return new int[]{-1,-1};
        while(left+1<right){
            int mid = left+(right-left)/2;
            if(nums[mid]>=target)right =mid;
            else{
                left = mid;
            }
        }
        int leftIndex=0,rightIndex=0;
        if(nums[left]==target)leftIndex=left;
        else if(nums[right]==target)leftIndex =right;
        else leftIndex =-1;
        left = 0;
        right = nums.length-1;
        while(left+1<right){
            int mid =left+ (right-left)/2;
            if(nums[mid]<=target)left=mid;
            else{
                right=mid;
            }
        }
        //这里的顺序必须要调换一下,否则[2,2]会出问题
        if(nums[right]==target)rightIndex=right;
        else if(nums[left]==target)rightIndex=left;
        else rightIndex=-1;
        return  new int []{leftIndex,rightIndex};
    }
}

35 搜索插入位置

class Solution {
    public int searchInsert(int[] nums, int target) {
        for(int i = 0;i<nums.length;i++){
            if(nums[i]>=target)return i;
        }
        return nums.length;
    }
}

36 有效的数独

三个标志数组,注意一下frame数组的索引即可

class Solution {
    public boolean isValidSudoku(char[][] board) {
        boolean colFlag [][] = new boolean[9][9];
        boolean rowFlag [][] = new boolean[9][9];
        boolean frameFlag [][] = new boolean[9][9];
        for(int i =0;i<9;i++){
            for(int j=0;j<9;j++){
                if(board[i][j]=='.')continue;
                int num = board[i][j]-'1';
                if(colFlag[j][num]==false)colFlag[j][num]=true;
                else return false;
                if(rowFlag[i][num]==false)rowFlag[i][num]=true;
                else return false;
                if(frameFlag[i/3*3+j/3][num]==false)frameFlag[i/3*3+j/3][num]=true;
                else return false;
            }
        }
        return true;
    }
}

38 外观数列

可以考虑不需要数组

class Solution {
    public String countAndSay(int n) {
        String [] strArray = new String[n];
        strArray[0]="1";
        for(int i =1;i<n;i++){
            StringBuilder tempStr = new StringBuilder();
            char lastChar ='.';
            int count=0;
            for(int j=0;j<strArray[i-1].length();j++){
                if(j==0){
                    count =1;
                    lastChar=strArray[i-1].charAt(j);
                }else{
                    if(strArray[i-1].charAt(j)==lastChar){
                        count++;
                    }else{
                        tempStr.append(String.valueOf(count));
                        tempStr.append(lastChar);
                        count =1;
                        lastChar=strArray[i-1].charAt(j);
                    }
                }
                if(j==strArray[i-1].length()-1){
                    tempStr.append(String.valueOf(count));
                    tempStr.append(strArray[i-1].charAt(j));
                }
            }
            strArray[i]=tempStr.toString();
        }
        return strArray[n-1];
    }
}

不用数组(还是很慢)

class Solution {
    public String countAndSay(int n) {
        String  strArray = "1";
        for(int i =1;i<n;i++){
            StringBuilder tempStr = new StringBuilder();
            char lastChar ='.';
            int count=0;
            for(int j=0;j<strArray.length();j++){
                if(j==0){
                    count =1;
                    lastChar=strArray.charAt(j);
                }else{
                    if(strArray.charAt(j)==lastChar){
                        count++;
                    }else{
                        tempStr.append(String.valueOf(count));
                        tempStr.append(lastChar);
                        count =1;
                        lastChar=strArray.charAt(j);
                    }
                }
                if(j==strArray.length()-1){
                    tempStr.append(String.valueOf(count));
                    tempStr.append(strArray.charAt(j));
                }
            }
            strArray=tempStr.toString();
        }
        return strArray;
    }
}

39 组合总和

回溯+排序剪枝

class Solution {
    List<List<Integer>>res= new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        //排序为了剪枝方便一些
        Arrays.sort(candidates);
        getCandi(candidates,target,0,new ArrayList<Integer>());
        return res;
    }
    public void getCandi(int[] candidates,int target,int index, ArrayList<Integer>tempRes){
        if(target==0){
            res.add((ArrayList)tempRes.clone());
        }
        //加个剪枝 
        if(target<0||target<candidates[index])return ;
        for(int i = index;i<candidates.length;i++){
            tempRes.add(candidates[i]);
            getCandi(candidates,target-candidates[i],i,tempRes);
            tempRes.remove((Integer)candidates[i]);
        }
    }
}

40 组合总和II

同一个元素只能用一次
基于39 修改结果条件即可

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        //排序为了剪枝方便一些
        Arrays.sort(candidates);
        getCandi(candidates,target,0,new ArrayList<Integer>());
        return res;
    }
    public void getCandi(int[] candidates,int target,int index, ArrayList<Integer>tempRes){
        if(target==0){
            res.add((ArrayList)tempRes.clone());
        }
        //加个剪枝 
        //修改点3 index+1可能越界,这里剪枝补充一个index判断
        if(target<0||index>=candidates.length||target<candidates[index])return ;
        int last = candidates[index]-1;
        for(int i = index;i<candidates.length;i++){
            //修改点1:去重,相同的元素不能两次出现在同一个位置(2 4和2 4是重复  但是22可以出现)
            if(last==candidates[i])continue;
            last = candidates[i];
            tempRes.add(candidates[i]);
            //修改点2:同一个元素使用一次后index后移
            getCandi(candidates,target-candidates[i],i+1,tempRes);
            tempRes.remove((Integer)candidates[i]);
        }
    }
}

41 缺失的第一个正数

归位
如果被移动位置已经是正确元素,或者位置非法,或目标位置已经是正确元素,什么都不做
否则交换。

class Solution {
    public int firstMissingPositive(int[] nums) {
        for(int i=0;i<nums.length;i++){
            if(nums[i]==i+1||nums[i]>nums.length||nums[i]<=0||nums[nums[i]-1]==nums[i]){

            }else{
                int temp = nums[nums[i]-1] ;
                nums[nums[i]-1]=nums[i];
                nums[i]=temp;
                i--;
            }
        }
        for(int i =0;i<nums.length;i++){
            if(nums[i]!=i+1) return i+1;
        }
        return nums.length+1;
    }
}

42 接雨水

和盛水最多的容器场景差不多,但是思路不大一样。
最开始了考虑左右增加一个0高度
实际上在左右维护最高值的时候,可以直接添加进去

class Solution {
    public int trap(int[] height) {
        int [] left = new int[height.length];
        int [] right = new int [height.length];
        int res = 0;
        int leftMax = 0,rightMax =0;
        for(int i =0;i<height.length;i++){
            left[i]=leftMax;
            leftMax=Math.max(leftMax,height[i]);
        }
        for(int i =height.length-1;i>=0;i--){
            right[i]=rightMax;
            rightMax=Math.max(rightMax,height[i]);
        }
        for(int i=0;i<height.length;i++){
            int num = Math.min(left[i],right[i])-height[i];
            if(num>=0){
                res+=num;
            }
        }
        return res;
    }
}

43 字符串相乘

这里使用了官方题解的方法2
设num1长度为m num2长度为n
m位数乘n位数可能等于m+n位,也可能等于m+n-1位。?
num1的个位索引为m-1,num2个位索引为n-1.

用res数组暂存结果。长度为m+n。
从右往左分别代表个位(十的0次方)、十位等等一直到 res[0]数位代表10的 m+n-1次方。
最低位相乘在个位,最高位相乘在m+n-1位,m+n位只能进位得到
最后判断一下进位即可。

class Solution {
    public String multiply(String num1, String num2) {
        int [] res = new int[num1.length()+num2.length()];
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        for(int i=num2.length()-1;i>=0;i--){
            int x = num2.charAt(i)-'0';
            for(int j =num1.length()-1;j>=0;j--){
                int y = num1.charAt(j)-'0';
                res[i+j+1]+=x*y;
            }
        }
        for(int i=res.length-1;i>0;i--){
            res[i-1]+=res[i]/10;
            res[i]=res[i]%10;
        }
        int index = (res[0]==0?1:0);
        StringBuilder ans= new StringBuilder();
        while(index<res.length){
            ans.append(res[index]);
            index++;
        }
        return ans.toString();
    }
}

45 跳跃游戏II

DP

class Solution {
    public int jump(int[] nums) {
        if(nums.length==0)return 0;
        int[]dp = new int[nums.length];
        for(int i=1;i<nums.length;i++){
            dp[i]=nums.length;
        }
        for(int i=0;i<nums.length;i++){
            for(int j=i+1;j<nums.length&&j-i<=nums[i];j++){
                dp[j]=Math.min(dp[i]+1,dp[j]);
            }
        }
        return dp[nums.length-1];
    }
}

贪心

class Solution {
    public int jump(int[] nums) {
        int rightIndex = 0;
        int leftIndex=0;
        int count = 0;
        int newRightIndex= rightIndex;
        while(rightIndex<nums.length-1){
            while(leftIndex<=rightIndex){
                newRightIndex=Math.max(leftIndex+nums[leftIndex],newRightIndex);
                leftIndex++;
            }
            count++;
            rightIndex=newRightIndex;
        }
        return count;
    }
}

46 全排列

无重复数字全排列,随便操作

class Solution {
    List<List<Integer>> res= new ArrayList<>();
    boolean []flag;
    public List<List<Integer>> permute(int[] nums) {
        flag = new boolean[nums.length];
        helper(nums,new ArrayList<>());
        return res;
    }
    public void helper(int[]nums, ArrayList<Integer> tempRes){
        if(tempRes.size()==nums.length)
            res.add((ArrayList)tempRes.clone());
        for(int i = 0;i<nums.length;i++){
            if(flag[i]==true)continue;
            else{
                flag[i]=true;
                tempRes.add(nums[i]);
                helper(nums,tempRes);
                tempRes.remove((Integer)nums[i]);
                flag[i]=false;
            }
        }
    }
}

47 全排列II

关于lastNum的判断优化了一下
不需要建一个int存储
用flag判断即可区分 是同层还是上下层


class Solution {
    List<List<Integer>> res= new ArrayList<>();
    boolean []flag;
    public List<List<Integer>> permuteUnique(int[] nums) {
        flag = new boolean[nums.length];
        Arrays.sort(nums);
        helper(nums,new ArrayList<>());
        return res;
    }
    public void helper(int[]nums, ArrayList<Integer> tempRes){
        if(tempRes.size()==nums.length)
            res.add((ArrayList)tempRes.clone());
        for(int i = 0;i<nums.length;i++){
            if(flag[i]==true||(i>0&&nums[i]==nums[i-1]&&flag[i-1]==false))continue;
            else{
                flag[i]=true;
                tempRes.add(nums[i]);
                helper(nums,tempRes);
                //这里remove用索引好于用元素
                tempRes.remove(tempRes.size()-1);
                flag[i]=false;
            }
        }
    }
}

48 90度旋转图像

对角变换+轴对称

class Solution {
    public void rotate(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;

        for(int i =0;i<m;i++){
            for(int j = i+1;j<n;j++){
                int temp = matrix[i][j];
                matrix[i][j]=matrix[j][i];
                matrix[j][i]=temp;
            }
        }
        for(int i =0;i<m;i++){
            for(int j = 0;j<n/2;j++){
                int temp = matrix[i][j];
                matrix[i][j]=matrix[i][n-j-1];
                matrix[i][n-1-j]=temp;
            }
        }
        return ;
    }
}

49 字母异位词分组

排序的思路
用排好序的String作为hashmap的key

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> res = new ArrayList<>();
        HashMap<String,Integer> strToIndex = new HashMap<>();
        for(int i =0;i<strs.length;i++){
            char [] strArray = strs[i].toCharArray();
            Arrays.sort(strArray);
            String key = new String(strArray);
            if(strToIndex.containsKey(key)){
                res.get(strToIndex.get(key)).add(strs[i]);
            }else{
                List<String> temp = new ArrayList<>();
                temp.add(strs[i]);
                res.add(temp);
                strToIndex.put(key,res.size()-1);
            }
        }
        return res;
    }
}

POW(X,N)

注意负数和越界

class Solution {
    public double myPow(double x, int n) {
        double res= 1;
        boolean flag= false;
        if(n==0)return 1;
        if(n<0){
            if(n==Integer.MIN_VALUE){
                if(x==1)return 1;
                if(x==-1)return 1;
                return 0;
            }
            n=-n;
            flag = true;
        }
        while(n>1){
            if(n%2==0){
                n=n/2;
                x=x*x;
            }else{
                res*=x;
                n=n-1;
            }
        }
        res*=x;
        if(flag==true)return 1/res;
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值