国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

你可能知道的 javaScript 數據結構與算法

alin / 825人閱讀

摘要:本文已同步到你可能知道的數據結構與算法,歡迎。新添加的或待刪除的元素都保存在棧的末尾,稱作棧頂,另一端就叫棧底。參考文章學習數據結構與算法數據結構與算法描述

本文已同步到github 你可能知道的 javaScript 數據結構與算法,歡迎Star。

如果想閱讀筆者更多文章,歡迎猛戳這里

關于數據結構與算法,終于抽時間把之前看過的這兩本書《學習JavaScript數據結構與算法》、《數據結構與算法JavaScript描述》,整理出來了一部分內容,由于最近較忙,先把已整理出來的內容發一下。對于未整理出來的內容會在后續整理出來,并更新到此文,也會隨著對數據結構與算法不斷的學習,不斷優化更新此文,感興趣的小伙伴可以先收藏哦。這兩本書對前端來講是很好的入門數據結構與算法的書,個人感覺《學習JavaScript數據結構與算法》這本書從排版以及思路上更清晰一些。

另外,為了截圖和驗證方便,本文的例子大多是書中的例子。

本文較長,如果閱讀起來不方便,可鏈接到我的github中,多帶帶查看。

github
數據結構
隊列
鏈表
集合
排序算法 冒泡排序
選擇排序
插入排序

閑言少敘,直接開始了

JavaScript 數據結構
棧是一種遵從后進先出LIFO(Last In First Out,后進先出)原則的有序集合。新添加的或待刪除的元素都保存在棧的末尾,稱作棧頂,另一端就叫棧底。

定義一個棧的類,并為該棧聲明一些方法,存儲數據的底層數據結構使用數組

class Stack {
    constructor() {
        this.dataStore = []
    }

    // 向棧中添加一個或多個元素到棧頂
    push() {
        for (let i = 0; i < arguments.length; i++) {
            this.dataStore.push(arguments[i])
        }
    }

    // 移出棧頂元素,并返回被移出的元素
    pop() {
        return this.dataStore.pop()
    }

    // 返回棧頂元素,不對棧做修改
    peek() {
        return this.dataStore[this.dataStore.length - 1]
    }

    // 判斷棧是否為空,如果為空返回true,否則返回false
    isEmpty() {
        return this.dataStore.length === 0
    }

    // 清空棧
    clear() {
        this.dataStore = []
    }

    // 返回棧中元素的個數

    size() {
        return this.dataStore.length
    }
}

// 棧的操作

let stack = new Stack()

stack.push(1, 2, 3)
console.log(stack.dataStore) // [1, 2, 3]
console.log(stack.pop())     // 3
console.log(stack.dataStore) // [1, 2]
console.log(stack.peek())    // 2
console.log(stack.dataStore) // [1, 2]
console.log(stack.size())    // 2
console.log(stack.isEmpty()) // false
stack.clear()
console.log(stack.dataStore)  // []
console.log(stack.isEmpty())  // true
console.log(stack.size())     // 0

棧的應用

本文舉書中一個進制轉換的例子并稍作修改,棧的類還是使用上面定義的Stack

function transformBase(target, base) {
    let quotient;                    // 商
    let remainder;                   // 余數
    let binaryStr = "";              // 轉換后的值
    let digits = "0123456789ABCDEF"  // 對轉換為16進制數做處理
    let stack = new Stack()
    while(target > 0) {
        remainder = target % base
        stack.dataStore.push(remainder)
        target = Math.floor(target / base)
    }

    while(!stack.isEmpty()) {
        binaryStr += digits[stack.dataStore.pop()].toString()
    }

    return binaryStr
}

console.log(transformBase(10, 2))   // 1010
console.log(transformBase(10, 8))   // 12
console.log(transformBase(10, 16))  // A
隊列
隊列是遵循FIFO(First In First Out,先進先出,也稱為先來先服務)原則的一組有序的項。隊列在尾部添加新元素,并從頂部移除元素。最新添加的元素必須排在隊列的末尾。

