数据结构算法 二进制转十进制_数据结构 - 栈
兩種類似數組的數據結構,在添加和刪除元素時更為可控,他們就是棧和隊列
棧是一種遵從后進先出(LIFO)原則的有序集合。新添加或待刪除的元素都保存在棧的同一端,稱作棧頂,另一端就叫棧底。在棧里,新元素都靠近棧頂,舊元素都接近棧底。
被用在編程語言的編譯器和內存中保存變量、方法調用等,也被用于瀏覽器歷史記錄(瀏覽器的返回按鈕)。
創建一個基于數組的棧
創建一個類來表示棧,利用數組來保存棧里的元素
class Stack {constructor() {this.items = []} }數組允許我們在任何位置添加或刪除元素,由于棧遵循 LIFO 原則,所以需要對元素的添加和刪除做一些限制,接下來為棧聲明一些方法
- push() : 添加新元素到棧頂
- pop() : 移除棧頂的 元素,同時返回被移除的元素
- peek() : 返回棧頂的元素,不對棧做任何修改
- isEmpty() : 如果棧里沒有任何元素返回 true, 否則返回 false
- clear() : 移除棧里所有的元素
- size() : 返回棧里的元素個數
向棧添加元素,首先實現 push() ,向棧里添加新元素,該方法只添加元素到棧頂,可以這樣寫
push(element) {this.items.push(element) }從棧移除元素,實現 pop() 方法,移除棧里的元素,棧遵循 LIFO 原則,移除的是最后添加進去的元素
pop() {return this.items.pop() }限制為 push 和 pop 方法添加和刪除棧中元素,這樣棧就自然遵循了 LIFO 原則
查看棧頂元素,想知道棧里最后添加的元素是什么,可以用 peek 方法,該方法將返回棧頂的元素
peek() {return this.items[this.items.length - 1] }檢查棧是否為空,實現 isEmpty,如果棧為空的話將返回 true,否則就返回 false
isEmpty() {return this.items.length === 0 }實現棧的長度
size() {return this.items.length }清空棧元素,實現 clear 方法,移除棧里所有的元素
clear() {this.items = [] }以上實現了一個棧
使用 Stack 類
在深入了解棧的應用前,先來了解如何使用 Stack 類。首先需要初始化 Stack 類,然后驗證一下棧是否為空(輸出是 true,因為還沒有往棧里添加元素)
const stack = new Stack() console.log(stack.isEmpty()) //true接下來,往棧里添加一些元素
stack.push(5) stack.push(8)調用 peek 方法(),返回棧頂的元素
console.log(stack.peek()) //8再添加一個元素
stack.push(11) console.log(stack.size()) //3 console.log(stack.isEmpty()) //false繼續添加元素
stack.push(15)下圖描繪了我們對棧的操作,以及棧的當前狀態
調用兩次 pop 方法從棧里移除兩個元素
stack.pop() stack.pop() console.log(stack.size()) //2在兩次調用 pop 方法前,我們的棧里有四個元素。調用兩次后,現在棧里僅剩下 5 和 8 了,下圖描繪了這個執行過程
創建一個基于對象的 Stack 類
使用數組來存儲元素,在處理大量數據時,需評估如何操作數據是最高效的,使用數組時,大部分方法的時間復雜度是 O(n) ,我們需要迭代整個數組直到找到要找的那個元素,在最壞的情況下需要迭代數組的所有位置,其中的 n 代表數組的長度。如果數組有更多元素的話,所需的時間會更長。另外,數組是元素的一個有序集合,為了保證元素排列有序,它會占用更多的內存空間。
如果我們能直接獲取元素,占用較少的內存空間,并且仍然保證所有元素按照我們的需要排列,那不是更好嗎?對于使用 JavaScript 語言實現棧數據結構的場景,我們也可以使用一個JavaScript 對象來存儲所有的棧元素,保證它們的順序并且遵循 LIFO 原則。我們來看看如何實現這樣的行為。
首先聲明一個 stack 類
class Stack {constructor() {this.count = 0 //記錄棧的大小this.items = {}} }向棧中插入元素,因為使用的是對象, 所以 push 方法只允許我們一次插入一個元素
push(element) {this.items[this.count] = elementthis.count++ }對象是鍵值對的集合,所以要向棧中添加元素,可以使用 count 變量作為 items 對象的鍵名,插入的元素則是它的值。在向棧插入元素后,我們遞增 count 變量。
使用 Stack 類,插入元素 5,8
const stack = new Stack() stack.push(5) stack.push(8)查看 stack
驗證一個棧是否為空, count 屬性也表示棧的大小,因此,我們可以簡單地返回 count 屬性的值來實現 size 方法
size() {return this.count }驗證棧是否為空
isEmpty() {return this.count === 0 }從棧中彈出元素,對象中沒有直接用的 api ,所以手動實現
pop() {if (this.isEmpty()) {return undefined}this.count--const result = this.items[this.count]delete this.items[this.count]return result }首先,我們需要檢驗棧是否為空。如果為空,就返回 undefined。如果棧不為空的話,我們會將 count 屬性減 1,并保存棧頂的值,以便在刪除它之后將它返回。
stack.pop() //8模擬 pop 操作, 要訪問到棧頂的元素(即最后添加的元素 8),我們需要訪問鍵值為 1 的位置。因此我們將 count 變量從 2 減為 1。這樣就可以訪問 items[1],刪除它,并將它的值返回了。
查看棧頂的元素
peek() {if (this.isEmpty()) {return undefined}return this.items[this.count -1] }清空該棧,只需要將它的值復原為構造函數中使用的值即可
clear() {this.items = {}this.count = 0 }創建 toString 方法
在數組版本中,我們不需要關心 toString 方法的實現,因為數據結構可以直接使用數組已經提供的 toString 方法。對于使用對象的版本,我們將創建一個 toString 方法來像數組一樣打印出棧的內容。
toString() {if (this.isEmpty()) {return ''}let objString = `${this.items[0]}`for (let i = 1; i < this.count; i++) {objString = `${objString}, ${this.items[i]}`}return objString }如果棧是空的,我們只需返回一個空字符串即可。如果它不是空的,就需要用它底部的第一個元素作為字符串的初始值,然后迭代整個棧的鍵,一直到棧頂,添加一個逗號以及下一個元素如果棧只包含一個元素,for循環不會執行
除了 toString 方法,我們創建的其他方法的復雜度均為 O(1),代表我們可以直接找到目標元素并對其進行操作(push、 pop 或 peek)。
保護數據結構內部元素
在創建別的開發者也可以使用的數據結構或對象時,我們希望保護內部的元素,只有我們暴露出的方法才能修改內部結構,對于 Stack 類來說,要確保元素只會被添加到棧頂,而不是棧底或其他任意位置(比如棧的中間)。
使用 WeakMap 實現類
WeakMap 可以存儲鍵值對,其中鍵是對象,值可以是任意數據類型,如果用 WeakMap 來存儲 items 屬性(數組版本), Stack 類就是這樣的:
const items = new WeakMap() //聲明一個 WeakMap 類型的變量 itemsclass Stack {constructor() {items.set(this, []) //以 this(Stack 類自己的引用)為鍵,把代表棧的數組存入 items。}push(element) {//從 WeakMap 中取出值,即以 this 為鍵(行{2}設置的)從 items 中取值。const s = items.get(this)s.push(element)}pop() {const s = items.get(this)const r = s.pop()return r} }items 在 Stack 類里是真正的私有屬性
ECMAScript 類屬性提案(易讀性更好)
class Stack {#count = 0#items = 0//方法 }用棧解決問題
如何解決十進制轉二進制問題,以及任意進制轉換的算法。
從十進制到二進制
該十進制數除以 2(二進制是滿二進一)并對商取整,直到結果是 0 為止。舉個例子,把十進制的數 10 轉化成二進制的數字,過程大概是如下這樣。
function decimalToBinary(decNumber) {const remStack = new Stack()let number = decNumberlet rem let binaryString = ''while (number > 0) {rem = Math.floor(number % 2) //js 不區分整數和浮點數,使用 Math.floor 返回整數部分,得到余數remStack.push(rem) //放入棧里number = Math.floor(number / 2) //繼續除以2,直到結果等于0時,才會停止 }while (!remStack.isEmpty()) {binaryString += remStack.pop().toString() //用 pop 方法把棧中的元素都移除,把出棧的元素連接成字符串}return binaryString }測試
console.log(decimalToBinary(233)) //11101001 console.log(decimalToBinary(10)) //1010 console.log(decimalToBinary(1000)) //1111101000進制轉換算法
修改上面的算法,使之能把十進制轉換成基數為 2~36 的任意進制。除了把十進制數除以 2 轉成二進制數,還可以傳入其他任意進制的基數為參數,就像下面的算法這樣。
function baseConverter(decNumber, base) {const remStack = new Stack()const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'let number = decNumberlet remlet baseString = ''if (!(base >= 2 && base <= 36)) {return ''}while (number > 0) {rem = Math.floor(number % base)remStack.push(rem)number = Math.floor(number / base)}while (!remStack.isEmpty()) {baseString += digits[remStack.pop()]}return baseString }console.log(baseConverter(100345, 2)); // 11000011111111001 console.log(baseConverter(100345, 8)); // 303771 console.log(baseConverter(100345, 16)); // 187F9 console.log(baseConverter(100345, 35)); // 2BW0我們只需要改變一個地方。在將十進制轉成二進制時,余數是 0 或 1;在將十進制轉成八進制時,余數是 0~7;但是將十進制轉成十六進制時,余數是 0~9 加上 A、 B、 C、 D、 E 和 F(對應 10、 11、 12、 13、 14 和 15)。因此,我們需要對棧中的數字做個轉化才可以(行{6}和行{7})。因此,從十一進制開始,字母表中的每個字母將表示相應的基數。字母 A 代表基數 11, B 代表基數 12,以此類推。
總結
以上是生活随笔為你收集整理的数据结构算法 二进制转十进制_数据结构 - 栈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 僵尸进程的清除
- 下一篇: tensor flow lstm 图像