前端基礎之探索數據結構與算法系列(一)

1、前端爲何要學算法和數據結構?

  • 前言

其實,在前端,瞭解一些經常使用的Jvascript Array和Object並可以靈活運用就足以解決常見的初級和中級的算法問題,若是不夠用還有不少的ECMAscript的API能夠依賴。一個排序功能,用冒泡排序、快速排序仍是並歸排序?who care?一個原生的sort函數就直接搞定。何況前端的代碼運行環境原本就不善於處理大量的數據計算。並且前端也有不少的東西比算法重要(好比安全性、用戶體驗、模塊化和可擴展性等),在大多數狀況下,前端並不在意你寫的程序有多快,更加在意你能不能很好的實現功能。那咱們爲何要學習算法與數據結構呢?javascript


  1. 數據結構與算法是基礎

前端工程師首先得是工程師,既然是計算機的工程師那麼掌握必定的數據結構和算法並可以理解和靈活運用它們是一個最起碼的要求。只有這樣才能在日益複雜的前端領域站穩腳跟,越走越遠。前端

  1. 有助於實現新的功能

當一個全新的功能擺在你面前又找不到現有的函數或api去實現的時候,良好的數據結構和算法功底就顯的尤其重要。java

  1. 鍛鍊能力

當你在學習數據結構和算法的時候,你的不少能力也會跟着提高。化繁爲簡,直達問題本質的能力、分析判斷的能力、觸類旁通的能力 。面試

  1. 有利於面試和晉升

前端的面試離不開算法與數據結構,有用良好的該方面的知識儲備量可以大大加分。算法


2、數據結構

數據結構:數據結構是數據元素相互之間存在的一種和多種特定的關係集合 包括二個部分組成邏輯結構,存儲結構。
邏輯結構:簡單的來講 邏輯結構就是數據之間的關係,邏輯結構大概統一的能夠分紅兩種 一種是線性結構,非線性結構 。
線性結構:是一個有序數據元素的集合。 其中數據元素之間的關係是一對一的關係,即除了第一個和最後一個數據元素以外,其它數據元素都是首尾相接的。經常使用的線性結構有: 列表,棧,隊列,鏈表,線性表,集合。
非線性結構:各個數據元素再也不保持在一個線性序列中,每一個數據元素可能與零個或者多個其餘數據元素髮生聯繫。常見的線性結構有 二維數組,多維數組,廣義表,樹(二叉樹等),圖(網)等。
存儲結構:邏輯結構指的是數據間的關係,而存儲結構是邏輯結構用計算機語言的實現。 常見的存儲結構有順序存儲、鏈式存儲、索引存儲以及散列存儲(哈希表)。
大體結構以下:api

clipboard.png

3、線性結構--棧

  1. Javascript中的棧的實現

棧,是一種後入先出(LIFO)的線性結構。棧的頭部,稱爲棧底;棧的尾部,稱爲棧頂。元素,從棧頂壓入;從棧頂彈出。
如何實現一個棧:(1)肯定存儲結構:是離散的,仍是連續的。或者說,是鏈表,仍是連續表。在Javasript中能夠選用數組,做爲存儲結構。
(2)肯定操做方法:入棧方法(push),出棧方法(pop),預覽棧頂方法(peek),清棧方法(clear),獲取棧高方法(length)。具體實現以下:數組

function Stack() {
  this.dataStore = [];
  this.top = 0;

  this.push = function ( element ) {
    this.dataStore[this.top++] = element;
  };

  this.pop = function () {
    return this.dataStore.splice(--this.top,1)[0];
  };

  this.peek = function () {
    return this.dataStore[this.top-1];
  };

  this.length = function () {
    return this.top;
  };

  this.clear = function () {
    delete this.dataStore;
    this.dataStore = [];
    this.top = 0;
  }
};
  1. 棧的應用

(1)進制轉化:將十進制整數轉化爲任意正整數進制。
原理:取餘法。
實現以下:安全

function newBase( num, base ) {
  var stack = new Stack();

  // 除n取餘
  do {
    stack.push(num % base);
    num = Math.floor(num/base);
  } while (num > 0);
  
  // 倒寫餘數
  var str = '';
  for (var i = 0, length = stack.length(); i < length; i++) {
    str += stack.pop();
  };

  return str;
};

(2)階乘的迭代實現
原理:將1到n依次壓入棧中,再將棧頂彈出的數,一一相乘。
實現以下:前端工程師

function newFactorial( n ) {
  var stack = new Stack();
  while (n > 1) {
    stack.push(n--)
  };

  var result = 1;
  while (stack.length() > 0) {
    result *= stack.pop();
  };
  return result;
}

(3)判斷單詞是否迴文
原理:倒寫單詞。將原單詞從首字母到尾字母,依次壓入棧中,再依次彈出,寫入字符串,完成倒寫,生成新單詞。判斷倒寫以後的新單詞和原單詞是否相等。
實現以下:數據結構

function isPalindrome( words ) {
  var stack = new Stack();

  for (var i = 0; i < words.length; i++) {
    stack.push(words[i]);
  };

  var rwords = '';
  for (var i = 0, length = stack.length(); i < length; i++) {
    rwords += s.pop();
  };

  if ( words === rwords ) {
    return true;
  } else {
    return false;
  };
};