其實隊列和棧類似,只是原則不同,隊列是先進先出,用代碼來實現一個隊列及操作隊列的一些方法,以下用代碼實現一個隊列的類, 測試和棧類似,就不做具體測試了。

class Queue {
    constructor() {
        this.dataStore = []
    }

    // 入隊
    enqueue() {
        for (let i = 0; i < arguments.length; i++) {
            this.dataStore.push(arguments[i])
        }
    }

    // 出隊
    dequeue() {
        return this.dataStore.shift()
    }

    // 返回隊列第一個元素,不改變隊列
    front() {
        return this.dataStore[0]
    }

    // 隊列是否為空
    isEmpty() {
        return this.dataStore.length === 0
    }

    // 返回隊列的的元素個數
    size() {
        return this.dataStore.length
    }
}
優先隊列

隊列中在生活中有著大量應用,如登機時,商務艙要優于經濟艙,這時候可以給隊列中的元素設置優先級,下面用代碼來實現一個優先隊列的類

class PriorityQueue {

    constructor() {
        this.dataStore = []
    }

    isEmpty() {
        return this.dataStore.length === 0
    }

    enqueue(element, priority) {

        function QueueElement(element, priority) {
            this.element = element
            this.priority = priority
        }
        // 定義每次往隊列里添加的元素
        let queueElement = new QueueElement(element, priority)

        if (this.isEmpty()) {
            // 如果每次隊列為空直接添加到隊列中
            this.dataStore.push(queueElement)
        } else {
            // 定一個是否被添加到隊列的標志
            let isAdded = false
            for (let i = 0; i < this.dataStore.length; i++) {
                if (queueElement.priority < this.dataStore[i].priority) {
                    // 優先級數值越小,代表優先級越高
                    this.dataStore.splice(i, 0, queueElement)
                    isAdded = true
                    break;
                }
            }

            if (!isAdded) {
                // 如果被添加的新元素優先級最低,添加到隊尾
                this.dataStore.push(queueElement)
            }
        }
    }
    //
}

let priorityQueue = new PriorityQueue()

priorityQueue.enqueue("a", 5)
priorityQueue.enqueue("b", 2)
priorityQueue.enqueue("c", 3)

console.log(priorityQueue.dataStore)

最后的隊列如下圖:

鏈表

直接上代碼:

class LinkedList {
    constructor() {
        this.head = null // 鏈表的第一個元素
        this.length = 0
    }
    // 向鏈表尾部添加一個新元素
    append(element) {
        let Node = function(element) {
            this.element = element
            this.next = null
        }
        let node = new Node(element)
        let currentNode;
        if (this.head == null) {
            // 如果鏈表head為null,表示鏈表無元素,直接把node賦值給head即可
            this.head = node
        } else {
            currentNode = this.head
            while (currentNode.next) {
                // 每次循環會進行到鏈表的倒數第一個元素,把currentNode設置為倒數第一個元素
                currentNode = currentNode.next
            }
            // 把新增的node賦值給currentNode的next屬性,最后一個元素的next永遠為null
            currentNode.next = node
        }
        // 鏈表的元素個數每次append后 +1
        this.length++
    }
    // 從鏈表中按位置刪除元素
    removeAt(position) {
        // position表示要移除元素的位置
        let index = 0
        let previous = null
        let currentNode = this.head
        if (position >= 0 && position < this.length) {
            while (index < position) {
                // 主要是找出position位置的元素,設置為currentNode
                previous = currentNode
                currentNode = currentNode.next
                index++
            }
            // 把currentNode的上一個元素的next指向currentNode的下一個元素,就對應刪除了currentNode
            previous.next = currentNode.next
        } else {
            // 表示鏈表中不存在這個元素,直接return null
            return null
        }
        // 刪除后鏈表的元素個數每次刪除減1
        this.length--;
        // 返回刪除的元素
        return currentNode
    }
    // 按元素值刪除元素
    remove(element) {
        let index = this.indexOf(element)
        return this.removeAt(index)
    }
    // 向鏈表中插入新元素
    insert(element, position) {
        // element表示被插入元素的具體值
        // position表示被插入元素的位置
        if (position >= 0 && position < this.length) {
            let index = 0
            let previous = null
            let currentNode = this.head
            let Node = function(element) {
                this.element = element
                this.next = null
            }
            let node = new Node(element)
            while (index < position) {
                previous = currentNode
                currentNode = currentNode.next
                index++
            }
            // 把當前元素的上一個元素的next設置為被插入的元素
            previous.next = node
            // 把被插入元素的next設置為當前元素
            node.next = currentNode
            // 鏈表元素個數加1
            this.length++;
            // 如果插入元素成功,返回true
            return true
        } else {
            // 如果找不到插入元素位置,返回false
            return false
        }
    }
    // 查找元素在鏈表中的位置
    indexOf(element) {
        let currentNode = this.head
        let index = 0
        // 如果currentNode也就是head為空,則鏈表為空不會進入while循環,直接返回 -1
        while (currentNode) {
            if (element === currentNode.element) {
                // 如果被找到,返回當前index
                return index
            }
            // 每一輪循環如果被查找元素還沒有被找到,index后移一位,currentNode指向后一位元素,繼續循環
            index++
            currentNode = currentNode.next
        }
        // 如果一直while循環結束都沒找到返回 -1
        return -1
    }
    // 鏈表是否為空
    isEmpty() {
        return this.length === 0
    }
    // 鏈表元素個數
    size() {
        return this.length
    }
}

