深度優先遍歷,廣度優先遍歷實現對象的深拷貝

来源:https://www.cnblogs.com/chenlei987/archive/2019/08/05/11304239.html
-Advertisement-
Play Games

深度優先遍歷(Depth-First-Search),是搜索演算法的一種,它沿著樹的深度遍歷樹的節點,儘可能深地搜索樹的分支。當節點v的所有邊都已被探尋過,將回溯到發現節點v的那條邊的起始節點。這一過程一直進行到已探尋源節點到其他所有節點為止,如果還有未被髮現的節點,則選擇其中一個未被髮現的節點為源節... ...


深度優先遍歷

深度優先遍歷(Depth-First-Search),是搜索演算法的一種,它沿著樹的深度遍歷樹的節點,儘可能深地搜索樹的分支。當節點v的所有邊都已被探尋過,將回溯到發現節點v的那條邊的起始節點。這一過程一直進行到已探尋源節點到其他所有節點為止,如果還有未被髮現的節點,則選擇其中一個未被髮現的節點為源節點並重覆以上操作,直到所有節點都被探尋完成。
簡單的說,DFS就是從圖中的一個節點開始追溯,直到最後一個節點,然後回溯,繼續追溯下一條路徑,直到到達所有的節點,如此往複,直到沒有路徑為止。

DFS和BFS一般是用來解決圖的遍歷的,但是在這裡,既然是前端,我是用DFS和BFS來遍歷DOM樹。
下麵採用棧的形式或者遞歸的形式實現:
DOM節點

<div class="parent">
    <div class="c-1">
        <div class="c-1-1">
        </div>
        <div class="c-1-2">
        </div>
        <div class="c-1-3">
        </div>
        <div class="c-1-4">
        </div>
        <div class="c-1-5">
        </div>
    </div>
    <div class="c-2">
        <div class="c-2-1">
        </div>
        <div class="c-2-2">
        </div>
        <div class="c-2-3">
        </div>
        <div class="c-2-4">
            <div class="c-2-4-1">
            </div>
            <div class="c-2-4-2">
            </div>
            <div class="c-2-4-3">
            </div>
            <div class="c-2-4-4">
            </div>
            <div class="c-2-4-5">
            </div>
        </div>
        <div class="c-2-5">
        </div>
    </div>
    <div class="c-3">
    </div>
    <div class="c-4">
    </div>
</div>

DFS實現

var node = document.querySelectorAll('.parent')[0];
    //遞歸寫法
    function DFS1 (node, nodeList = []){
        if (node != null){
            nodeList.push(node);
            let children = node.children
            for(let i = 0; i < children.length; i++){
                DFS1(children[i], nodeList)
            }
        }
        return nodeList
    }
    let nodeList = DFS1(node);
    console.log(nodeList);
    //棧寫法
    function DFS2(node){
        let nodeList = [];
        if (node){
            //棧  後進先出
            let stack = [];
            stack.push(node);
            while (stack.length) {
                let _node = stack.pop();
                nodeList.push(_node);
                let children = _node.children;
                //這樣寫是從右向左
                // for (let i = 0; i < children.length; i++) {
                //     stack.push(children[i]);
                // }
                //從左向右
                for (let i = children.length-1; i >= 0; i--) {
                    stack.push(children[i]);
                }
            }
        }
        return nodeList;
    }
    let nodeList2 = DFS2(node);
    console.log(nodeList2);

運行結果,上面DFS1和DFS2的結果是一樣的

廣度優先遍歷

廣度優先遍歷(Breadth-First-Search)是從根節點開始,沿著圖的寬度遍歷節點,如果所有節點均被訪問過,則演算法終止,BFS 同樣屬於盲目搜索,一般用隊列數據結構來輔助實現BFS。

還是採用上面的DOM節點。BFS的寫法如下。
代碼採用隊列的形式實現。

  var node = document.querySelectorAll('.parent')[0];

    function BFS1(node, nodeList = []) {
        if (!node){
            return;
        }
        //隊列 先進先出
        var sequeue = [];
        sequeue.push(node);
        while (sequeue.length){
            var _node = sequeue.shift();
            nodeList.push(_node)
            for(var i = 0; i < _node.children.length; i++){
                sequeue.push(_node.children[i])
            }
        }
        return nodeList
    }
    let nodeList = BFS1(node);
    console.log(nodeList);

