基於javascript的數據結構實現(一)

數據結構

  • 數據結構是做爲程序員必須的瞭解的必備基礎知識,也是必備素養之一,接下來爲大介紹比較常見幾種的簡單基礎的數據結構;
堆棧
  • 堆棧:這種數據結構,你必須要它的特點就是:後進先出,就好像喫進去的,吐出來一樣,這樣比喻不文雅,但很形象
  • 接下來,我用js爲大家實現一下,利用數組實現,便於理解,時間複雜度爲o(1), 空間複雜度爲o(n);
function Stack() {
  var items = [];
  // 進棧
  this.push = function(value) {
    items.push(value);
  };

  // 出棧
  this.pop = function() {
    return items.pop();
  };

  // 返回棧頂的元素
  this.peek = function() {
    return items[items.length - 1];
  };

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

  this.size = function() {
    return items.length;
  };
  this.clear = function() {
    items = [];
  };
  this.print = function() {
    console.log(items.toString());
  };
}
  • 上面代碼我爲大家實現了從出棧/進棧/棧長及棧頂元素,用代碼爲大家實現了遍,大家有空也來擼一擼代碼把。
隊列
  • 隊列:先進先出,接下來也是利用數組實現;
  • 時間複雜度爲o(1), 空間複雜度爲o(n)
    function Queue() {
    var items = [];
    
    // 進隊列
    this.enQueue = function(value) {
        items.push(value);
    };
    
    // 出隊列
    this.outQueue = function() {
        return items.shift();
    };
    
    this.front = function() {
        return items[0];
    };
    
    this.isEmpty = function() {
        return items.length === 0;
    };
    this.clear = function() {
        items = [];
    };
    this.size = function() {
        return items.length;
    };
    this.print = function() {
        console.log(items);
    };
    }
    
  • 接下來爲大家介紹一下優先隊列,那就排隊時,有個優先權,可以插隊進去。
  • 優先隊列
  • 空間複雜度爲o(n), 時間複雜度o(n)
    function PriorityQueue() {
    var items = [];
    function QueueElement(value, priority) {
        this.value = value;
        this.priority = priority;
    }
    this.enQueue = function(value, priority) {
        var queueElement = new QueueElement(value, priority);
        if (this.isEmpty()) {
            items.push(queueElement);
        } else {
        var isAdd = false;
        for (var i = 0; i < this.size(); i++) {
            if (items[i].priority > queueElement.priority) {
            items.splice(i, 0, queueElement);
            isAdd = true;
            break;
            }
        }
        if (!isAdd) {
            items.push(queueElement);
        }
        }
    };
    this.outQueue = function() {
        return items.shift();
    };

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

    this.isEmpty = function() {
        return items.length === 0;
    };
    this.clear = function() {
        items = [];
    };
    this.size = function() {
        return items.length;
    };
    this.print = function() {
        console.log(items);
    };
    }
  • 對於上面兩種數據結構理解好了,對於前端很多原理,也會豁然開朗,像瀏覽器原理中事件循環機制還有垃圾回收中棧內存和堆內存回收機制,你也會很快明白一二的,還有棧內存中esp指針的原理等等
鏈表
  • 特點:內存不連續性,這種也是對比於數組具有連續的內存而言的。
  • 正是因爲這種特性,鏈表的表長,需要去求解的,鏈表也就有當前節點和下一個指針next,next指針指向下一個節點,下一個又指向下下一個節點,直到最後一節點的next爲null
  • 上面描述的正是單向鏈表,除此之外還有雙向鏈表和循環鏈表;雙向鏈表就是必單向鏈表多了一個pre針意味着鏈表有兩條鏈表分別由next和pre鏈接而成的
  • 循環鏈表,就是將尾節點的next指針指向頭節點head,這樣就形成一個大環,稱之爲循環鏈表
  • 代碼實現,以單向鏈表爲例:
