用JS描述的數據結構及演算法表示——棧和隊列(基礎版)

来源:http://www.cnblogs.com/zllwebstudy/archive/2016/08/26/5812004.html
-Advertisement-
Play Games

前言:找了上課時數據結構的教程來看,但是用的語言是c++,所以具體實現在網上搜大神的博客來看,我看到的大神們的博客都寫得特別好,不止講了最基本的思想和演算法實現,更多的是側重於實例運用,一邊看一邊在心裡隱隱歌頌大神的厲害,然後別人的厲害不是我的,所以到底看得各種受打擊+頭昏腦漲,寫這個系列是希望自己能 ...


前言:找了上課時數據結構的教程來看,但是用的語言是c++,所以具體實現在網上搜大神的博客來看,我看到的大神們的博客都寫得特別好,不止講了最基本的思想和演算法實現,更多的是側重於實例運用,一邊看一邊在心裡隱隱歌頌大神的厲害,然後別人的厲害不是我的,所以到底看得各種受打擊+頭昏腦漲,寫這個系列是希望自己能夠總結學到東一塊、西一下的知識,因為水平有限+經驗不足,所以在此只說最基礎的思想,附上我自己的演算法實現(肯定還有更優解),如果要想看進階版的,可以在園裡搜“數據結構”,各種語言實現和進階提升的文章有很多,希望大家都能儘快打敗數據結構這個紙老虎~

參考書是:數據結構(c++版)(第2版) 編者:王紅梅、胡明、王濤

正文:

熱身準備:

1、根據數據元素之間的不同關係,數據結構可以分為以下四種:

  (1)集合:數據元素之間的關係就是“屬於同一集合”,除此之外,沒有其他關係。(此關係過於簡單,就不詳述了)

  (2)線性結構:數據元素之間存在“一對一”的線性關係。

  (3)樹結構:數據元素之間存在“一對多”的層級關係。

  (4)圖結構:數據元素之間存在“多對多”的任意關係。

2、數據結構在電腦中的存儲方式,主要有兩種:順序存儲和鏈接存儲。

3、線性結構在電腦中的實現,即線性表。當然根據存儲方式的不同又可以分為:順序表和鏈表。

  (1)順序表:用一段地址連續的存儲單元依次存儲線性表的數據元素。通常用一維數組來實現。其數據元素間的關係由下標表現。

  (2)鏈表:最常見的是單鏈表。單鏈表是用一組任意的存儲單元存放數據元素。其數據元素間的關係由指針表現。除去單鏈表外,常見的還有迴圈鏈表和雙鏈表,都是在單鏈表的基礎上增加了更多信  息,便於實現某些操作。

 

開始運動:

棧和隊列都是某些功能受限制的線性表,所以它們有線性表的基本性質,但是更特別,也正是它們的特別之處才使得它們脫穎而出。

棧:限定僅在表尾進行插入和刪除操作的線性表,允許插入和刪除的一端成為棧頂,另一端稱為棧底,不含任何元素的棧稱為空棧。

  棧具有FILO(first in last out)即先進後出的特性。

1、棧的基本操作:

push

  前置條件:棧已存在

  輸入:元素值x

  功能:入棧操作,在棧頂插入一個元素x

  輸出:如果插入不成功,則拋出異常

  後置條件:如果插入成功,則棧頂增加一個元素

pop

  前置條件:棧已存在

  輸入:無

  功能:出棧操作,刪除棧頂元素

  輸出:如果刪除成功,返回被刪除元素,否則拋出異常

  後置條件:如果刪除成功,則棧頂減少一個元素

getTop

  前置條件:棧已存在

  輸入:無

  功能:取棧頂元素,讀取當前的棧頂元素

  輸出:如棧不空,返回當前的棧頂元素

  後置條件:棧不變

2、順序棧

