Java实现LeetCode 题号:531 - 550

149 阅读6分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。

LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

532. 数组中的K-diff数对

给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对。这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数组中的数字,且两数之差的绝对值是 k.

示例 1:

输入: [3, 1, 4, 1, 5], k = 2 输出: 2 解释: 数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。 尽管数组中有两个1,但我们只应返回不同的数对的数量。 示例 2:

输入:[1, 2, 3, 4, 5], k = 1 输出: 4 解释: 数组中有四个 1-diff 数对, (1, 2), (2, 3), (3, 4) 和 (4, 5)。 示例 3:

输入: [1, 3, 1, 5, 4], k = 0 输出: 1 解释: 数组中只有一个 0-diff 数对,(1, 1)。 注意:

数对 (i, j) 和数对 (j, i) 被算作同一数对。

pS:
    想给数组排个序(升序数组)
        类似于滑动窗口,用两个指针规定窗口的左右下标,
        如果窗口两端的差大于k,说明该窗口两端的差已经超过范围,左指针右移,然后重新计算当前范围两端的差,
        如果窗口两端的差小于k,说明该窗口两端的差还可以扩大范围,右指针右移,
        直到找到两端差值等于k的
        
        这里直接用 i 当作窗口的右指针,然后左指针用 start 
        
class Solution {
      public int findPairs(int[] nums, int k) {
        if(k < 0) return 0;
        Arrays.sort(nums);
        int start = 0, count = 0, prev = 0x7fffffff;
        for (int i = 1; i < nums.length; i++) {
            if(nums[i] - nums[start] > k || prev == nums[start]) {
                if (++start != i) i--;
            }else if (nums[i] - nums[start] == k) {
                prev = nums[start++];
                count++;
            }
        }
        return count;
    }
}

535. TinyURL 的加密与解密

TinyURL是一种URL简化服务, 比如:当你输入一个URL leetcode.com/problems/de… 时,它将返回一个简化的URL tinyurl.com/4e9iAk.

要求:设计一个 TinyURL 的加密 encode 和解密 decode 的方法。你的加密和解密算法如何设计和运作是没有限制的,你只需要保证一个URL可以被加密成一个TinyURL,并且这个TinyURL可以用解密方法恢复成原本的URL。

PS:
    这里随便写,只要自己的解密和加密能合成就行,
    (只要不发生a加密成b,b解密成c  , 就可以)
    
    这里笔者运用的把一个字符异或相同的值两次,值不变,进行加密解密操作
public class Codec {

    
     private static int key=35;
    // Encodes a URL to a shortened URL.
    public String encode(String longUrl) {
        char[] c=longUrl.toCharArray();
        for (int i=0;i<c.length;i++)
        {
            c[i]^=key;
        }
        String encode = new String(c);
        return "http://"+encode;
    }

    // Decodes a shortened URL to its original URL.
    public String decode(String shortUrl) {
        char[] c=shortUrl.substring(7).toCharArray();
        for (int i=0;i<c.length;i++)
        {
            c[i]^=key;
        }
        return new String(c);
    }
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.decode(codec.encode(url));

537. 复数乘法

给定两个表示复数的字符串。

返回表示它们乘积的字符串。注意,根据定义 i2 = -1 。

示例 1:

输入: "1+1i", "1+1i" 输出: "0+2i" 解释: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i ,你需要将它转换为 0+2i 的形式。 示例 2:

输入: "1+-1i", "1+-1i" 输出: "0+-2i" 解释: (1 - i) * (1 - i) = 1 + i2 - 2 * i = -2i ,你需要将它转换为 0+-2i 的形式。 注意:

输入字符串不包含额外的空格。 输入字符串将以 a+bi 的形式给出,其中整数 a 和 b 的范围均在 [-100, 100] 之间。输出也应当符合这种形式。

PS:
    把两个复数都分开,用 +  分开,实部乘实部,虚部乘虚部
    然后在放到一起,返回String
class Solution {
     public String complexNumberMultiply(String a, String b) {

        StringBuilder res = new StringBuilder();

        int aspit = a.indexOf('+');
        int bspit = b.indexOf('+');

        int aa = Integer.parseInt( a.substring(0,aspit) );
        int ab = Integer.parseInt( a.substring(aspit+1, a.length()-1) );
        int ba = Integer.parseInt( b.substring(0,bspit) );
        int bb = Integer.parseInt( b.substring(bspit+1, b.length()-1) );
 
        res.append(aa * ba - ab * bb);
        res.append('+');
        res.append(aa * bb + ab * ba);
        res.append('i');
        return res.toString();
    }
}

538. 把二叉搜索树转换为累加树

给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。

例如:

输入: 原始二叉搜索树:
              5
            /   \
           2     13

输出: 转换为累加树:
             18
            /   \
          20     13
 
PS:
    因为要加上所有大于当前结点的值,所以从右到左开始遍历,
        (二叉搜索树,右面都大于左面结点)
        (如果是从左到右,右面的没办法算,从右到左,右面的都已经被算好了)
        