let linkedList = new LinkedList()

linkedList.append("a")
linkedList.append("b")
linkedList.append("c")
linkedList.append("d")
linkedList.removeAt(2)
linkedList.insert("e", 2)
console.log("bIndex", linkedList.indexOf("b"))
console.log("fIndex", linkedList.indexOf("f"))
linkedList.remove("d")
console.log(linkedList)

上述代碼測試結果如下圖所示:

集合
class Set {
    constructor() {
        this.items = {}
    }

    has(val) {
        return val in this.items
    }

    // 向集合中添加一個新的項
    add(val) {
        this.items[val] = val
    }

    // 從集合中移除指定項
    remove(val) {
        if (val in this.items) {
            delete this.items[val]
            return true
        }
        return false
    }

    // 清空集合
    clear() {
        this.items = {}
    }

    // 返回集合中有多少項
    size() {
        return Object.keys(this.items).length
    }

    // 提取items對象的所有屬性,以數組的形式返回
    values() {
        return Object.keys(this.items)
    }

    // 取當前集合與其他元素的并集
    union(otherSet) {
        let unionSet = new Set()
        let values = this.values()
        for (let i = 0; i < values.length; i++) {
            unionSet.add(values[i])
        }

        let valuesOther = otherSet.values()
        for (let i = 0; i < valuesOther.length; i++) {
            unionSet.add(valuesOther[i])
        }
        return unionSet
    }

    // 取當前集合與其他元素的交集
    intersection(otherSet) {
        let intersectionSet = new Set()
        let values = this.values()
        for (let i = 0; i < values.length; i++) {
            if (otherSet.has(values[i])) {
                intersectionSet.add(values[i])
            }
        }
        return intersectionSet
    }

    // 取當前集合與其他元素的差集
    diff(otherSet) {
        let intersectionSet = new Set()
        let values = this.values()
        for (let i = 0; i < values.length; i++) {
            if (!otherSet.has(values[i])) {
                intersectionSet.add(values[i])
            }
        }
        return intersectionSet
    }

    // 判斷當前集合是否是其他集合的子集
    isSubSet(otherSet) {
        // 如果當前集合項的個數大于被比較的otherSet的項的個數,則可判斷當前集合不是被比較的otherSet的子集
        if (this.size() > otherSet.size()) {
            return false
        } else {
            let values = this.values()
            for (let i = 0; i < values.length; i++) {
                // 只要當前集合有一項不在otherSet中,則返回false
                if (!otherSet.has(values[i])) {
                    return false
                }
            }
            // 循環判斷之后,當前集合每一項都在otherSet中,則返回true
            return true
        }
    }
}
// 測試
let setA = new Set()
setA.add("a")
setA.add("b")
setA.add("c")
setA.remove("b")
console.log(setA.values()) // ["a", "c"]
console.log(setA.size()) // 2