(4)中綴表達式轉化爲後綴表達式(逆波蘭表達式)
原理:自左向右掃描中綴表達式,若是當前是運算數,則壓入棧中;若是當前是運算符(左括號,加減乘除),則壓入棧;若是當前是運算符(右括號),則從棧中彈出,輸出到後綴表達式中。直到棧頂不是(加減乘除)。再將左括號彈出。重複第一步和第二步,直到算術表達式掃描完畢。
實現以下:

function reversePolishExpression( arithmetic ) {
  var operater = new Stack();
  var reverseExp = '';

  for (var i = 0; i < arithmetic.length; i++) {
    if ( '0123456789'.indexOf(arithmetic[i]) !== -1 ) {
      reverseExp += arithmetic[i];
    } else if ( '(+-*/'.indexOf(arithmetic[i]) !== -1 ) {
      operater.push(arithmetic[i]);
    } else if ( arithmetic[i] === ')' ) {
      while ( '+-*/'.indexOf(operater.peek()) !== -1 ) {
        reverseExp += operater.pop();
      };
      operater.pop();
    };
  };
  return reverseExp;
};

4、線性結構--隊列

  1. Javascript中的隊列的實現

隊列是隻容許在一端進行插入操做,另外一個進行刪除操做的線性表,隊列是一種先進先出(FIFO)的數據結構

隊列在程序程序設計中用的很是的頻繁,由於javascript單線程,因此致使了任何一個時間段只能執行一個任務,並且還參雜了異步的機制。

隊列原本也是一種特殊的線性表,在JavaScript咱們能夠直接使用數組實現這樣的一個設計,數組的push()方法能夠在數組末尾加入元素,shift()方法則可刪除數組的第一個元素。
實現以下:

function Queue() {
    this.dataStore = [];
    this.enqueue   = enqueue;
    this.dequeue   = dequeue;
    this.first     = first;
    this.end       = end;
    this.toString  = toString;
    this.empty     = empty;
    
    this.enqueue = function(element) {  //向隊尾添加一個元素
        this.dataStore.push(element);
    }
    
    this.dequeue = function() {      //刪除隊首的元素
        return this.dataStore.shift();
    }
    
    this.getFirst = function() {  //讀取隊首元素
        return this.dataStore[0];
    }
        
    this.getEnd = function() {    //讀取隊尾元素
        return this.dataStore[this.dataStore.length - 1];
    }
    
    this.showAll = function() {   //顯示隊列中的全部元素
        var retStr = "";
        for (var i = 0; i < this.dataStore.length; ++i) {
            retStr += this.dataStore[i] + "\n";
        }
        return retStr;
    }
    
    this.isEmpty = function() {      //判斷隊列是否爲空
        if (this.dataStore.length == 0) {
            return true;
        } else {
            return false;
        }
    }
}
  1. 隊列的應用

(1)優先隊列(可實現javascript橋接模式)
原理:數據成員入隊的時候要攜帶數據和優先級,優先級在這裏的定義是數字越大在數組中的位置靠後。當插入一個數組元素的時候,先要根據它的優先級來判斷,最小是直接插入到第一位,緊鄰一個比他大的優先級是,就插入到這個鄰居的前面。若是是最大的數字就插到數組的最後。這樣,一維數組變成了二位數組。
實現以下:

function PriorityQueue() {

    let items = [];

    function QueueElement (element, priority){ 
            this.element = element;//成員自己
        this.priority = priority;  //優先級
    }

    this.enqueue = function(element, priority){
        let queueElement = new QueueElement(element, priority);

        let added = false; //添加標記
        for (let i=0; i<items.length; i++){
            if (queueElement.priority <            
 items[i].priority){  //找到優先級數字比他大的那個元素

                items.splice(i,0,queueElement);            
             // 插到該元素的前面
                added = true;
                break; 
            }
        }
        if (!added){ //若是是優先級數字是最大的,直接插入到數組的末尾
            items.push(queueElement); //{5}
        }
    };

    //其餘操做和普通隊列是同樣的

    this.dequeue = function(){
        return items.shift();
    };

    this.front = function(){
        return items[0];
    };

    this.isEmpty = function(){
        return items.length == 0;
    };

    this.size = function(){
        return items.length;
    };
    
    this.print = function(){
        for (let i=0; i<items.length; i++){
            console.log(`${items[i].element}  - ${items[i].priority}`);
        }
    };
}

let priorityQueue = new PriorityQueue();
priorityQueue.enqueue("zhao", 2);
priorityQueue.enqueue("wang", 1);
priorityQueue.enqueue("zhang", 1);
priorityQueue.enqueue("li", 2);
priorityQueue.enqueue("liu", 3);
priorityQueue.print();
//=>
//打印出的結果(按優先級排序)
wang  - 1  
zhang  - 1
zhao  - 2
li  - 2
liu  - 3

由淺入深,後續會有更多數據結構和算法的思考和整理。。

相關文章
相關標籤/搜索