LeetCode刷题(21 - 30)

21.合并两个有序链表

方法1:利用一个额外的链表
易错:
1.ListNode dummy = new ListNode(0); // 哑结点一定是0;
2. ListNode cur = dummy; //在进行遍历的时候要赋值给另外一个

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode p1 = l1,p2 = l2;
        ListNode cur = dummy;
        while(p1!=null&&p2!=null){
            if(p1.val < p2.val){
                cur.next = p1;
                p1 = p1.next;
            }else{
                cur.next  =p2;
                p2 = p2.next;
            } 
            cur = cur.next;
        }
        ListNode res = (p1 ==null)?p2:p1;
        cur.next = res;
        return dummy.next;
    }
}

方法2:直接使用递归进行解决

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1==null){
            return l2;
        }
        else if(l2==null){
            return l1;
        }
        else if(l1.val<l2.val){
            l1.next=mergeTwoLists(l1.next,l2);
            return l1;
        }else{
            l2.next=mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
}

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

注意:第一个元素nums[0]:是直接没有动的。

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length==0) return 0;
        int i=0;
        for(int j=1;j<nums.length;j++){
            if(nums[i]!=nums[j]){
                i++;  //只有两个指针不相等的时候,才加1.所以最后的i+1能返回相应的长度。
                nums[i]=nums[j];  //改变之后i之前的元素就是应该返回的不重复的数组。
            }
        }
        return i+1;
        
    }
}

27.移除元素

注意:这道题和上道题都是使用自身数组,替换原数组的方式,但是要注意的是i++的位置。

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

28.实现strStr()

还有一种直接使用index的方法,感觉有点智障。

class Solution {
    public int strStr(String haystack, String needle) {
        int l1 = haystack.length(),l2 = needle.length();
        if(l1<l2){
            return -1;
        }else if(l2 ==0){
            return 0;
        }
        for(int i= 0;i<= l1-l2;i++){
           if(haystack.substring(i,i+l2).equals(needle)) {
                return i;
            }
        }
        return -1;
    }
}

29.两数相除

题目中要求不能用乘法和除法,但我们可以对减数进行倍增操作。对于dividend,如果dividend - divisor > 0,那么我们下一次减去的数不是divisor,而是divisor + divisor,这样倍增减数的操作可以使我们的时间复杂度到达O(log(dividend / divisor))的级别。而空间复杂度依然保持O(1)的级别。

对于最乐观的情况,假设divisor + divisor * 2 + divisor * 2 ^ 2 + divisor * 2 ^ 3 + … + divisor * 2 ^ n = dividend。那么我们总共只进行了n次操作,这个时间复杂度显然就是O(log(dividend / divisor))。而如果divisor + divisor * 2 + divisor * 2 ^ 2 + divisor * 2 ^ 3 + … + divisor * 2 ^ n > dividend,但是divisor + divisor * 2 + divisor * 2 ^ 2 + divisor * 2 ^ 3 + … + divisor * 2 ^ (n - 1) < dividend,那么我们就要以dividend - (divisor + divisor * 2 + divisor * 2 ^ 2 + divisor * 2 ^ 3 + … + divisor * 2 ^ (n - 1))为被减数进行重复的操作,即从divisor开始减起。直至我们的被减数小于divisor为止。

而对于结果的值,我们需要根据flag还有是否越界来讨论清楚各种情况。

public class Solution {
 
	public int divide(int dividend, int divisor) {
        int sign =1;
        //if((dividend>0)&&(divisor<0) || (dividend<0)&&(divisor>0)) sign = -1;
        if((dividend>0)^(divisor>0)) sign = -1;  //考虑结果是负数
        long ldividend =Math.abs((long)dividend);
        long ldivisior = Math.abs((long)divisor);  //考虑越界的问题
        if(ldivisior > ldividend || ldividend == 0) return 0;//这里不用考虑除数为0的情况,因为Java中自动会报异常。
        long lres = divide(ldividend,ldivisior);
        int res =0;
        if(lres > Integer.MAX_VALUE){  //如果结果越界之后
            res = (sign ==1 )?Integer.MAX_VALUE:Integer.MIN_VALUE;
            //res = (sign ==1 )?Integer.MAX_VALUE:Integer.MIN_VALUE;
        }else res =(int)(sign*lres);
        return res;

    }
        public long divide(long ldividend,long ldivisior){
            if(ldividend < ldivisior) return 0;
            long sum =ldivisior;
            long multiple =1; //表示的是倍数
            while((sum+sum) <= ldividend){
                sum += sum;
                multiple +=multiple;
            }
            return multiple + divide(ldividend-sum,ldivisior);
        }

    }

