前端算法系列之二:數據結構鏈表、雙向鏈表、閉環鏈表、有序鏈表

前言

上一次咱們講到了數據結構:棧和隊列,並對他們的運用作了一些介紹和案例實踐;咱們也講到了怎麼簡單的實現一個四則運算、怎麼去判斷標籤是否閉合徹底等等,anyway,今天接着和你們介紹一些數據結構:
上一篇:前端算法系列之一:時間複雜度、空間複雜度以及數據結構棧、隊列的實現前端

鏈表

鏈表是一種怎麼樣的結構呢?鏈表就是一種能夠把數據串聯起來的結構,每一個元素會有指向下一個元素的指針(末尾的沒有普通鏈表),就像現實世界中的火車同樣一節一節的串聯起來;鏈表根據自身的指針指向又能夠分爲:單向鏈表、雙向鏈表、循環鏈表;node

微信圖片_20210121115535.jpg

微信圖片_20210121162755.jpg

鏈表首先會有一個表頭,表頭做爲起始的指針,而後每個元素咱們稱做爲節點(node);每一個節點有一個指向下一個節點的指針(next),直到鏈表的末尾指針會指向undefined;git

鏈表的實現

一、節點

節點的建立和定義;每一個節點會有一個保存本身數據的屬性(element),而後有一個指針(next)github

export class Node {
    constructor(element, next = null) {
        this.element = element;
        this.next = next;
    }
}

二、鏈表的api

getElementAt(position): 獲取某個位置的元素算法

append(element): 向鏈表末尾中添加元素segmentfault

removeAt(idx): 移除某個元素api

insert(element, position = 0, dir = 'before'): 向指定位置添加元素微信

insertAfter(element, position): 向指定的位置後面添加元素數據結構

size(): 鏈表的長度app

remove(): 刪除鏈表末端元素

removeAll(): 刪除整個鏈表

isEmpty(): 檢查鏈表是否爲空

import { defaultEquals } from "../util.js";
import { Node } from './Node.js'

export default class LinkedList {
    constructor(equalsFn = defaultEquals) {
        this.count = 0;
        this.head = null;
        this.equalsFn = equalsFn;
    }
    getElementAt(position) {
        if(position >= 0 && position <= this.count) {
            let node = this.head;
            for (let i = 0; i < position && !!node; i++) {
                node = node.next;
            }
            return node;
        }
        return undefined;
    }

    insertAfter(element, position) {
        return this.insert(element, position, 'after');
    }
    size() {
        return this.count;
    }
    remove() {
        return this.removeAt(this.size() - 1);
    }
    removeAll() {
        this.count = 0;
        this.head = null;
    }
    isEmpty() {
        return this.size() === 0;
    }
    getHead() {
        return this.head;
    }
}

三、鏈表末尾添加一個元素append;

append(element) {
        const node = new Node(element);
        let current = this.head;
        if(current == null) {
            this.head = node;
        } else {
            current = this.head;
            while (current.next !=  null) {
                current = current.next;
            }
            current.next = node
        }
        this.count++;
        return element;
    }

link.png

四、插入一個元素

insert(element, position = 0, dir = 'before') {
        if (element === undefined) {
             throw Error('缺乏須要插入的元素');
             return;
        }
        if (position >= this.count) {
            return this.append(element);
        }
        const node = new Node(element);
        const targetNode = dir === 'before' ? this.getElementAt(position - 1) : this.getElementAt(position);
        if (!targetNode) {
            let prev = this.head;
            this.head = node;
            node.next = prev;
        } else {
            let next;
            next = targetNode.next
            targetNode.next = node;
            node.next = next;
        }
        this.count++;
        return element;
    }

link-insert.png

五、刪除一個元素

removeAt(idx) {
        if (idx >= 0 && idx < this.count) {
            let current = this.head;
            if (idx === 0) {
                this.head = current.next;
                current.next = null;
            } else {
                let prev = this.getElementAt(idx - 1);
                current = prev.next;
                prev.next = current.next;
            }
            this.count--;
            return current.element;
        }
        return undefined;
    }

remove.png

六、雙向鏈表

單向鏈表元素指向都是一個方向的,也只能被單向遞歸搜索,而雙向鏈表不單單有指向下一個元素的指針同時具備指向上一個元素的指針;

doublelink.png

import LinkedList from "./LinkedList";
import {defaultEquals} from "../util";
import { DoubleNode } from "./DoubleNode";

export default class DoubleLinkedList extends LinkedList{
    constructor(equalIsFn = defaultEquals){
        super(equalIsFn);
        this.tail = null;// 隊列尾部
    }
    getElementAt(position) {
        if(position >= 0 && position <= this.count) {
            if (position > this.count/2) {
                let cur = this.tail;
                for (let i = this.count - 1; i > position; i--){
                    cur = cur.prev;
                }
                return cur;
            } else {
                return super.getElementAt(position)
            }
        }
        return undefined;
    }
    removeAll() {
        super.removeAll();
        this.tail = null;
    }
    removeAt(position) {
        if (position >= 0 && position < this.count) {
            let cur = this.getElementAt(position);
            if(position === 0) {
                this.head = cur.next;
                cur.next = null;
                this.prev = null;
            } else if (position === this.count - 1) {
                this.tail = cur.prev;
                this.tail.next = null;
                cur.prev = null;
            } else {
                let prev = cur.prev;
                let next = cur.next;
                prev.next = next;
                next.prev = prev;
                cur.prev = null;
                cur.next = null;
            }
            this.count--;
            return cur.element;
        }
        return undefined;
    }
}

隊列末尾插入元素(append)

雙向鏈表插入元素和單向比較相似,不一樣的是雙向不只要連接他的下級還得關聯他的前一級;

append(element) {
        const node = new DoubleNode(element);
        if (!this.tail) {
            this.head = node;
            this.tail = node;
        } else {
            let cur = this.tail;
            cur.next = node;
            node.prev = cur;
            this.tail = node;
        }
        this.count++;
        return element;
    }

doulinkappend.png

中間某個位置插入元素

insert(element, position = 0, dir = 'before'){
        if (element === undefined) {
            throw Error('缺乏須要插入的元素');
            return;
        }
        if (position >= this.count) {
            return this.append(element);
        }
        const node = new DoubleNode(element);
        let cur;
        const targetNode = dir === 'before' ? this.getElementAt(position - 1) : this.getElementAt(position);
        if (!targetNode) {
            cur = this.head;
            node.next = cur;
            cur.prev = node;
            this.head = node;
        } else {
            let next;
            next = targetNode.next
            targetNode.next = node;
            node.prev = targetNode;
            node.next = next;
            next.prev = node;
        }
        this.count++;
        return element;
    }

insertDoub.png

移除某個元素也是上述相同,修改節點的先後指針就能夠了,這裏再也不贅述,詳情看源碼

https://github.com/JasonCloud/DataStructuresAndAlgorithms

閉環鏈表

閉環鏈表也稱環,是一個閉合的結構,尾部會指向頭部
閉環鏈表

有序鏈表

有序鏈表就是在append元素的時候進行排序加入從而獲得一個有順序的鏈表,比較函數能夠根據實例化的時候傳入比較函數equalIsFn;

獲取第一手信息請關注個人專欄或者關注公衆號
image

相關文章
相關標籤/搜索