function CreateList() {
  this.headNode = null;
  this.nodeLength = 0;
  // 單向鏈表實現
  this.createNode = function (value) {
    this.data = value;
    this.nextNode = null;
  };
}
CreateList.prototype.ListLength = function () {
  let headNode = this.headNode;
  let count = 0;
  if (headNode) {
    count = count + 1;
  }
  while (headNode.nextNode) {
    count++;
    headNode = headNode.nextNode;
  }
  return count;
};
// 添加節點
CreateList.prototype.appendNode = function (value) {
  // console.log(this);
  const node = new this.createNode(value);
  let current = null;
  if (!this.headNode) {
    this.headNode = node;
  } else {
    current = this.headNode;
    while (current.nextNode) {
      current = current.nextNode;
    }
    current.nextNode = node;
  }
  this.nodeLength++;
};
// 插入節點
CreateList.prototype.insertNode = function (value, location) {
  if (location >= 0 && location <= this.nodeLength) {
    let node = new this.createNode(value);
    let current = this.headNode;
    let count = 0;
    let pre;
    if (location == 0) {
      node.nextNode = current;
      this.headNode = node;
    } else {
      while (count < location) {
        pre = current;
        current = current.nextNode;
        count++;
      }
      node.nextNode = current;
      pre.nextNode = node;
    }
    this.nodeLength++;
    return true;
  } else {
    return false;
  }
};
// 刪除位置節點
CreateList.prototype.deleteLocationNode = function (location) {
  if (location >= 0 && location < this.nodeLength) {
    let current = this.headNode;
    let count = 0;
    let pre, next;
    if (location === 0) {
      current = this.headNode.nextNode;
      this.headNode = current;
    } else {
      while (count < location) {
        pre = current;
        current = current.nextNode;
        next = current.nextNode;
        count++;
      }
      pre.nextNode = next;
    }
    this.nodeLength--;
    return true;
  } else {
    console.warn('超出範圍');
    return false;
  }
};
// 查詢節點是否存在
CreateList.prototype.isNode = function (element) {
  let current = this.headNode;
  let indexFlag = false;
  if (current.data === element) {
    return true;
  }
  while (current.nextNode) {
    current = current.nextNode;
    if (current.data === element) {
      indexFlag = true;
      break;
    }
  }
  return indexFlag;
};
// 查詢節點元素位置
CreateList.prototype.indexOf = function (element) {
  const isElement = this.isNode(element);
  if (isElement) {
    let count = 0;
    let current = this.headNode;
    if (current.data === element) {
      return count;
    }
    while (current.nextNode) {
      count++;
      current = current.nextNode;
      if (current.data === element) {
        break;
      }
    }
    return count;
  } else {
    return -1;
  }
};
// 刪除元素節點
CreateList.prototype.deleteElementNode = function (element) {
  // 判斷元素值是否存在
  const isElement = this.isNode(element);
  if (isElement) {
    let current = this.headNode;
    let pre;
    let count = 0;
    if (current.data === element) {
      this.headNode = current.nextNode;
    }
    pre = current;
    current = current.nextNode;
    while (current) {
      if (current.data === element) {
        pre.nextNode = current.nextNode;
        current = current.nextNode;
        this.nodeLength--;
        count++;
      } else {
        pre = current;
        current = current.nextNode;
      }
    }
    return count;
  } else {
    console.warn('元素節點不存在');
    return false;
  }
};
// 轉換成字符串
CreateList.prototype.toString = function () {
  let data = this.headNode.data;
  let current = this.headNode.nextNode;
  while (current) {
    data += `,${current.data}`;
    current = current.nextNode;
  }
  return data;
};
  • 上面鏈表實現了鏈表的添加節點,插入節點,查詢節點,刪除節點等,對這種數據結構熟悉之後,也讓你對react和vue的原理能有更加清晰的認識。
  • 樹呢,二叉樹,平衡二叉樹,二叉搜索樹,堆,原地建堆,及紅黑樹,本文以二叉搜索樹爲例,來進行代碼實現
  • 二叉搜索樹的特性:根節點分爲左右兩個子樹,左子樹比父節點小,右子樹比父節點大;總結爲,左小右大。
  • 分片實現:樹結構:左右子樹建好
function BinarySearchTree() {
  var Node = function(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  };
  var root = null;
}
  • 爲樹加葉子節點:
  var insertNode = function(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);
      }
    }
  };
  this.insert = function(key) {
    var node = new Node(key);
    if (root === null) {
      root = node;
    } else {
      insertNode(root, node);
    }
  };
  • 構建二叉搜素樹的結構
    var tree = new BinarySearchTree();
    tree.insert(11);
    tree.insert(7);
    tree.insert(15);
    tree.insert(5);
    tree.insert(3);
    tree.insert(9);
    tree.insert(8);
    tree.insert(10);
    tree.insert(13);
    tree.insert(12);
    tree.insert(14);
    tree.insert(20);
    tree.insert(18);
  • 實現樹的中序,先序及後序遍歷