結果如下

下麵採用兩種方式來實現對象深度克隆的實現。

DFS深度克隆

深度克隆要註意兩個問題
1、環狀數據問題:如果一個對象具有環狀對象,比如obj.a.b.c === obj.a,就會使遞歸進入死迴圈,從而導致爆棧錯誤。
2、邊界處理: 對象中不止原始類型,還存在如函數、Set等數據類型,我們需要一一做處理。下麵代碼只是解決了對函數的複製。

let _toString = Object.prototype.toString;
let map = {
    array: 'Array',
    object: 'Object',
    function: 'Function',
    string: 'String',
    null: 'Null',
    undefined: 'Undefined',
    boolean: 'Boolean',
    number: 'Number'
}
function getType(obj){
    return _toString.call(obj).slice(8, -1)
}
function isTypeOf(obj, type){
    return map[type] && map[type] === getType(obj)
}

//深度克隆
//深度優先遍歷
/**
 * 
 * 解決三個問題 遞歸問題  環狀數據問題   邊界處理(比如函數,Set等)
 */
const DFSdeepClone = function (obj, visitedArr = []){
    let _obj = {};
    if (isTypeOf(obj, 'array') || isTypeOf(obj, 'object')){
        let index = visitedArr.indexOf(obj);
        if (index > -1){
            _obj = visitedArr[index]
        }
        else{
            visitedArr.push(obj)
            for (let key in obj){
                _obj[key] = DFSdeepClone(obj[key], visitedArr)
            }
        }
       
    }
    else if(isTypeOf(obj, 'function')){
        _obj = eval( '(' + obj.toString() + ')')//處理函數
    }
    else{
        _obj = obj;//處理原始值
    }
    return _obj;
}
let testObj = {
    a: 1,
    b: {
        c: 1,
        d: 2
    },
    circle: null,
    e: function() {
        console.log(1);
    }
}
let cloneTestObj = DFSdeepClone(testObj);
let cloneTestObj2 = testObj;
console.log(cloneTestObj);

console.log('經過深度克隆後的更改');
cloneTestObj.b = {};//經過深度克隆後的更改
console.log(cloneTestObj);
console.log(testObj);

cloneTestObj2.b = {}; //引用的更改
console.log('引用的更改');
console.log(cloneTestObj2);
console.log(testObj);

//環狀數據
let testCircle = {
    a: 1,
    b: {
        c: 1,
        d: 2,
        circle: null,
    },
    e: function() {
        console.log(1);
    }
}
testCircle.b.circle = testCircle.b;
cloneTestCircle = DFSdeepClone(testCircle);//不處理環問題是會爆棧的 進入死迴圈
console.log(cloneTestCircle);

BFS深度克隆

let _toString = Object.prototype.toString;
let map = {
    array: 'Array',
    object: 'Object',
    function: 'Function',
    string: 'String',
    null: 'Null',
    undefined: 'Undefined',
    boolean: 'Boolean',
    number: 'Number'
}
function getType(obj){
    return _toString.call(obj).slice(8, -1)
}
function isTypeOf(obj, type){
    return map[type] && map[type] === getType(obj)
}

//廣度優先深度克隆, 利用隊列的方式實現
//利用copyObj建立一個與原對象相同的數據結構, 遇到可處理的值(比如原始值,函數,就處理後賦值到相應的節點下)
const BFSdeepClone = function (obj, visitedArr = []){
    let copyObj = {};
    let sequeue = [obj];//進隊列
    //同時copyObj也跟著一起進隊列
    let copySequeue = [copyObj];
    while(sequeue.length){
        let _obj = sequeue.shift();
        let _copyObj = copySequeue.shift();
        if (isTypeOf(_obj, 'array') || isTypeOf(_obj, 'object')){
            for(item in _obj){
                let val = _obj[item];
                if (isTypeOf(val, 'object')){
                    let index = visitedArr.indexOf(val)
                    if (~index){
                        //是環形數據
                        _copyObj[item] = visitedArr[index];
                    }
                    else{
                        //新的對象,給copyObj一個對應屬性的空對象
                        sequeue.push(val);
                        _copyObj[item] = {};
                        copySequeue.push(_copyObj[item]);
                        visitedArr.push(val);
                    }
                    
                }
                else if (isTypeOf(val, 'array')){
                    sequeue.push(val);
                    _copyObj[item] = [];
                    copySequeue.push(_copyObj[item])
                }
                else if(isTypeOf(val, 'function')){
                    _copyObj[item] = eval( '(' + val.toString() + ')');//處理函數
                }
                else{
                    _copyObj[item] = val;//處理原始值
                }
            }
        }
        else if(isTypeOf(obj, 'function')){
            _copyObj = eval( '(' + _obj.toString() + ')');//處理函數
        }
        else{
            _copyObj = _obj;//處理原始值
        }
    }

    return copyObj

}

