全排列-遇到的深浅拷贝问题

题目描述:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

解答代码:

js 复制代码
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permute = function(nums) {
    let res = [];
    let used = [];

    function dfs(path){
        if(path.length === nums.length){
            res.push(path.slice());
            return;
        }
        for(let i = 0;i<nums.length;i++){
            if(used[nums[i]])continue;
            path.push(nums[i]);
            used[nums[i]] = true;
            dfs(path);
            path.pop();
            used[nums[i]] = false;
        }
    }
    dfs([]);
    return res;
};

遇到的问题描述:

  为什么res.push(path.slice())这里要拷贝一个新的数组,而不能直接用res.push(path):这句代码的作用是保存了当前排列的副本,可以避免后续递归修改path时影响之前res中保存好的结果。如果直接 res.push(path),后续 path 的变化会影响 res 中的内容,导致结果错误。

  但是我印象中了解到的问题是JS中slice()方法是一个浅拷贝,既然是浅拷贝,而且数组本身不是一个基础数据类型,所以它依然是复制的引用,内部数据应该是共享的,后续改变path依然会出现问题,那这里为什么没出现问题呢?

解释

浅拷贝对于复制到的第一层属性不会互相影响,类似于深拷贝。如果数组中只有基本数据类型,浅拷贝和深拷贝的效果是一样的;只有数组中有对象、数组等引用类型时,浅拷贝和深拷贝才有区别,同理对象也是。

引用赋值
js 复制代码
let path = [{a:1}, {b:2}];
let copy = path;
copy.push({ c: 3 });
console.log(path);  //[ { a: 1 }, { b: 2 }, { c: 3 } ]
console.log(copy);  //[ { a: 1 }, { b: 2 }, { c: 3 } ]
path[0].a = 100;
console.log(path);  //[ { a: 100 }, { b: 2 }, { c: 3 } ]
console.log(copy);   //[ { a: 100 }, { b: 2 }, { c: 3 } ]

可以看出来引用赋值是完全会互相影响。

浅拷贝

重点:浅拷贝只会完全复制对象的第一层属性(和引用赋值的区别)。

js 复制代码
//数组中是基本数据类型
let path = [1,2];
let arr = path.slice()
path.push(3);   
console.log(path);  //[1,2,3]
console.log(arr);  //[1,2]
js 复制代码
let path = [{a:1}, {b:2}];
let copy = path.slice();
copy.push({ c: 3 });
console.log(path);  //[ { a: 1 }, { b: 2 } ]
console.log(copy);  //[ { a: 1 }, { b: 2 }, { c: 3 } ]
path[0].a = 100;
console.log(path);  //[ { a: 100 }, { b: 2 } ]
console.log(copy);   //[ { a: 100 }, { b: 2 }, { c: 3 } ]

这里还可以看出即使是浅拷贝,两个数组也是不同的数组对象,执行的push操作不会影响另一个,但是复制后,对于已复制的引用数据类型(也就是第二层属性)的改变还是会互相影响的。

js 复制代码
let path = {a:1, b:2};
let copy = Object.assign({},path);
copy.c = 3;
console.log(path);  //{ a: 1, b: 2}
console.log(copy);  //{ a: 1, b: 2, c: 3 }
path.a = 100;
console.log(path);  //{ a: 100, b: 2 }
console.log(copy);   //{ a: 1, b: 2, c: 3 }

上面这段代码同样说明浅拷贝对于第一层属性的变化互相之间不会有影响,所以可以得出结论:

❗️当对象只有一级属性为深拷贝;
❗️当对象中有多级属性时,二级属性后就是浅拷贝;

浅拷贝的一些方法:

  • 对象:let cppy = Object.assign({}, original); 数组:let cppy = Object.assign([], original);
  • 对象:let copy = { ...original }; 数组:let copy = [ ...original ];
  • let copyy = original.slice();仅适用于数组
深拷贝
js 复制代码
let path = [{a:1}, {b:2}];
let copy = JSON.parse(JSON.stringify(path));
copy.push({ c: 3 });
console.log(path);  //[ { a: 1 }, { b: 2 } ]
console.log(copy);  //[ { a: 1 }, { b: 2 }, { c: 3 } ]
path[0].a = 100;
console.log(path);  //[ { a: 100 }, { b: 2 } ]
console.log(copy);   //[ { a: 1 }, { b: 2 }, { c: 3 } ]

深拷贝互相之间完全不影响。

相关推荐
山河木马11 小时前
矩阵专题3-怎么创建投影矩阵(uProjectionMatrix)
javascript·webgl·计算机图形学
泯泷13 小时前
第 2 篇:设计第一套字节码:Opcode、Instruction 与 Constant Pool
前端·javascript·安全
泯泷13 小时前
第 1 篇:从 1 + 2 开始:亲手写出第一台 JSVM
前端·javascript·安全
朦胧之13 小时前
页面白屏卡住排查方法
前端·javascript
犇驫聊AI14 小时前
Chrome DevTools MCP + Claude Code 自定义skills生成接口代码生成器
前端·javascript
kyriewen14 小时前
别再这样写 async/await 了:我在 Code Review 中见过最多的 8 个错误
前端·javascript·面试
用户2986985301419 小时前
在 React 中使用 JavaScript 将 Excel 转换为 SVG
前端·javascript·react.js
labixiong20 小时前
手写Promise--微任务、静态方法、async/await 全搞懂(三)
前端·javascript
铁皮饭盒21 小时前
3行代码搞定页面截图,Bun.WebView真的简单
javascript
kyriewen1 天前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试