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

题目描述:

给定一个不含重复数字的数组 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 } ]

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

相关推荐
德育处主任2 小时前
p5.js 绘制 3D 椭球体 ellipsoid
前端·javascript·数据可视化
安卓开发者2 小时前
鸿蒙NEXT Web组件与JavaScript交互:打通原生与前端的桥梁
前端·javascript·harmonyos
fdc20172 小时前
Avalonia 基础导航实现:从页面切换到响应式交互全指南
开发语言·javascript·ecmascript
小菜花293 小时前
利用H5实现svg图片中各部分监听事件
前端·javascript·svg·object标签
前端小巷子4 小时前
JS 打造「放大镜 + 缩略图」一体组件
前端·javascript·面试
知识分享小能手4 小时前
React学习教程,从入门到精通,React AJAX 语法知识点与案例详解(18)
前端·javascript·vue.js·学习·react.js·ajax·vue3
古夕5 小时前
前端文件下载的三种方式:a标签、Blob、ArrayBuffer
前端·javascript·vue.js
李李记5 小时前
Node.js 打包踩坑?NCC+PKG 从单文件到多平台可执行文件,解决 axios 缺失等 80% 问题
javascript
wow_DG7 小时前
【Vue2 ✨】Vue2 入门之旅 · 进阶篇(九):Vue2 性能优化
javascript·vue.js·性能优化