    相对于一棵树来说,
        根节点加上的是当前树的右结点
        左节点要的是加上当前结点的跟结点和右节点
        也就是说,左节点加上累加树的根节点就可以(累加树的根节点就是二叉搜索树的根节点和右节点)
        
    从右到左遍历,
        每个根节点加上右下子树的右结点,每个左节点加上当前树的根节点
        先递归到右节点,然后回来加上右节点的值,记录当前累加树,根节点然后调用左节点,
        当前左节点,如果没有右结点左节点的加上当前根节点
        如果当前左节点有右结点,那就递归到没有右节点,添加上
            二叉搜索树中,左节点的右节点,一定比右节点的左节点小
        
 
class Solution {
    int num = 0;

    public TreeNode convertBST(TreeNode root) {
        if (root != null) {
            //遍历右子树
            convertBST(root.right);
            //遍历顶点
            root.val = root.val + num;
            num = root.val;
            //遍历左子树
            convertBST(root.left);
            return root;
        }
        return null;
    }
}

539. 最小时间差

给定一个 24 小时制(小时:分钟)的时间列表,找出列表中任意两个时间的最小时间差并已分钟数表示。

示例 1:

输入: ["23:59","00:00"] 输出: 1

备注:

列表中时间数在 2~ 20000 之间。 每个时间取值在 00:00~23:59 之间。

PS:
    时间有点异于平常数,我们把时间转成分钟数,小时 * 60 + 分钟
    然后排序,找相邻差最小的那个
    最后记得判断一下第一个和最后一个的差,是看时间差,可能第一个和最后一个的时间差小
class Solution {
    public int findMinDifference(List<String> timePoints) {
  int min = Integer.MAX_VALUE;
		int[] minute = new int[timePoints.size()];
		for (int i = 0; i < timePoints.size(); i++)
			minute[i] = Integer.valueOf(timePoints.get(i).substring(0, 2)) * 60
					+ Integer.valueOf(timePoints.get(i).substring(3, 5));
		Arrays.sort(minute);
		for (int i = 0; i < minute.length - 1; i++)
			min = Math.min(min, minute[i + 1] - minute[i]);
		return Math.min(min, 1440 - minute[minute.length - 1] + minute[0]);
    }
}

540. 有序数组中的单一元素

给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

示例 1:

输入: [1,1,2,3,3,4,4,8,8] 输出: 2 示例 2:

输入: [3,3,7,7,10,11,11] 输出: 10 注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。

PS:
	异或是,遇到相同的就为0,
	所以只有一个不是成对出现,只能是那一个

class Solution {
    public int singleNonDuplicate(int[] nums) {
        int num = 0;
        for(int i : nums)
            num ^= i;
        
        return num;
    }
}

541. 反转字符串 II

给定一个字符串和一个整数 k,你需要对从字符串开头算起的每个 2k 个字符的前k个字符进行反转。如果剩余少于 k 个字符,则将剩余的所有全部反转。如果有小于 2k 但大于或等于 k 个字符,则反转前 k 个字符,并将剩余的字符保持原样。

示例:

输入: s = "abcdefg", k = 2 输出: "bacdfeg" 要求:

该字符串只包含小写的英文字母。 给定字符串的长度和 k 在[1, 10000]范围内。

	暴力
        找到匹配的格式,就进行反转
class Solution {
    public static void swap(char [] a,int begin,int end){
        while(begin<end){
            a[begin]^=a[end];
            a[end]^=a[begin];
            a[begin]^=a[end];
            begin++;
            end--;
        }
    }
    public String reverseStr(String s, int k) {
        char [] c=s.toCharArray();
        int len=c.length;
        for(int i=0;i<len; i=i+2*k){
            if(i+2*k<=len||i+k<=len){
                swap(c,i,i+k-1);
            }else{
                swap(c,i,len-1);
            }
           
        }
        return new String(c);
        
        
    }
}

542. 01 矩阵

给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。

两个相邻元素间的距离为 1 。

示例 1: 输入:

0 0 0 0 1 0 0 0 0 输出:

0 0 0 0 1 0 0 0 0 示例 2: 输入:

0 0 0 0 1 0 1 1 1 输出:

0 0 0 0 1 0 1 2 1 注意:

给定矩阵的元素个数不超过 10000。 给定矩阵中至少有一个元素是 0。 矩阵中的元素只在四个方向上相邻: 上、下、左、右。

class Solution {
    private int row;
    private int col;
    private int[][] vector = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

    /**
     * DP(两次遍历,可 AC)
     */
    public int[][] updateMatrix(int[][] matrix) {
        row = matrix.length;
        col = matrix[0].length;
        // 第一次遍历,正向遍历,根据相邻左元素和上元素得出当前元素的对应结果
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (matrix[i][j] == 1) {
                    matrix[i][j] = row + col;
                }
                if (i > 0) {
                    matrix[i][j] = Math.min(matrix[i][j], matrix[i - 1][j] + 1);
                }
                if (j > 0) {
                    matrix[i][j] = Math.min(matrix[i][j], matrix[i][j - 1] + 1);
                }
            }
        }
        // 第二次遍历,反向遍历,根据相邻右元素和下元素及当前元素的结果得出最终结果
        for (int i = row - 1; i >= 0; i--) {
            for (int j = col - 1; j >= 0; j--) {
                if (i < row - 1) {
                    matrix[i][j] = Math.min(matrix[i][j], matrix[i + 1][j] + 1);
                }
                if (j < col - 1) {
                    matrix[i][j] = Math.min(matrix[i][j], matrix[i][j + 1] + 1);
                }
            }
        }
        return matrix;
    }
}

