519. 随机翻转矩阵

尝试解答

我的思路是首先需建立map数组,记录matrix所有的row和col;
flip时随机值为0-数组长度为该数组的下标然后移除该项,这样每次随机都是有效的,最后返回;
直到数组为空。

class Solution {
    private matrix: number[][] = [];
    private mIndexList: number[] = [];
    private matrixChildMap!: Map<number, number[]>;

    constructor(private m: number, private n: number) {
        this.init();
    }

    flip(): number[] {
        if (!this.mIndexList.length) return [];
        const [m, mListIndex] = Solution.randItem(this.mIndexList);
        const childIndexList = this.matrixChildMap.get(m) as number[];
        const [n, nListIndex] = Solution.randItem(childIndexList);
        childIndexList.splice(nListIndex, 1)
        if (!childIndexList.length) {
            this.mIndexList.splice(mListIndex, 1)
        }
        this.matrix[m][n] = 1;
        return [m, n];
    }

    private static randItem<T>(arr: T[]): [T, number] {
        const index = ~~(Math.random() * arr.length);
        return [arr[index], index];
    }

    private init(){
        const matrix: number[][] = [];
        const child: number[] = []

        const nIndexList: number[] = []
        const matrixChildMap = new Map<number, number[]>();
        for (let i = 0; i < this.n; i++) {
            nIndexList.push(i);
            child.push(0);
        }

        const mIndexList: number[] = []
        for (let i = 0; i < this.m; i++) {
            matrix.push(child.slice())
            mIndexList.push(i);
            matrixChildMap.set(i, nIndexList.slice());
        }
        this.mIndexList = mIndexList;
        this.matrixChildMap = matrixChildMap;

        this.matrix = matrix;
    }

    reset(): void {
        this.init();
    }
}

结果卡在最后一步测试用例

再试

使用`${number},${number}`字符串作记录

class Solution {
    private matrix: number[][] = [];
    private indexList: `${number},${number}`[] = [];

    constructor(private m: number, private n: number) {
        this.init();
    }

    flip(): number[] {
        if (!this.indexList.length) return [];
        const index = ~~(Math.random() * this.indexList.length);
        const item = this.indexList.splice(index,1)[0];
        const [m,n] = item.split(",").map(Number);
        this.matrix[m][n] = 1;
        return [m, n];
    }
    
    private init(){
        const matrix: number[][] = [];
        const {m,n} = this;
        const indexList: `${number},${number}`[] = []
        for (let i = 0; i < m; i++) {
            const child: number[] = []
            for (let j = 0; j < n; j++) {
                child.push(0);
                indexList.push(`${i},${j}`)
            }
            matrix.push(child.slice())
        }
        this.indexList = indexList;

        this.matrix = matrix;
    }

    reset(): void {
        this.init();
    }
}

结果leetcode的ts版本太低了,不支持`${number},${number}`这种字符串模版类型。
再改

class Solution {
    private matrix: number[][] = [];
    private indexList: string[] = [];

    constructor(private m: number, private n: number) {
        this.init();
    }

    flip(): number[] {
        if (!this.indexList.length) return [];
        const index = ~~(Math.random() * this.indexList.length);
        const item = this.indexList.splice(index,1)[0];
        const [m,n] = item.split(",").map(Number);
        this.matrix[m][n] = 1;
        return [m, n];
    }

    private init(){
        const matrix: number[][] = [];
        const {m,n} = this;
        const indexList: string[] = []
        for (let i = 0; i < m; i++) {
            const child: number[] = []
            for (let j = 0; j < n; j++) {
                child.push(0);
                indexList.push(`${i},${j}`)
            }
            matrix.push(child.slice())
        }
        this.indexList = indexList;

        this.matrix = matrix;
    }

    reset(): void {
        this.init();
    }
}

还是不行,最后一个用例报另外一个错

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc

第三次尝试

使用[number,number]元组的方式记录

class Solution {
    private matrix: number[][] = [];
    private indexList: [number, number][] = [];

    constructor(private m: number, private n: number) {
        this.init();
    }

    flip(): number[] {
        if (!this.indexList.length) return [];
        const index = ~~(Math.random() * this.indexList.length);
        const item = this.indexList.splice(index, 1)[0];
        const [m, n] = item;
        this.matrix[m][n] = 1;
        return item;
    }

    private init() {
        const matrix: number[][] = [];
        const {m, n} = this;
        const indexList: [number, number][] = []
        for (let i = 0; i < m; i++) {
            const child: number[] = []
            for (let j = 0; j < n; j++) {
                child.push(0);
                indexList.push([i, j])
            }
            matrix.push(child.slice())
        }
        this.indexList = indexList;

        this.matrix = matrix;
    }

    reset(): void {
        this.init();
    }
}

这比字符串的性能还差,到18题就不行了。
都不行,看来不是map方式的问题。
仔细想想其实matrix没有用,毕竟不是真的让你设置为1,所以得去掉matrix。

去掉matrix

class Solution {
    private indexList: string[] = [];

    constructor(private m: number, private n: number) {
        this.init();
    }

    flip(): number[] {
        if(!this.indexList.length) return [];
        const index = ~~(Math.random() * this.indexList.length);
        const item = this.indexList.splice(index,1)[0];
        return item.split(",").map(Number);
    }

    private init() {
        const {m, n} = this;
        const indexList: string[] = []
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                indexList.push(`${i},${j}`)
            }
        }
        this.indexList = indexList;
    }

    reset(): void {
        this.init();
    }
}

拿去测试,结果还是内存占用太大,卡在最后。

最后

我的答案首先需建立map数组,然后flip时只要随机移除对应的,最终为空。除非去掉建立map数组,才能节省掉这份占用最大的空间。绞尽脑汁,还是没想出优化方法,最后去看了官方答案


class Solution {
    private total: number;
    private map: Map<number,number> = new Map();

    constructor(private m: number, private n: number) {
        this.total = m * n;
    }

    flip(): number[] {
        const x = ~~(Math.random() * this.total);
        this.total--;
        // 查找位置 x 对应的映射
        const idx = this.map.get(x) || x;
        // 将位置 x 对应的映射设置为位置 total 对应的映射
        this.map.set(x, this.map.get(this.total) || this.total);
        return [~~(idx / this.n), idx % this.n];
    }


    reset(): void {
        this.total = this.m * this.n;
        this.map.clear();
    }
}

其实如果没有空间要求的话,上面的也能解答,但是数据量大就不行了,空间占太多了。
这题的答案有点绕,有点难以理解,但是用途挺大的,可以使用到代码优化上面去,值得花时间去理解。

评论

0 / 800
全部评论()