30.串联所有单词的子串

没有弄清楚为啥在每次遍历的时候需要重新copy一次map。
—其实防止,万一在一次循环中,只有一个单词匹配的时候,如果不重新copy的话,就会影响原来的map中key值所对应的次数。。

map.getOrDefault()意思就是当Map集合中有这个key时,就使用这个key值,如果没有就使用默认值defaultValue

    public static List<Integer> findSubstring(String s, String[] words) {
        if(s == null || words ==null) return new ArrayList<>();
        List<Integer> res = new ArrayList<>();
        int n = words.length;
        int m = words[0].length();
        HashMap<String,Integer> map = new HashMap<String,Integer>();
        for(String ch:words){
            map.put(ch,map.getOrDefault(ch,0)+1);
        }

        for(int i = 0;i <= s.length() - n*m;i++){
            HashMap<String,Integer> copy = new HashMap<String,Integer>(map);
            int k =n,j =i;
            while(k> 0){
                String ans = s.substring(j,j+m);
                if(!copy.containsKey(ans) || copy.get(ans) < 1){
                    break;
                }
                copy.put(ans,copy.get(ans)-1);
                k--;
                j+=m;
            }
            if(k==0) res.add(i);
        }
        return res;
    }

31.下一个全排列

思路参考博文,点击这里
需要注意的是:if(i >=0)的判断,如果没有找到逆序的情况,直接进行翻转。

