演算法的定義:
一個有限指令集,每條指令的描述不依賴與語言。
接受一些輸入
產生輸出
一定在有限步驟後終止
演算法的通俗理解
Algorithm 這個單詞本意就是解決問題的辦法/步驟邏輯
資料結構的實現,離不開演算法
JS的陣列API掌握的已經足夠熟練了,所以不講了。
這裡只講一下js與其他語言有關的地方
首先常見語言的陣列不能存放不同的資料型別,因此封裝是通常存放在陣列中的是object型別
常見的語言的陣列的容量不會自動改變,因此需要擴容。擴容在c是極為效率低的一個操作
同樣低效率的操作還有在陣列中間進行插入和刪除的操作。因為要移動後面的或者前面的所有節點。
優點:
- 在有關於下標的時候,陣列方便的很。
陣列是可以在任何位置進行增加或者刪除的一種線性結構。
有時候必須對資料進行限制,對其任意性加以限制。
棧和佇列就是這種的受限的線性結構。
特性:後進先出(last in first out)
只允許在一端進行插入和刪除操作,這端叫棧頂。相對的其中另一端叫棧低
插入叫入棧,進棧或者壓棧。
刪除叫出棧或者退棧。
程式中什麼是使用棧實現的呢?
學了這麼久的程式設計,是否聽說過,函式呼叫棧呢?
我們知道函式之間和相互呼叫:A呼叫B,B中又呼叫C,C中又呼叫D.
那樣在執行的過程中,會先將A壓入棧,A沒有執行完,所有不會彈出 棧
在A執行的過程中呼叫了B,會將B壓入到棧,這個時候B在棧頂,A在 棧底
如果這個時候B可以執行完,那麼B會彈出棧,但是B有執行完嗎?沒有 它呼叫了C.
所以C會壓棧,並且在棧頂.而C呼叫了D,D會壓入到棧頂.
所以當前的棧順序是:棧頂A->B->C->D棧頂
D執行完,彈出棧.C/B/A依次彈出棧.
所以我們有函式呼叫棧的稱呼,就來自於它們內部的實現機制.(通過 棧來實現的)
例題
有六個元素6,5,4,3,2,1的順序進棧,問下列哪一個不是合法的出棧序列?(c)
A.543612 B.453216 C.346521 D.234156// 棧的封裝
function Stack() {
// 棧中的屬性
this.items = []
}
// 原型要放在外面,這樣不會重複執行。
// 實測放在外面新建五個物件平均0.05ms。放在外面要0.15ms
// 1.入棧操作
Stack.prototype.push = function (params) {
this.items.push(params)
}
// 2.取出棧頂 元素
Stack.prototype.pop = function () {
return this.items.pop()
}
// 3.檢視棧頂 元素
Stack.prototype.peek = function () {
return this.items[this.items.length - 1]
}
// 4.判斷棧是否為空
Stack.prototype.isEmpty = function () {
return this.items.length === 0
}
// 5.獲取棧中元素個數
Stack.prototype.size = function () {
return this.items.length
}
// 6.toString方法
Stack.prototype.toString = function () {
let str = this.items.reduce((pre, cur) => {
return pre + ‘ ‘ + cur
})
return str
}
console.time(‘global’)
// 棧的使用
let s = new Stack()
let s1 = new Stack()
let s2 = new Stack()
let s3 = new Stack()
console.timeEnd(‘global’)
// 棧的操作
// s.push(10)
// s.push(20)
// s.push(30)
// console.log(s); // Stack{ items:[10,20,30]}
// s.pop()
// console.log(s);
// console.log(s.peek());
// console.log(s);
// console.log(s.size());
// s.push(40)
// console.log(s.toString());
export default Stackimport Stack from ‘./1_棧的封裝.js’
// 100
// 計算:100/2餘數:0
// 計算:50/2餘數:0
// 計算:25/2餘數:1
// 計算:12/2餘數:0
// 計算:6/2餘數:0
// 計算:3/2餘數:1
// 計算:1/2餘數:1
// 從底下到上面:1100100
// 主要是運用隊棧的進去再拿出來
// 思路:
// 1.計算主要留下兩個有用的數,就是餘數和除法的結果。
// 2.結果儲存下來接著用
// 3.餘數直接放到棧裡
// 4.結束條件是當除法結果為0的時候,結束。
// 5.然後把陣列搗騰出來組合成字串就行了
function dec2bin(decNumber) {
let stack = new Stack()
while (decNumber > 0) {
stack.push(decNumber % 2)
decNumber = Math.floor(decNumber / 2)
}
let res = ‘’
console.log(stack.isEmpty());
while (!stack.isEmpty()) {
res += stack.pop()
}
console.log(res);
}
dec2bin(1)// 佇列本質還是陣列
// 這波用的class 效能和直接構造原型的方法基本一致。
class Queue {
constructor() {
this.items = []
}
// enqueue
emqueue(element) {
this.items.push(element)
}
// dequeue
dequeue() {
return this.items.shift()
}
// front
front() {
return this.items[this.items.length - 1]
}
// isEmpty
isEmpty() {
return this.items.length === 0
}
// size
size() {
return this.items.length
}
// toString
toString() {
let str = this.items.reduce((pre, cur) => {
return pre + ‘ ‘ + cur
})
return str
}
}
console.time(‘global’)
let queue = new Queue()
console.timeEnd(‘global’)
queue.emqueue(10)
queue.emqueue(20)
queue.emqueue(30)
queue.dequeue()
console.log(queue);
// 匯出,供2用
export default Queue// 擊鼓傳花是一個常見的面試演算法題.使用佇列可以非常方便的實現最終的結果.
// 原遊戲規則:
// 口 班級中玩一個遊戲,所有學生圍成一圈,從某位同學手裡開始向旁邊的同學傳一束花.
// 口 這個時候某個人(比如班長),在擊鼓,鼓聲停下的一顆,花落在誰手裡,誰就出來表演節目.
// 修改遊戲規則:
// 口 我們來修改一下這個遊戲規則.
// 口幾個朋友一起玩一個遊戲,圍成一圈,開始數數,數到某個數字的人自動淘汰
// 口最後剩下的這個人會獲得勝利,請問最後剩下的是原來在哪一個位置上的人?
// 封裝一個基於佇列的函式
// 口 引數:所有參與人的姓名,基於的數字
// 口結果:最終剩下的一人的姓名
// 解決思路
// 1.佇列裡不斷迴圈,佇列外加一個計數器。
// 2.出隊一個計數器+1,看計數器等不等於5,不過不等於,則證明不是第五個,所以不淘汰。
// 3.不淘汰的元素出隊,然後再加入到隊尾,計數器加1
// 4.淘汰的直接出隊,不加入到隊尾,計數器歸為1
// 5.這樣人越來越少,判斷結束的條件是佇列的元素是否等於一個
import Queue from ‘./1_封裝佇列.js’
let q1 = new Queue()
let arr = [‘小李’, ‘bill’]
for (let item of arr) {
q1.emqueue(item)
}
let i = 1
// console.log(typeof q1.size());
while (q1.size() > 1) {
console.log(111);
let tmp = q1.dequeue()
if (i != 5) {
q1.emqueue(tmp)
i++
} else if (i == 5) {
i = 1
}
}
console.log(q1);// 優先順序佇列的特點:
// 口 我們知道,普通的佇列插入一個元素,資料會被放在後端.並且需要前面所有的元素都處理完成後才會處理前面的資料.
// 口 但是優先順序佇列,在插入一個元素的時候會考慮該資料的優先順序.
// 口 和其他資料優先順序 進行比較.
// 口 比較完成後,可以得出這個元素在佇列中正確的位置
// 口 其他處理方式,和基本佇列的處理方式一樣.
// 二優先順序佇列主要考慮的問題:
// 口每個元素不再只是一個資料,而且包含資料的優先順序
// 口在新增方式中,根據優先順序放入正確的位置.
// 現實中的優先順序佇列
// 1. 機場登機 老人和小孩,或者商務艙的多
// 2. 醫院的急診室和普通病房
import Queue from ‘./1_封裝佇列.js’
// 優先順序佇列的封裝
// 優先順序佇列和普通佇列的區別僅在於 優先順序佇列的插入與普通的不同
// 所以可以僅僅通過定義一個插入佇列,然後剩下的直接通過extends繼承下來就可以了。
class PriorityQueue extends Queue {
constructor() {
// 要繼承的話必須執行super()方法,因為super是呼叫原來的父類,將方法和值賦值到this上。
// 和原先老的es5寫法是不一樣的,原來的es5是通過原型鏈來實現的.
super()
this.items = []
}
emqueue(element, priority) {
let queueElement = new QueueElement(element, priority)
if (this.items.length === 0) {
this.items.push(queueElement)
} else {
let added = false
this.items.every((item, index) => {
if (queueElement.priority < item.priority) {
this.items.splice(index, 0, queueElement)
added = true;
// 這裡用every 或者 some 都可以break。
// break的方法就是 every中用return false
// some中用return true
return false
}
})
if (!added) {
this.items.push(queueElement)
}
}
}
}
// es6 class 沒有內部類的概念,所以不推薦用function寫在裡面這種寫法
// 直接在外面寫就行,效能高。
// 內部類的寫法就是重複的去新建QueueElement,寫在裡面沒有任何意義。
class QueueElement {
constructor(element, priority) {
this.element = element
this.priority = priority
}
}
let pq = new PriorityQueue()
pq.emqueue(111, 1)
pq.emqueue(222, 2)
pq.emqueue(333, 0)
pq.dequeue()優先順序佇列
例題
實現
只能在前端進行刪除
只能在後盾進行插入
先進先出(fifo first in first out)
本作品採用《CC 協議》,轉載必須註明作者和本文連結