let testObj = {
    a: 1,
    b: {
        c: 1,
        d: 2
    },
    circle: null,
    e: function() {
        console.log(1);
    }
}
let cloneTestObj = BFSdeepClone(testObj);
let cloneTestObj2 = testObj;
console.log(cloneTestObj);

//環狀數據
let testCircle = {
    a: 1,
    b: {
        c: 1,
        d: 2,
        circle: null,
    },
    e: function () {
        console.log(1);
    }
}
testCircle.b.circle = testCircle.b;
cloneTestCircle = BFSdeepClone(testCircle);//不處理環問題是會爆棧的 進入死迴圈
console.log(cloneTestCircle);
/** 
 * 列印如下
{ a: 1, b: { c: 1, d: 2 }, circle: null, e: [Function] }
{
    a: 1,
        b: { c: 1, d: 2, circle: { c: 1, d: 2, circle: [Circular] } },
    e: [Function]
}
*/

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 08.05自我總結 JavaScript 一.概念 JavaScript(下文我們會用簡稱JS來代替)是腳本編程語言,JS語言開發的文件是以.js為尾碼,通過在html文件中引入該js文件來控制html代碼的交互功能以及前臺數據處理的業務邏輯(js語言代碼也可以直接寫在html文件中),採用的 "E ...
  • 0805自我總結 一.絕對定位 生成絕對定位的元素,相對於瀏覽器視窗進行定位。 二.相對定位 父級(最近的一個父級)相對定位的目的 1)不影響自身佈局 2)輔助自己絕對定位佈局 三預設定位 預設值。沒有定位,元素出現在正常的流中(忽略 top, bottom, left, right 或者 z in ...
  • 08.05自我總結 一.盒子佈局 1.盒子佈局的組成 margin border padding content 2.margin margin是外邊距,控制盒子的顯示位置相對於他的上一級 left、top控制自身,right、bottom影響兄弟 3.border 寬度:border width ...
  • 因為項目中用的是 element-ui 框架,而這個框架並沒有抽屜組件,所以自己實現一個,具體代碼如下: drawer.vue 組件具體使用如下: ...
  • 通過四個方法來實現元素的淡入淡出,這四個方法分別是:fadeIn()、fadeOut()、fadeToggle() 以及 fadeTo() ...
  • 項目開發中,不管是建立在哪個框架基礎上,對數據的處理都是必須的,而處理數據離不開各種遍歷迴圈。javascript中迴圈遍歷有很多種方式,記錄下幾種常見的js迴圈遍歷。 一、for迴圈 for迴圈應該是最普遍的,使用最多的一種迴圈遍歷方法了,所以也導致其可讀性和易維護性比較差,但是它可以及時brea ...
  • 項目開發中 ajax 是不可缺少的,一個好的封裝可以減少我們很多的重覆代碼,維護也更方便。在 vue 開發中我們用的比較多的就是 axios。下麵代碼是項目中用到的 axios 的封裝。 http.js 註: 1. 上面代碼依賴了 elementui 框架的 Message 組建,用於提示錯誤消息 ...
  • 1. javascript的typeof返回哪些數據類型. 答案:string,boolean,number,undefined,function,object 2. 例舉3種強制類型轉換和2種隱式類型轉換? 答案:強制(parseInt,parseFloat,number) 隱式(== ) 3. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...