1. 程式人生 > >JS資料結構和演算法 --- 連結串列

JS資料結構和演算法 --- 連結串列

概念:性質類似於陣列,是計算機的一種儲存結構。連結串列由一系列結點組成,每個結點裡包含了本結點的資料域和指向下一個結點的指標(裡面儲存著下一個結點)。

作用:按一定順序儲存資料,允許在任意位置插入和刪除結點。

分類:雙向連結串列、迴圈連結串列

應用場景:對線性表的長度或者規模難以估計;頻繁做插入和刪除操作;構建動態性比較強的線性表。

操作方法:

1、append(element) 尾部插入元素

     1)連結串列為空時,即插入的元素作為連結串列頭(直接賦值)

     2)連結串列不為空時,從連結串列頭開始查詢,若當前查詢項的指標不為null,繼續查詢下一項;直到當前查詢項的指標等於null(最後一項),把新元素賦值當前項的指標(尾部插入)

 

2、insert(position,element) 向連結串列插入元素

     1)頭部插入(下標等於0),即插入的元素作為連結串列頭

     2)非頭部插入,從頭部開始查詢,當前查詢項的下標不等於position時,繼續向後查詢,直到查詢項下標等於position,使當前查詢的前一項的指標指向插入項,插入項的指標指向當前項,完成插入。

 

3、removeAt(position) 根據下標移除元素

     1)移除第1項(下標等於0),即使原連結串列頭的指標作為新連結串列頭

     2)移除非第一項(下標大於0),即跳過當前結點,使前一個結點的指標 指向 當前節點的指標

4、indexOf(element) 查詢某元素的下標

     1)從頭部開始查詢,如果當前查詢項等於目標項就返回對應的下標,否則繼續往後查詢;

     2)若連結串列迴圈結束了還沒找到,則返回-1(不存在)

5、remove(element) 直接移除某項元素(程式碼複用)

     1)呼叫indexOf(element)方法找到目標項的下標;

     2)呼叫removeAt(position)方法根據目標項的下標移除目標項

6、isEmpty() 判斷連結串列是否為空

7、size() 查詢連結串列的大小

例項:js實現各操作方法的詳解