let setB = new Set()
setB.add("c")
setB.add("d")
setB.add("e")

let unionAB = setA.union(setB)
console.log(unionAB.values()) // ["a", "c", "d", "e"]
let intersectionAB = setA.intersection(setB)
console.log(intersectionAB.values()) // ["c"]

let diffAB = setA.diff(setB)
console.log(diffAB.values()) // ["a"]

let setC = new Set()
setC.add("d")
setC.add("e")

let isSubSetCB = setC.isSubSet(setB)
console.log(isSubSetCB) // true

let isSubSetAB = setA.isSubSet(setB)
console.log(isSubSetAB) // false
一個樹結構包含一系列存在父子關系的節點。每個節點都有一個父節點(除了頂部的第一個節點)以及零個或多個子節點
二叉樹
// 創建一個鍵
function createNode(key) {
    this.key = key
    this.left = null
    this.right = null
}

// 向樹中插入鍵
function insertNode(node, newNode) {
    if (newNode.key < node.key) {
        if (node.left === null) {
            node.left = newNode
        } else {
            insertNode(node.left, newNode)
        }
    } else {
        if (node.right === null) {
            node.right = newNode
        } else {
            insertNode(node.right, newNode)
        }
    }
}

// 遍歷回調
function printNode(value) {
    console.log(value)
}

// 中序遍歷
function inOrderTraverseNode(node, callback) {
    if (node !== null) {
        inOrderTraverseNode(node.left, callback)
        callback(node.key)
        // debugger 可以加入debugger,用瀏覽器控制觀察Call Stack(執行環境棧)來分析程序執行過程
        inOrderTraverseNode(node.right, callback)
    }
}

// 先序遍歷
function prevOrderTraverseNode(node, callback) {
    if (node !== null) {
        // 先訪問節點本身
        callback(node.key)
        // 再訪問左側節點
        prevOrderTraverseNode(node.left, callback)
        // 然后再訪問右側節點
        prevOrderTraverseNode(node.right, callback)
    }
}

// 后序遍歷
function postOrderTraverseNode(node, callback) {
    if (node !== null) {
        // 先訪問左側節點
        postOrderTraverseNode(node.left, callback)
        // 再訪問右側節點
        postOrderTraverseNode(node.right, callback)
        // 然后再訪問節點本身
        callback(node.key)
    }
}

class BinarySearchTree {
    constructor() {
        this.key = null
    }
    insert(key) {
        let newNode = new createNode(key)
        if (this.key === null) {
            this.key = newNode
        } else {
            insertNode(this.key, newNode)
        }
    }
    // 中序遍歷訪問節點(結果為按值由小到大訪問)
    inOrderTraverse(callback) {
        inOrderTraverseNode(this.key, callback)
    }
    // 先序遍歷訪問節點(結果為先訪問節點本身,再左側節點,然后再訪問右側節點)
    prevOrderTraverse(callback) {
        prevOrderTraverseNode(this.key, callback)
    }
    // 后序遍歷訪問節點(結果為先訪問左側節點,再訪問右側節點,然后再訪問節點本身)
    postOrderTraverse(callback) {
        postOrderTraverseNode(this.key, callback)
    }
    // 查找樹中的最小值
    findMin(node) {
        if (node) {
            while(node && node.left !== null) {
                node = node.left
            }
            return node.key
        }
        return null
    }
    // 查找樹中的最小值對應的節點
    findMinNode(node) {
        if (node) {
            while(node && node.left !== null) {
                node = node.left
            }
            return node
        }
        return null
    }

    // 查找樹中的最大值
    findMax(node) {
        if (node) {
            while(node && node.right !== null) {
                node = node.right
            }
            return node.key
        }
        return null
    }