var inOrderTraverseNode = function(node, callback) {
    // 中序遍歷
    if (node !== null) {
      inOrderTraverseNode(node.left, callback);
      callback(node, callback);
      inOrderTraverseNode(node.right, callback);
    }
  };
  this.inOrderTraverse = function(callback) {
    // 中序遍歷
    inOrderTraverseNode(root, callback);
  };

  // 先序遍歷
  var preOrderTraverseNode = function(node, callback) {
    if (node !== null) {
      callback(node, callback);
      preOrderTraverseNode(node.left, callback);
      preOrderTraverseNode(node.right, callback);
    }
  };
  this.preOrderTraverse = function(callback) {
    // 先序遍歷
    preOrderTraverseNode(root, callback);
  };

  //後序遍歷
  var postOrderTraverseNode = function(node, callback) {
    if (node !== null) {
      postOrderTraverseNode(node.left, callback);
      postOrderTraverseNode(node.right, callback);
      callback(node, callback);
    }
  };
  //後序遍歷
  this.postOrderTraverse = function(callback) {
    postOrderTraverseNode(root, callback);
  };
  • 總結:二叉搜索樹就建立成功啦,樹這種數據結構,也是使用相對比較頻繁的一種數據結構,特別應用於底層框架的開發。

集合

  • 沒有重複元素,沒有順序概念的數數據結構
  • 接下來爲大家簡單實現集合方法:集合的增刪改查;

function Set() {
  var items = {};

  // 判斷是否是集合的值
  this.has = function(value) {
    return value in items;
  };

  // 添加元素
  this.add = function(value) {
    if (!this.has(value)) {
      items[value] = value;
      return true;
    }
    return false;
  };

  // 移除元素
  this.remove = function(value) {
    if (this.has(value)) {
      delete items[value];
      return true;
    }
    return false;
  };
  this.clear = function() {
    items = {};
  };
  this.size = function() {
    return Object.keys(items).length;
  };
  this.values = function() {
    return Object.keys(items);
  };

  
}
  • 交集:

  this.intersection = function(otherSet) {
    var intersectionSet = new Set();
    var values = this.values();
    for (var i = 0; i < values.length; i++) {
      if (otherSet.has(values[i])) {
        intersectionSet.add(values[i]);
      }
    }
    return intersectionSet;
  };
  • 差集
 this.difference = function(otherSet) {
    var differenceSet = new Set();
    values = this.values();
    for (var i = 0; i < values.length; i++) {
      if (!otherSet.has(values[i])) {
        differenceSet.add(values[i]);
      }
    }
    return differenceSet;
  };
  • 並集
this.union = function(otherSet) {
    var unionSet = new Set();
    var values = this.values();
    for (var i = 0; i < values.length; i++) {
      unionSet.add(values[i]);
    }
    values = otherSet.values();
    for (var i = 0; i < values.length; i++) {
      unionSet.add(values[i]);
    }
    return unionSet;
  };
  • 子集
this.subSet = function(otherSet) {
    if (this.size() > otherSet.size()) {
      return false;
    } else {
      var value = this.values();
      for (var i = 0; i < values.length; i++) {
        if (!otherSet.has(values[i])) {
          return false;
        }
      }
      return true;
    }
  };
字典
function Dictionary() {
  var items = {};
  this.has = function(value) {
    return value in items;
  };
  this.set = function(key, value) {
    items[key] = value;
  };
  this.remove = function(key) {
    if (this.has(key)) {
      delete items[key];
    }
  };
  this.get = function(key) {
    return this.has(key) ? items[key] : undefined;
  };
  this.values = function() {
    var values = [];
    for (var i in items) {
      if (this.has(i)) {
        values.push(items[i]);
      }
    }
    return values;
  };
  this.getItems = function() {
    return items;
  };
  this.clear = function() {
    items = {};
  };
  this.size = function() {
    return Object.keys(items).length;
  };
  this.keys = function() {
    return Object.keys(items);
  };
}
  • 總結: 這上面,本人實現了棧,隊列,樹,優先隊列,集合及字典這幾種簡單的數據結構實現,接下來一篇將會實現堆,原地建堆,散列表及圖的數據結構的實現
參考文檔
  • 基於javascript的數據結構與算法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章