function SeqStack(){
    this._stack = [];  //
    this._top = -1;    //棧頂指針
}
SeqStack.prototype = {
    _push:function(x){
    try{
this._stack.push(x); this._top++;
    }catch(e){
     console.log("壓棧失敗");
    } }, _pop:
function(){ if(this._top == -1) throw Error("棧空"); this._top--; return this._stack.pop(); }, _getTop:function(){ if(this._top == -1) return "空棧"; return this._stack[this._top]; } }; var s = new SeqStack(); s._push(1); s._push(2); s._push(3); console.log(s._getTop());//輸出3 s._pop(); console.log(s._getTop());//輸出2 s._pop(); s._pop(); console.log(s._getTop());//輸出空棧 s._pop();//拋出異常

特別要說:

(1)因為js是一門非常優秀的語言,(哈哈,沒有打廣告,只是日常告白),js已經實現了push,pop的操作,所以此處撿了個便宜,只是對於獲取棧頂(getTop)增加了實現。之所以在每個方法和屬性前加了下劃線,是因為看不慣sublime特別標註出來的顏色(感覺像是用了保留字一樣),所以如果你看不慣_完全可以不要_。

(2)因為js中的數組是可以自己隨著元素插入而增長的(如果大於你原本預計的長度),所以沒有設置maxSize,如果希望棧是定長的,可以再添加maxSize屬性。

(3)因為js不是強數據類型,所以如果需要棧做更實際化的操作可能會出現元素值x為特定值,沒關係,加判斷就好咯,交給你來,未來的靈魂程式猿(這什麼中二的稱呼)

3、鏈棧

function LinkStack(){
    this._top = null;
}
LinkStack.prototype = {
    _push:function(x){
    try{
var node = {_data:x,_next:null};//至少含有此處兩屬性 node._next = this._top;//當前和下一句不能互換,否則不能實現 this._top = node;
    }catch(e){
     console.log("壓棧失敗");
    } }, _pop:
function(){ if(this._top == null) throw Error("棧空"); var node = this._top; this._top = node._next; return node; }, _getTop:function(){ if(this._top == null) return "空棧"; return this._top._data; } }; var s = new LinkStack(); s._push(1); s._push(2); s._push(3); console.log(s._getTop()); console.log(s._pop()); console.log(s._getTop()); console.log(s._pop()); console.log(s._pop()); console.log(s._getTop()); s._pop();

特別要說:

(1)鏈棧如果要求長度的話,會需要遍歷整個棧才能計算(順序棧基於數組有一個length屬性可以獲得長度),所以如果在求長度很常用的情況下,可以再增加一個自定義的length屬性,每次_push時,獲取當前top的length值,加1後賦值給新增node的length屬性,則獲取鏈棧的長度就很容易了。

(2)因為壓入的node定義為了一個對象,所以_top初始化時用了null,非常明確的表明瞭之後壓入的node會是對象,同時判斷棧空時也很明確。建議使用這樣的方法初始化_top

棧先告一段落,繼續來說隊列。

隊列:只允許在一端進行插入,在另一端進行刪除的線性表。允許插入(也稱入隊、進隊)的一端稱為隊尾,允許刪除(也稱出隊)的一端稱為隊頭。

  隊列具有FIFO(first in first out)先進先出的特性。(想想棧呢?)

1、隊列的基本操作:

enQueue

  前置條件:隊列已存在

  輸入:元素值x

  功能:入隊操作,在隊尾插入一個元素

  輸出:如果插入不成功,拋出異常

  後置條件:如果插入成功,隊尾增加一個元素

deQueue

  前置條件:隊列已存在

  輸入: 無

  功能:出隊操作,刪除隊頭元素

  輸出:如果刪除成功,返回被刪除元素值,否則,拋出異常

  後置條件:如果刪除成功,隊頭減少一個元素

getQueue

  前置條件:隊列已存在

  輸入:無

  功能:讀取隊頭元素

  輸出:若隊列不可,返回隊頭元素

  後置條件:隊列不變

2、迴圈隊列