543. 二叉树的直径

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

示例 : 给定二叉树

      1
     / \
    2   3
   / \     
  4   5   

返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。

注意:两结点之间的路径长度是以它们之间边的数目表示。

PS:
	  二叉树的直径不一定过根节点,因此需要去搜一遍所有子树(例如以root,root.left, root.right...为根节点的树)对应的直径,取最大值。
 root的直径 = root左子树高度 + root右子树高度
 root的高度 = max {root左子树高度, root右子树高度} + 1

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */


class Solution {
     private int max = 0;

    public int diameterOfBinaryTree(TreeNode root) {
        dfs(root);
        return max;
    }

    private int dfs(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = dfs(root.left), rightHeight = dfs(root.right);
        max = Math.max(leftHeight + rightHeight, max);
        return Math.max(leftHeight, rightHeight) + 1;
    }
}

546. 移除盒子

给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色。 你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止。每一轮你可以移除具有相同颜色的连续 k 个盒子(k >= 1),这样一轮之后你将得到 k*k 个积分。 当你将所有盒子都去掉之后,求你能获得的最大积分和。

示例 1: 输入:

[1, 3, 2, 2, 2, 3, 4, 3, 1] 输出:

23 解释:

[1, 3, 2, 2, 2, 3, 4, 3, 1] 
----> [1, 3, 3, 4, 3, 1] (3*3=9 分) 
----> [1, 3, 3, 3, 1] (1*1=1 分) 
----> [1, 1] (3*3=9 分) 
----> [] (2*2=4 分)
 

提示:盒子的总数 n 不会超过 100。

PS:
    类似于范围递归,
        如果左边界大于右边界了,直接返回,当前范围最大值已经被计算
        找从右到左相同的重新递归一遍,当作一个临时的积分
        然后在把左面的值循环(i),如果i下标的值等于右端点的话,并且 i+1 下标对应的值不等于右结点的值
        从这个点分开,计算左面和右面的最大值,然后计算完后,记录一下当前范围的最大值
        (想当于是先算右半部分,然后再算左半部分,因为左半部分最后一个和right对应的是相同的
        左半部分相同的samecount,右半部分相同的是0)
        
        算第一个maxscore的时候是右边界以左,相同的数量加上上一次递归相同的数量,这里都是和右边界值相同的,然后递归左边的加上当前的相同的积分
     
        

网友大哥:2019.9.11 vivo秋招, 数据分析, 第三题原题, 多么痛的领悟

class Solution {
    public int removeBoxes(int[] boxes) {  
        int[][][] scores = new int[boxes.length][boxes.length][boxes.length];

        return calcMaxScore(boxes, 0, boxes.length - 1, 0, scores);
    }

    private int calcMaxScore(int[] boxes, int left, int right, int sameCount, int[][][] scores) {
        if (left > right) {
            return 0;
        }

        int maxScore = scores[left][right][sameCount];
        if (maxScore != 0) {
            return maxScore;
        }

        int pos = left - 1;
        for (int i = right - 1; i >= left; --i) {
            if (boxes[i] != boxes[right]) {
                pos = i;
                break;
            }
        }
        int mergeSameCount = sameCount + right - pos;
        maxScore = calcMaxScore(boxes, left, pos, 0, scores) + mergeSameCount * mergeSameCount;

        for (int i = pos - 1; i >= left; --i) {
            if (boxes[i] == boxes[right] && boxes[i + 1] != boxes[right]) {
                int score = calcMaxScore(boxes, left, i, mergeSameCount, scores) + calcMaxScore(boxes, i + 1, pos, 0, scores);
                maxScore = Math.max(maxScore, score);
            }
        }

        scores[left][right][sameCount] = maxScore;

        return maxScore;
    }
}

547. 朋友圈

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

示例 1:

输入: [[1,1,0], [1,1,0], [0,0,1]] 输出: 2 说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 第2个学生自己在一个朋友圈。所以返回2。 示例 2:

输入: [[1,1,0], [1,1,1], [0,1,1]] 输出: 1 说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 注意:

N 在[1,200]的范围内。 对于所有学生,有M[i][i] = 1。 如果有M[i][j] = 1,则有M[j][i] = 1。

并查集

类似题目合根植物

class Solution {
       
    public int findCircleNum(int[][] M) {
        int[] visited = new int[M.length];
        int count = 0;
        for (int i = 0; i < M.length; i++) {
            if (visited[i] == 0) {
                dfs(M, visited, i);
                count++;
            }
        }
        return count;
    }
    
    public void dfs(int[][] M, int[] visited, int i) {
        for (int j = 0; j < M.length; j++) {
            if (M[i][j] == 1 && visited[j] == 0) {
                visited[j] = 1;
                dfs(M, visited, j);
            }
        }
    }
}