深度優先遍歷 and 廣度優先遍歷

深度優先遍歷 and 廣度優先遍歷

遍歷在前端的應用場景很少,多數是處理DOM節點數或者 深拷貝。下面筆者以深拷貝爲例,簡單說明一些這兩種遍歷。 😄前端

深度優先遍歷

想象有一顆節點樹,從某個頂點開始,一直往下遍歷,直到遍歷到的節點都訪問事後,往回走,遍歷沒有訪問的節點,感受很像遞歸。下面筆者就用遞歸實現 深度優先遍歷。prototype

function getRegExp(target){
    var flat = '';
    if(target.global) flat+='g';
    if(target.ignoreCase) flat+='i';
    if(target.multiline) flat+='m';
    return target;
}

function DFS(target,visiteds){
    var Type = Object.prototype.toString.call(target).slice(8,-1);
    console.log(Type)
    var copy = Type == 'Array'?[]:{};
    visiteds = visiteds|| []; //處理環形數據,防止無限循環
    switch(Type){
        case 'Date':
            copy = new Date(target.getTime());
        break;
        case 'RegExp':
            copy = new RegExp(target.source,getRegExp(target));
        break;
        case 'Array':
        case 'Object':
            var index = visiteds.indexOf(target);
            if(index>-1){
                copy = visiteds[index];
            }else{
                visiteds.push(target);
                for(var key in target){
                    copy[key] = DFS(target[key],visiteds);
                }
            }
        break;
        default:
            copy = target;
        break; 

    }
    return copy;
};

廣度優先遍歷

若是把深度優先遍歷當作縱向遍歷,那麼廣度優先遍歷就是橫向遍歷,一層一層的往下遍歷。下面用隊列(FIFO)來實現。code

function getEmpty(o){
        var Type = Object.prototype.toString.call(o).slice(8,-1);
    if(Type === 'Object'){
        return {};
    }
    if(Type === 'Array'){
        return [];
    }
        if(Type==='Date'){
            return new Date(o.getTime());
        }
        if(Type==='RegExp'){
            return new RegExp(o.source,getRegExp(o))
        }
        
    return o;
}

function getRegExp(o){
    var flat = '';
    if(o.global) flat+='g';
    if(o.ignoreCase) flat+='i';
    if(o.multiline) flat+='m';
    return o;
}

function BFS(target){
    var queue = [];
    var targetMap = []; //處理環形數據,防止無限循環
    var copy = getEmpty(target);
    if(copy!==target){
        queue.push([target,copy]);
    }
    while(queue.length>0){
        var [_target,_copy] = queue.shift();  //*
        for(var key in _target){
            var index = targetMap.indexOf(_target[key]);
            if(index>-1){
                _copy[key] = targetMap[index]
                continue;
            }
            _copy[key] = getEmpty(_target[key])
            if(_copy[key]!==_target[key]){
                queue.push([_target[key],_copy[key]]); //*
                targetMap.push(_target[key]);
            }
        }
    }
    return copy
}

總結

上面用兩種不一樣的方法實現了深拷貝,可是隻針對Object,Array的狀況,其餘的複雜對象沒有考慮到,固然你也能夠添加更多的處理,但筆者認爲目前這樣已經足夠用了。。。
深度優先遍歷,關鍵在於理解遞歸,而廣度優先遍歷,關鍵在於理解,queue.shift出去的數據保存着原來數據的引用,因此纔可以在不斷的進棧出棧中修改值(間接修改值) 👊對象

相關文章
相關標籤/搜索