    // 查找樹中的特定值,如果存在返回true,否則返回false
    search(node, key) {
        if (node === null) {
            return false
        }

        if (key < node.key) {
            // 如果被查找的key小于節點值,從節點的左側節點繼續遞歸查找
            return this.search(node.left, key)
        } else if (key > node.key) {
            // 如果被查找的key大于節點值,從節點的左側節點繼續遞歸查找
            return this.search(node.right, key)
        } else {
            // 被查找的key等于node.key
            return true
        }
    }

    // 移除樹中的特定節點
    removeNode(node, key) {
        if (node === null) {
            return null
        }

        if (key < node.key) {
            node.left = this.removeNode(node.left, key)
        } else if (key > node.key) {
            node.right = this.removeNode(node.right, key)
        } else {
            // console.log(node)
            // 移除葉子節點(無左右節點的節點)
            if (node.left === null && node.right === null) {
                node = null
                return node
            }

            // 移除只有一個節點的節點(只有左節點或只有右節點)
            if (node.left === null) {
                node = node.right
                return node
            } else if (node.right === null) {
                node = node.left
                return node
            }

            // 移除有兩個節點(既有左節點又有右節點)
            if (node.left && node.right) {
                // 1. 找到被移除節點的右節點下的最小節點,替換被移除的節點
                let minRightNode = this.findMinNode(node.right)
                // 2. 把被移除節點的key設置為 被移除節點的右節點下的最小節點的key
                node.key = minRightNode.key
                // 3. 移除找到的那個最小節點
                this.removeNode(node.right, node.key)
                // 4. 向被移除節點的父節點返回更新后節點的引用
                return node
            }
        }
    }
}

測試如下:

let tree = new BinarySearchTree()
tree.insert(11)
tree.insert(7)
tree.insert(15)
tree.insert(5)
tree.insert(6)
tree.insert(3)
tree.insert(9)
tree.insert(8)
tree.insert(10)
tree.insert(13)
tree.insert(20)
tree.insert(12)
tree.insert(14)
tree.insert(18)
tree.insert(25)

tree.inOrderTraverse(printNode)   // 3  5 6 7 8  9 10 11 12 13 14 15 18 20 25
tree.prevOrderTraverse(printNode) // 11 7 5 3 6  9 8  10 15 13 12 14 20 18 25
tree.postOrderTraverse(printNode) // 3  6 5 8 10 9 7  12 14 13 18 25 20 15 11

// tree.key為根節點,為了保持樹不同層的結構一致,沒有使用root為屬性,使用了key
let minNodeVal = tree.findMin(tree.key)
console.log("minNodeVal", minNodeVal)

let maxNodeVal = tree.findMax(tree.key)
console.log("maxNodeVal", maxNodeVal)

let isHasNodeVal = tree.search(tree.key, 7)
console.log(isHasNodeVal)   // true

tree.removeNode(tree.key, 15)
console.log(tree) // 可以查看樹的結構,15的這個節點的key已經被替換為18,并且key為18的節點已經被刪除
樹的遍歷

1. 中序遍歷

2. 先序遍歷

3. 后序遍歷

移除節點的過程

1. 移除以一個葉節點

2. 移除只有一個左側子節點或右側子節點的節點

3. 移除有兩個子節點的節點

JavaScript 算法 排序算法 1.冒泡排序

冒泡排序的執行過程

代碼如下:

let arr = [5, 4, 3, 2, 1]

// 交換元素的位置
function swap(arr, index1, index2) {
    var temp = arr[index1]
    arr[index1] = arr[index2]
    arr[index2] = temp
}

function bubbleSort(arr) {
    for (let i = 0; i < arr.length - 1; i++) {
        for (let j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1)
            }
        }
    }
}

bubbleSort(arr)
console.log(arr)  // [1, 2, 3, 4, 5]
2.選擇排序

選擇排序的執行過程

代碼如下:

let arr = [5, 4, 3, 2, 1]

function swap(arr, index1, index2) {
    var temp = arr[index1]
    arr[index1] = arr[index2]
    arr[index2] = temp
}