class Solution {
    public void nextPermutation(int[] nums) {
        if(nums == null || nums.length == 0){
            return ;
        }
        int i = nums.length - 2;
        while(i >=0 && nums[i] >= nums[i+1]){ //找出第一个不是逆序排列的数的位置i
            i--;
        }
        if(i >= 0){  //这种是表示的是找到了逆序排列,下面进行交换
            int j = nums.length -1;
            while(j >= 0 && nums[j] <= nums[i]){  //找出逆序中第一个比i大的位置j
                j--;
            }
            swap(nums,i,j);
        }
        reverse(nums,i+1,nums.length-1);  //(1)如果没有找到的话,i=-1,同时进行全部交换就行了。(2)如果找到了,后面的进行交换

    }
    public void reverse(int[] nums,int start, int end){
        int i = start,j = end;
        while(i < j){
            swap(nums,i++,j--);
        }
    }
    public void swap(int[] nums,int i ,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

36. 有效的数独

题目详解
注意:字符和数字之间的相互转换:数字 = 字符 -‘0’:例子:数字1的ASCII码为49,而0的ASCII码为48,所以正好能计算。
getOrDefault(n,0):表示如果HashMap中有值就使用HashMap中的value值,如果没有的话使用默认的值。
注意:
1.时间复杂度和空间复杂度的分析:都是O(1)
2.对于HashMap数组建立之后,每个数组要放入一个HashMap进去。

注意:建立HashMap数组之后,如果不进行第二步的赋值操作的话,row里面的元素是null,所以必须进行一个初始化,不然会报空指针异常。

class Solution {
  public boolean isValidSudoku(char[][] board) {
    // init data
    HashMap<Integer, Integer> [] rows = new HashMap[9];
    HashMap<Integer, Integer> [] columns = new HashMap[9];
    HashMap<Integer, Integer> [] boxes = new HashMap[9];
    
    for (int i = 0; i < 9; i++) {
      rows[i] = new HashMap<Integer, Integer>();
      columns[i] = new HashMap<Integer, Integer>();
      boxes[i] = new HashMap<Integer, Integer>();
    }

    // validate a board
    for (int i = 0; i < 9; i++) {
      for (int j = 0; j < 9; j++) {
        char num = board[i][j];
        //题目中空白格用'.'进行表示。
        if (num != '.') {  
          int n = (int)num;
          int box_index = (i / 3 ) * 3 + j / 3;

          // keep the current cell value
          rows[i].put(n, rows[i].getOrDefault(n, 0) + 1);
          columns[j].put(n, columns[j].getOrDefault(n, 0) + 1);
          boxes[box_index].put(n, boxes[box_index].getOrDefault(n, 0) + 1);

          // check if this value has been already seen before
          if (rows[i].get(n) > 1 || columns[j].get(n) > 1 || boxes[box_index].get(n) > 1)
            return false;
        }
      }
    }

    return true;
  }
}

37解数独

在这里插入图片描述
为了便于采用:二维数组的一个坐标系。所以此题采用的是先由上向下进行遍历,然后在从左到有进行遍历。

class Solution {
   private  boolean[][] rows = new boolean[9][10];
    private  boolean[][] cols = new boolean[9][10];
    private  boolean[][] boxes = new boolean[9][10];

    public void solveSudoku(char[][] board) {
       for (int j = 0; j < 9; j++) {
            for (int i = 0; i < 9; i++) {
                if (board[i][j] != '.') {
                    // n : 1 - 9
                    int num = board[i][j] - '0';
                    // 在第i行 n 被用到
                    rows[i][num] = true;
                    // 在第j列 n 被用到
                    cols[j][num] = true;
                    // 在小格子中 n 被用到
                    boxes[(i / 3) * 3 + j / 3][num] = true;
                }
            }
        }
        dfs(board, 0, 0);
    }

    private  boolean dfs(char[][] board, int x, int y) {
        // 行数等于9的时候找到了一组解
        if (y == 9) return true;
        // 下一个位置,从左到右,从上到下
        int nx = (x + 1) % 9;
        int ny = (nx == 0) ? y + 1 : y;
        // 当前位置已经是数字,看下一个位置
        if (board[x][y] != '.') return dfs(board, nx, ny);
        // 当前位置需要填一个数字:1-9
        for (int i = 1; i <= 9; i++) {
            // 小格子的位置
            int boxIndex = (x / 3) * 3 + y / 3;
            // 都是false的时候放置当前位置放 i
            if (!(rows[x][i] || cols[y][i] || boxes[boxIndex][i])) {
                rows[x][i] = true;
                cols[y][i] = true;
                boxes[boxIndex][i] = true;
                board[x][y] = (char) (i + '0');
                if (dfs(board, nx, ny)) return true;
                board[x][y] = '.';
                boxes[boxIndex][i] = false;
                cols[y][i] = false;
                rows[x][i] = false;
            }
        }
        return false;
    }
}

38. 外观数列

注意:第二个循环从1开始,是因为在大循环之中,已经取了第0个了,所以这里直接从1开始。

    /**
     * 解题思路:
     * 本题的难点在于:报数的概念理解,至少我从题意中没有很清晰的理解,但是感觉像是个递推式
     * 从4->5分析,将4个每一位拆开看(个数+数字),4=1211 => 1=11,2=12,11=21,所以5=111221
     * 所以解题用循环,从1->n可求解出来
     *
     * @param n
     * @return
     */
    public String countAndSay(int n) {
        String str = "1";
        for (int i = 2; i <= n; i++) {
            StringBuilder builder = new StringBuilder();
            char pre = str.charAt(0);
            int count = 1;
            for (int j = 1; j < str.length(); j++) {
                char c = str.charAt(j);
                if (c == pre) {
                    count++;
                } else {
                    builder.append(count).append(pre);
                    pre = c;
                    count = 1;
                }
            }
            builder.append(count).append(pre);
            str = builder.toString();
        }

        return str;
    }


动态规划

class Solution {
    public String countAndSay(int n) {
        String[] dp = new String[n];
        dp[0] = "1";
        for(int i = 1;i < n;i++){
            int last = dp[i-1].length();
            StringBuilder sb = new StringBuilder();
            for(int j =0;j < last;j++){
                int k = j;
                
                while(k < last-1 && dp[i-1].charAt(k) == dp[i-1].charAt(k+1)){
                    k++;
                }
                char ch = dp[i-1].charAt(j);
                sb.append(k -j +1 + "").append(ch);
                j = k;
            }
            dp[i] = sb.toString();
        }
        return dp[n-1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值