/* 一個連結串列類 */
function LinkedList(element){
    // 列表頭
    let head = null;
    // 連結串列長度
    let length = 0;
    // 輔助類:結點
    function NodeItem(element){
        this.element = element;    // 資料域
        this.next = null;          // 指標
    }
    /* 
        append(element) 連結串列尾新增元素 
        1)連結串列為空時,即插入的元素作為連結串列頭(直接賦值)
        2)連結串列不為空時,從連結串列頭開始查詢,若當前查詢項的指標不為null,繼續查詢下一項;直到當前查詢項的指標等於null(最後一項),把新增項賦值給當前項的指標(尾部插入)
    */
    this.append = (element)=>{
        let node = new NodeItem(element);   // 呼叫輔助類建立一個新結點
        // 連結串列為空
        if(head == null){   
            head = node;    // 把第一個新增的結點作為連結串列頭
        }
        // 已存在結點
        else{
            let current = head;             // 定義一個current變數,儲存head連結串列頭的值,代表當前正在查詢的值(從頭部開始查詢)
            while(current.next!==null){     // 當前查詢項的指標不為null(即不是最後一項),繼續往後找,直到next=null迴圈結束
                current = current.next;     // 把下一項賦值給當前查詢項,即查詢下一項(迴圈的經典方法)
            }
            // while迴圈結束後,current已經到了最後一項(current.next=null)
            current.next = node;            // 插入新元素
        }
        length++;
    };
    /* 
        insert(position,element) 向連結串列插入元素
        1)頭部插入(下標等於0),即插入的元素作為連結串列頭
        2)非頭部插入,從頭部開始查詢,當前查詢項的下標不等於position時,繼續向後查詢,直到查詢項下標等於position,使當前查詢的前一項的指標指向插入項,插入項的指標指向當前項,完成插入。
    */
    this.insert = (position, element)=>{
        let node = new NodeItem(element);       // 呼叫輔助類建立一個新元素結點(即插入的元素)
        // 越界
        if(position>-1 && position<length){
            // 連結串列頭插入
            if(position==0){                    
                let current = head;             // 先把原表頭儲存在current變數裡
                head = node;                    // 把插入項賦值給表頭(即插入連結串列頭)
                head.next = current;            // 使插入項的指標指向原表頭(連結起來)
            }
            // 非連結串列頭插入
            else{
                let index = 0;                  // 當前查詢項的下標
                let previous = null;            // 查詢項的前一項
                let current = head;             // 從連結串列頭開始查詢,先把它儲存在current變數裡
                while(index < position){        // 查詢的當前項下標小於目標項下標時,繼續往後找
                    previous = current;         // 當前項賦值給前一項
                    current = current.next;     // 當前項的指標賦值給當前項,即查詢項往後移動一項
                    index++;                    // 下標加一
                }
                // 直到index=position時,即找到了目標項
                previous.next = node;           // 前一項的指標指向插入項
                node.next = current;            // 插入項的指標指向當前找到的目標項
                // 完成上面兩步,即完成插入
            }
            length++;
        }
    };
    /* 
        removeAt(position) 根據下標移除元素
        1)移除第1項(下標等於0),即使原連結串列頭(第一項)的指標作為新連結串列的表頭
        2)移除非第一項(下標大於0),即跳過當前結點,使前一個結點的指標 指向 當前節點的指標
    */
    this.removeAt = (position)=>{
        if(position>-1 && position<length){
            let current = head;  
            // 移除第一項
            if(position==0){                    
                head = current.next;            // 使原表頭的指標變成新表頭,到達移除原表頭的目的
            }
            // 移除非第一項
            else{
                let index = 0;                  // 當前項的下標
                let previous = null;            // 當前查詢項的前一個結點
                // let current = head;             // 從頭部開始查詢
                while(index<position){          // 當前結點的下標小於指定結點下標時,往後移動一項,繼續查詢
                    previous = current;         // 往後移動一項的方法就是,把後一項賦值給前一項
                    current = current.next;     
                    index++;
                }
                // 當index = position 時跳出迴圈,即找到了要移除的那一項
                previous.next = current.next;   // 跳過當前項,使前一項的指標指向當前項的指標
            }
            length--;   //長度減1
            return current;
        }
    };
    /* 
        indexOf(element) 查詢某元素的下標 
        1)從頭部開始查詢,如果當前查詢項等於目標項就返回對應的下標,否則繼續往後查詢;
        2)若連結串列迴圈結束了還沒找到,則返回-1(不存在)
    */
    this.indexOf = (element)=>{
        let index = 0;                          // 下標
        let current = head;                     // 從頭部開始查詢
        while(current !== null){                // 當前項不等於null,表示還不是最後一項,繼續迴圈
            if(element === current.element){    // 當前項的值等於目標項
                return index;                   // 返回對應下標
            }
            current = current.next;             // 否則繼續往後查詢
            index++;                            // 下標加一
        }
        return -1;       // 若迴圈結束還沒找到,返回-1,連結串列裡不存在該元素
    };
    /* 
        remove(element) 直接移除某項元素(程式碼複用)
        1)呼叫indexOf(element)方法找到目標項的下標;
        2)呼叫removeAt(position)方法根據目標項的下標移除目標項 
    */
    this.remove = (element)=>{
        return this.removeAt(this.indexOf(element));
    };
    /* isEmpty() 判斷連結串列是否為空 */
    this.isEmpty = ()=>{
        return length == 0;
    };
    /* size() 查詢連結串列的大小 */
    this.size = ()=>{
        return length;
    };
    /* 檢視連結串列  */ 
    this.getHead = ()=>{     
      return head;       
    };

}

(完)