高能警報 隊列這裡有個坑,如果是在數組需要預先確定長度的環境里(如使用c++聲明數組,需要預先確實數組長度),當我入隊5個元素,又出隊5個元素,繼續入隊出隊,總有一個時刻,我的隊頭指針會到達數組的邊界,那麼即使前面所有的空間都是空閑的(因為出隊了),但是我此時依然無法再入隊,因為會被判斷“溢出”,這就是所謂了“假溢出”現象。那如果是在js中,當超過原定數組長度就會自動增加,那數組的長度不斷增加,隊頭指針之前的空間也一樣是浪費了的,所以,隊列的順序實現,採取了迴圈隊列。

迴圈隊列是將存儲隊列的數組看成是頭尾相接的迴圈結構,即允許隊列直接從數組中下標最大的位置延續到下標最小的位置。通過取模(%)很容易實現。

function CirQueue(){
    this._queue = [];
    this._front = this._rear = 0;//隊頭、隊尾指針
    this._maxSize = 10;//隊列定長為10,可以隨意設置
}
CirQueue.prototype = {
    _enQueue:function(x){
        try{
            if(this._front == (this._rear+1)%this._maxSize) throw Error("隊滿");//判斷是否隊滿
            this._queue.push(x);//入隊
            this._rear = (this._rear+1)%this._maxSize;//rear指向空閑空間
        }catch(e){
            console.log("入隊失敗");
        }
    },
    _deQueue:function(){
        if(this._front == this._rear) throw Error("隊空");//判斷是否隊空
        var q = this._queue[this._front];
        this._front = (this._front+1)%this._maxSize;
        return q;
    },
    _getQueue:function(){
        if(this._front == this._rear) throw Error("隊空");
        return this._queue[this._front];
    }
};
var q = new CirQueue();
q._enQueue(1);
q._enQueue(2);
q._enQueue(3);
console.log(q._getQueue());//1
console.log(q._deQueue());//1
console.log(q._getQueue());//2
console.log(q._deQueue());//2
console.log(q._getQueue());//3
console.log(q._deQueue());//3
console.log(q._getQueue());//拋出異常

特別要說:

(1)隊滿的判斷條件是front == (rear+1)%maxSize,因為犧牲了一個空閑空間,以便區別隊空隊滿,否則隊空隊滿的判斷條件都為front == rear,不方便操作。

(2)隊列刪除其實並沒有刪除元素,只是移動了front指針,讓那個元素看起來像是出隊了一樣。反正之後入隊新的元素值會覆蓋之前的值,不會對操作造成影響。

3、鏈隊

function LinkQueue(){
    this._front = this._rear = null;//隊頭、隊尾指針
}
LinkQueue.prototype = {
    _enQueue:function(x){
        try{
            var node = {_data:x,_next:null};
            if(this._rear == null){//當隊尾指針為null時,隊空
                this._rear = this._front = node;
            }else{
                this._rear._next = node;//將當前rear的下一個元素指向node
                this._rear = node;//把rear指向當前最後的元素,即node
            }
        }catch(e){
            console.log("入隊失敗");
        }
    },
    _deQueue:function(){
        if(this._front == null) throw Error("隊空");
        var q = this._front;
        this._front = this._front._next;//隊頭指針移動
        return q._data;
    },
    _getQueue:function(){
        if(this._front == null) throw Error("隊空");
        return this._front._data;
    }
};
var q = new LinkQueue();
q._enQueue(1);
q._enQueue(2);
q._enQueue(3);
console.log(q._getQueue());//1
console.log(q._deQueue());//1
console.log(q._getQueue());//2
console.log(q._deQueue());//2
console.log(q._getQueue());//3
console.log(q._deQueue());//3
console.log(q._getQueue());//拋出異常

以上棧和隊列的順序存儲和鏈接存儲的基本操作的演算法就完畢啦~完結撒花~說著玩的【嚴肅臉】接下來就是棧和隊列的應用舉例,可以自己寫寫代碼,如果有好的代碼求分享哦~比哈特

 