function changeSort(arr) {
    for (let i = 0; i < arr.length - 1; i++) {
        let minIndex = i
        for (let j = i; j < arr.length; j++) {
            if (arr[minIndex] > arr[j]) {
                minIndex = j
            }
        }

        if (i !== minIndex) {
            swap(arr, i, minIndex)
        }
    }
}

changeSort(arr)
console.log(arr)  // [1, 2, 3, 4, 5]
3.插入排序

插入排序的執行過程

代碼如下:

let arr = [5, 4, 3, 2, 1]

function swap(arr, index1, index2) {
    var temp = arr[index1]
    arr[index1] = arr[index2]
    arr[index2] = temp
}

function insertSort(arr) {
    for (let i = 1; i < arr.length; i++) {
        let j = i
        let temp = arr[i]
        while (j > 0 && arr[j - 1] > temp) {
            arr[j] = arr[j - 1]
            j--
        }
        arr[j] = temp
    }
}

insertSort(arr)
console.log(arr)  // [1, 2, 3, 4, 5]

由于功力有限,本文有錯誤和不合理的地方,歡迎各位大神多多指正,非常感謝。

參考文章:

學習JavaScript數據結構與算法
數據結構與算法JavaScript描述

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109271.html

相關文章

  • JavaScript如何工作:內存管理+如何處理4個常見內存泄漏

    摘要:本系列的第一篇文章簡單介紹了引擎運行時間和堆棧的調用。編譯器將插入與操作系統交互的代碼,并申請存儲變量所需的堆棧字節數。當函數調用其他函數時,每個函數在調用堆棧時獲得自己的塊。因此,它不能為堆棧上的變量分配空間。 本系列的第一篇文章簡單介紹了引擎、運行時間和堆棧的調用。第二篇文章研究了谷歌V8 JavaScript引擎的內部機制,并介紹了一些編寫JavaScript代碼的技巧。 在這第...

    anRui 評論0 收藏0
  • 前端開發周報: CSS 布局方式JavaScript數據結構算法

    摘要:如果沒有學習過計算機科學的程序員,當我們在處理一些問題時,比較熟悉的數據結構就是數組,數組無疑是一個很好的選擇。 showImg(https://segmentfault.com/img/bVTSjt?w=400&h=300); 1、常見 CSS 布局方式詳見: 一些常見的 CSS 布局方式梳理,涉及 Flex 布局、Grid 布局、圣杯布局、雙飛翼布局等。http://cherryb...

    huhud 評論0 收藏0
  • 前端開發周報: CSS 布局方式JavaScript數據結構算法

    摘要:如果沒有學習過計算機科學的程序員,當我們在處理一些問題時,比較熟悉的數據結構就是數組,數組無疑是一個很好的選擇。 showImg(https://segmentfault.com/img/bVTSjt?w=400&h=300); 1、常見 CSS 布局方式詳見: 一些常見的 CSS 布局方式梳理,涉及 Flex 布局、Grid 布局、圣杯布局、雙飛翼布局等。http://cherryb...

    ?xiaoxiao, 評論0 收藏0
  • 【譯】JavaScript是如何工作:內存管理 + 如何處理4個常見內存泄露

    摘要:本文作為第三篇,將會討論另一個開發者容易忽視的重要主題內存管理。我們也會提供一些關于如何處理內存泄露的技巧。這是當前整型和雙精度的大小。然而,這是一組可以收集的內存空間的近似值。 本文轉載自:眾成翻譯譯者:Leslie Wang審校: 為之漫筆鏈接:http://www.zcfy.cc/article/4211原文:https://blog.sessionstack.com/how-j...

    IntMain 評論0 收藏0
  • JavasScript重難點知識

    摘要:忍者級別的函數操作對于什么是匿名函數,這里就不做過多介紹了。我們需要知道的是,對于而言,匿名函數是一個很重要且具有邏輯性的特性。通常,匿名函數的使用情況是創建一個供以后使用的函數。 JS 中的遞歸 遞歸, 遞歸基礎, 斐波那契數列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執行機制 本文的目的就是要保證你徹底弄懂javascript的執行機制,如果...

    forsigner 評論0 收藏0

發表評論

0條評論

alin

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<