您现在的位置是:首页 >学无止境 >刷题记录:哈希 | leetcode-2352. 相等行列对 2023/6/6网站首页学无止境

刷题记录:哈希 | leetcode-2352. 相等行列对 2023/6/6

碳基肥宅 2024-08-28 12:01:03
简介刷题记录:哈希 | leetcode-2352. 相等行列对 2023/6/6

2352. 相等行列对

这题还是非常简单的。如果用模拟的方法,时间复杂度要达到O(n^3)了,感觉不太可。

这回学聪明了,没有一上来就想着暴力模拟。用哈希的办法,可以把时间复杂度降为O(n^2)。

我的思路是先转置矩阵,再用哈希Map。基本思路就是把grid当中的每一行存到一个哈希Map里,并记录它出现的次数;然后比对转置数组tmp的每一行是否在哈希Map中出现,以及出现的次数。

我想先转置矩阵的原因是,这道题要求行和列进行比较。Java里,二维数组就是一维数组的数组,操控行就是操控一个个一维数组,直接用下标grid[i]即可。操控列我感觉不那么方便。

Java里面没有转置二维数组的库函数,要自己写一个。(刚开始我还以为Arrays里有,在里面找了半天也没找到。后来才想起来是Python的numpy库里有……)

如何把数组内容存到哈希表里?直接写Map<int[],xxxx>或Set<int[]>是不能达到目的的。注意,这样写语法上是没问题的,但是放入Map或Set中的是数组的引用,实际上存入的并不是数组的内容。因此,如果要实现把数组按照内容放入哈希中,必须自己封装一个数组并重写hashcode()方法。

其实也不复杂,可以这样:

import java.util.Arrays;

public class MyIntArray {
    private int[] array;

    public MyIntArray(int[] array) {
        this.array = array;
    }

    // Getter and setter methods...

    @Override
    public int hashCode() {
        return Arrays.hashCode(array);
    }

    // Equals method and other code...
}

MyIntArray类包装了一个整型数组。在hashCode()方法中使用Arrays.hashCode()方法来计算整型数组的哈希码。该方法会将整型数组的内容纳入计算,并返回最终的哈希码。

注意:Arrays.hashCode()方法会考虑整型数组的内容来计算哈希码。这样可以确保在只有数组的内容相同的情况下,两个MyIntArray对象的哈希码才会相等。

Arrays.hashCode()是Java中Arrays类提供的一个静态方法,用于计算数组的哈希码。它接受一个数组作为参数,并返回一个基于数组内容的哈希码。

Arrays.hashCode()方法的计算过程如下:

  1. 如果传入的数组为null,返回0作为哈希码。

  2. 如果传入的数组不为null,计算数组的哈希码。

  3. 遍历数组的每个元素,并将每个元素的哈希码进行累加或异或操作,生成最终的哈希码。

使用Arrays.hashCode()方法可以方便地计算数组的哈希码,而不需要手动编写复杂的哈希码计算逻辑。它内部使用了与Java的Object.hashCode()方法类似的算法,确保在只有数组的内容相同的情况下,不同的数组会生成不同的哈希码。

需要注意的是,Arrays.hashCode()方法只计算数组的内容,而不考虑数组的引用地址。这意味着如果两个数组具有相同的内容,即使它们在内存中的位置不同,它们的哈希码也会相等。

由于hashcode是native方法,没法直接在idea中查看到它的源码。不过记住这个结论就好了。

我实现的时候其实没有用到上面的这些O(∩_∩)O

我通过自定义arrayToString()方法,把数组的内容转换成字符串,然后对字符串进行哈希操作。String是重写过hashcode()方法的,所以没有上述问题。

为什么用Map不用Set?原因很简单,因为原数组grid当中可能存在内容相同的行。如果用哈希Set,那么它们会被去重。比如原数组中有两行都是2 2 7 1,转置数组tmp中也有一行是2 2 7 1.那么原本应该记录结果为2,但由于Set去重了原数组中的两相同行,最终结果会变成1.

    String arrayToString(int[] array) {
        String ret = "";
        for (int j : array) {
            ret += j + " ";
        }
        return ret;
    }

注意,ret += j + " " 这一句里,后面的这个空格拼接非常重要。看看输入这个样例的情况就知道了:

[[11,1],[1,11]]

Java里的Arrays.toString转换后是格式化的字符串,其实也是可以的。因为这里这个分隔符并不影响。

不过,要想没有分隔符,也是可以的,可以这样:

import java.util.Arrays;
import java.util.stream.Collectors;

int[] arr = {1, 2, 3, 4, 5};
String arrString = Arrays.stream(arr)
                        .mapToObj(String::valueOf)
                        .collect(Collectors.joining());

System.out.println(arrString);  // 输出:12345

(不过这道题似乎就没有这个必要背这个了……)

我提交的代码是这,是通过的: 

class Solution {
    String arrayToString(int[] array) {
        int n = array.length;
        String ret = "";
        for (int j : array) {
            ret += j + " ";
        }
        return ret;
    }
    public int equalPairs(int[][] grid) {
        int n = grid.length;

        int[][] tmp = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                tmp[i][j] = grid[j][i];
            }
        }

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                System.out.print(grid[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("-----");
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                System.out.print(tmp[i][j] + " ");
            }
            System.out.println();
        }
        int count = 0;
        Map<String, Integer> map = new HashMap<>();

        for (int i = 0; i < n; i++) {
            String str = arrayToString(grid[i]);
            map.put(str, map.getOrDefault(str, 0)+1);
        }

        int j = 0;
        while(j < n) {
            String str = arrayToString(tmp[j]);
            if(map.containsKey(str)) {
                count += map.get(str);
            }
            j++;
        }

        return count;
    }
}

今天还刷了二分:162. 寻找峰值

class Solution {
    int bin(int[] nums, int l, int r) {
        if(l == r) {
            return l;
        }
        l = 1;
        r = nums.length-2;
        while(l < r) {
            int mid = (l+r+1)>>1;
            if(nums[mid] > nums[mid+1] && nums[mid] > nums[mid-1]) {
                return mid;
            }else if(nums[mid] < nums[mid+1]){
                l = mid;
            }else if(nums[mid] > nums[mid+1]){
                r = mid-1;
            }
        }
        return l;
    }
    public int findPeakElement(int[] nums) {
        if(nums.length == 1) {
            return 0;
        }
        int ret = bin(nums, 1, nums.length-2);
        if(nums[0] > nums[1])   ret = 0;
        if(nums[nums.length - 1] > nums[nums.length - 2])   ret = nums.length - 1;
        return ret;
    }
}

巩固一下模板:

 

 

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。