棧的應用舉例——表達式求值

  表達式求值是編譯程式中一個最基本問題。表達式是由運算對象、運算符和圓括弧組成的式子。運算符從運算對象的個數上分,有單目運算和雙目運算符;從運算類型上分,有算術運算、關係運算、邏輯運算等。在此只討論雙目運算的算術表達式。

  題目:中綴表達式3*(4+2)/2-5#的求值。(中綴表達式:運算符在運算對象中間的表達式)

  偽代碼:OPND—運算對象棧,OPTR—運算符棧,#結束符

  1.將棧OPND、OPTR初始化為空

  2.從左到右掃描每一個字元執行以下操作,直到遇到#

    2.1若當前字元為運算對象,入OPND棧

    2.2若當前字元是運算符且OPTR棧空,則入OPTR棧

    2.3若當前字元時運算符且OPTR棧不空,則比較棧頂元素和當前運算符的優先順序

      2.3.1 若當前元素為),棧頂元素為(,則從OPTR棧出棧一個元素,繼續2

      2.3.1 若棧頂元素優先順序高於或等於當前元素,則從OPND出棧兩個運算對象,從OPTR出棧一個運算符,將計算的結果壓入OPND棧中,繼續2

      2.3.2 若棧頂元素優先順序低於當前元素,則將當前元素入棧OPTR,繼續2

  3.輸出棧OPND的棧頂元素,即表達式的運算結果

  運算符優先順序為:()> * / > + - > #

var opnd = new SeqStack();//此處的SeqStack就是之前的順序棧
var optr = new SeqStack();

var str = "3*(4+2)/2-5#";//可以換成任何你希望的算式,也可以寫成函數,作為參數傳入,或者由用戶輸入,在處理字元串前記得在最後的地方加上#結束符
for(var i=0;i<str.length;i++){
    if(isNaN(parseInt(str[i]))){//判斷是否為數值,不為數值則進入if,為數值進入else
        console.log("optr:"+optr._getTop());
        if(optr._getTop() == "空棧" || optr._getTop() == "("){
            optr._push(str[i]);
            console.log("空棧或(:"+optr._getTop());
            continue;
        }
        if(str[i] == ")"){
            while(optr._getTop() != "("){
                opnd._push(calculate(opnd._pop(),opnd._pop(),optr._pop()));
            }
            optr._pop();
            console.log("52:"+optr._getTop());
            continue;
        }
        var compare = priority(optr._getTop())- priority(str[i]);
        switch(compare>=0){
            case true:
                console.log("59:"+opnd._getTop());
                opnd._push(calculate(opnd._pop(),opnd._pop(),optr._pop()));
                console.log("61:"+opnd._getTop());
                i--;//保證當前元素不會因為i++被跳過
                break;
            case false:
                optr._push(str[i]);break;
        }        
    }else{
        console.log("opnd:"+opnd._getTop());
        opnd._push(parseInt(str[i]));//當前字元解析為數值,壓入opnd棧中
    }
}
document.write(opnd._pop());
//運算符的優先順序
function priority(elem){
    switch(elem){
        case "(":
        case ")":elem = 3;break;
        
        case "*":
        case "/":elem = 2;break;

        case "+":
        case "-":elem = 1;break;

        default:elem = 0;
    }
    return elem;
}
//計算
function calculate(num1,num2,sign){
    switch(sign){
        case "*":
            return num2*num1;
        case "/":
            return num2/num1;
        case "+":
            return num2+num1;
        case "-":
            return num2-num1;
    }
}

 

隊列的應用舉例——火車車廂重排

  題目:給定任意編號的n節車廂,按照1~n的編號進行排序。現有一個入軌、一個出軌和k個緩衝軌進行排序,緩衝軌位於入軌和出軌直接。

  偽代碼:1.對k個緩衝軌初始化。初始化下一個要輸出的車廂號為 nowOut=1。

      2.依次取入軌中的車廂編號:

        2.1 如果當前車廂編號等於 nowOut,則

          2.1.1 輸出該車廂

          2.1.2 nowOut++

        2.2 當前車廂編號不等於 nowOut,考察每一個緩衝軌 for( i=0;i<k;i++)

          2.2.1 取緩衝軌 i 的隊頭元素c

          2.2.2 如果c等於nowOut,則

            2.2.2.1 將緩衝軌 i 的隊頭元素出隊並輸出

            2.2.2.2 nowOut++

        2.3 如果入軌和緩衝軌的隊頭元素沒用編號為nowOut的車廂,則

          2.3.1 求小於當前車廂編號的最大隊尾元素所在緩衝軌 i

          2.3.2 如果 i 存在,則把當前車廂一指該緩衝軌

          2.3.3 如果 i 不存在,則判斷是否有空緩衝軌

            2.3.3.1 有空緩衝軌,將當前車廂入緩衝軌

            2.3.3.2 沒有空緩衝軌,則無法重排車廂,演算法結束

 

function carriage(k,str){
    var queue = [];//存儲緩衝軌的數組,保存的元素是數組對象
    for(var j=0;j<k-1;j++){//有一條緩衝軌作為入軌到出軌的通道
        queue[j] = new LinkQueue();//鏈隊列
    }
    var nowOut = 1;//初始化需要出列的車廂號
    var result = "";//最後出列的順序
    for(j=0;j<str.length;j++){//遍歷傳入的數組
        if(str[j] == nowOut){//如果當前車廂號等於出列號,則直接出列,並將nowOut加一
            result += str[j];
            nowOut++;
        }else{
            var frontArr = [];//緩衝列的隊頭元素所在緩衝區和其車廂編號
            var rearArr = [];//緩衝區的隊尾元素所在緩衝區和其車廂編號
            var emptyQu = [];//存儲空的緩衝區
            var calc = false;//如果所以處理的方式都試過,但是並沒有辦法在繼續,則退出演算法
            for(var i=0;i<k-1;i++){//遍歷緩衝區,獲取存儲的數據
                try{//嘗試獲取隊頭和隊尾數據
                    frontArr.push({"_index":i,"_data":queue[i]._front._data});
                    rearArr.push({"_index":i,"_data":queue[i]._rear._data});    
                }catch(e){//如果緩衝區中沒有數據,則把當前緩衝區的下標存儲在emptyQu中
                    emptyQu.push(i);
                }
            }
            for(i=0;i<frontArr.length;i++){//查看隊頭信息,是否有隊頭能夠出隊
                if(frontArr[i]._data == nowOut){//當前隊頭正是需要出隊的車廂號
                    result += nowOut;
                    nowOut++;
                    queue[frontArr[i]._index]._deQueue();//出隊
                    j--;//當前的字元未用,而是在已入列的找到,故需要重新再次判斷
                    var finded = true;//已經出隊,則當前一輪操作可以退出,繼續下一輪
                    calc = true;//已經進行操作
                    break;
                }
            }
            if(window.finded){//退出當前一輪操作
                continue;
            }
            if(!!rearArr.length){//隊尾數據是否存在
                var lowRears = [];//存儲小於當前車廂號的隊尾元素
                for(i=0;i<rearArr.length;i++){
                    if(rearArr[i]._data < str[j]){
                        lowRears.push(rearArr[i]._data);
                    }
                }
                if(!!lowRears.length){//存在小於當前車廂號的隊尾元素
                    var max = Math.max.apply(Math,lowRears);//找出其中最大者
                    rearArr.filter(function(elem){//篩選出最大者,並使其進入相應緩衝區中
                        if(parseInt(elem["_data"]) == max){
                            queue[parseInt(elem["_index"])]._enQueue(str[j]);
                        }
                    });
                    calc = true;//已經處理了當前數據
                    continue;
                }
            }
            if(!!emptyQu.length){//空緩衝區是否存在
                queue[emptyQu[0]]._enQueue(str[j]);//將數據入隊
                continue;
            }
            if(!calc){//如果以上操作都沒能進行,則重排失敗,退出演算法
                return "重排失敗";
            }
        }
    }
    while(nowOut <= str.length){//將還在緩衝區中的數據依次遍歷,嘗試出列
        for(var m=0;m<queue.length;m++){
            try{
                if(queue[m]._getQueue() == nowOut){
                    result += nowOut;
                    queue[m]._deQueue();
                    nowOut++;
                }    
            }catch(e){};
        }
    }
    return result;//將最後的結果返回
}
document.write(carriage(3,[3,6,9,2,4,7,1,8,5]));//123456789
document.write(carriage(3,[3,10,6,9,2,4,7,1,8,5]));//重排失敗

後話:

啊啊啊啊啊,真的差點崩了,寫了5個小時才寫完(/(ㄒoㄒ)/~~)代碼意外的愛我呢,拉著我各種嘮嗑不准我走,然後我就對它說啊,你要雨露均沾,但是它就寵我就寵我【笑哭】

終於終於,寫完啦!完事開頭難,看來之後應該會越寫越順的,加油咯~代碼的地方,還有很多很多不足之處,不夠精簡,不夠健壯,所以之後會再回過頭來改改的,到時做一個大的總結也是不錯的,今天先這樣咯~晚安安~


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

-Advertisement-
Play Games
更多相關文章
  • 關於shopnc 以下是摘抄自百度百科的關於shopnc的介紹: ShopNC商城系統,是天津市網城天創科技有限責任公司開發的一套多店模式的商城系統。 本系統具有商城系統非常完整和專業的功能與流程,系統包括了訂單管理、商品管理、購物車功能、網上支付功能、信息管理、客戶管理、會員體系設置、優惠促銷、廣 ...
  • 安裝composer: 1、在https://getcomposer.org/download/ 中下載 Composer-Setup.exe 2、安裝composer步驟如下: 至此,composer安裝完成。 安裝laravel: 安裝composer完成後,win+R >> cmd,調出命令行 ...
  • 一、抽象類 抽象類,只為繼承而出現,不定義具體的內容,只規定該有哪些東西 一般抽象類中只放置抽象方法,只規定了返回類型和參數 比如: 人 - 有吃飯,睡覺方法 男人 - 繼承人抽象類,必須實現吃飯,睡覺的方法主體 女人 - 繼承人抽象類,必須實現吃飯,睡覺方法的主體 抽象類中可以有普通屬性,通過子類 ...
  • HTML代碼: jquery代碼: 註:只要修改動畫時間就可以控制滾動的速度。 ...
  • 線上實例 使用方法 ...
  • 現象: 近期在微信中開發了一個電商的平臺,一切介面頁面處理完成後,正式佈置到公眾號,在公眾號上自定義菜單進行平臺時(如:.../index.html),發現了一個很有意思的問題:哪個頁面是從公眾號里點擊進入的平臺的,當切換一兩次頁面時,.../index.html這個頁面就切換不進了。 解決過程: ...
  • 享元模式 在JavaScript中,瀏覽器特別是移動端的瀏覽器分配的記憶體很有限,如何節省記憶體就成了一件非常有意義的事情。節省記憶體的一個有效方法是減少對象的數量。 享元模式(Flyweight),運行共用技術有效地支持大量細粒度的對象,避免大量擁有相同內容的小類的開銷(如耗費記憶體),使大家共用一個類( ...
  • 什麼是Ajax Ajax 是一種在無需重新載入整個網頁的情況下,能夠更新部分網頁的技術。 Ajax的全稱是Asynchronous JavaScript and XML,即非同步JavaScript+XML。它並不是新的編程語言,而是幾種原有技術的結合體。它由以下幾種技術組合而成,包括: HTML/X ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...