数据结构那些事(二)

318 阅读4分钟

上篇数据结构那些事(一)已经介绍了数组,列表和栈。这篇我们将介绍队列,链表,字典。

队列也是一种列表,遵循先进先出的规则。

队列的两种主要操作是:向队列中插入新元素和删除队列中的元素。入队操作在队尾插入新元素,出队在队头删除元素。 接下来,我们来设计一个队列Queue类:

function Queue(){
    this.dataStore = [];//存储
    this.enqueue = enqueue;//入队
    this.dequeue = dequeue;//出队
    this.front = front;//队首
    this.back = back;//队尾
    this.toString = toString;//显示队列元素
    this.empty = empty;//清除
}

实现上面的操作方法:

function enqueue(e){
    this.dataStore.push(e);
}
function dequeue(e){
    this.dataStore.shift(e)
}
function front(){
    return this.dataStore[0]
}
function back(){
    return this.dataStore[this.dataStore.length - 1]
}
function toString(){
    let retStr = "";
    for (let i = 0; i<this.dataStore.length; ++i){
        retStr += this.dataStore[i] + "\n";
    }
    return retStr;
}
function empty(){
    if(this.dataStore.length === 0){
        return true;
    }else{
        return false;
    }
}

上面我们实现了一个基础的队列。但现实生活中,可能会出现类似军人优先,重病患优先这样的场景,那样我们上面的队列就无法满足了。这时,我们需要一个优先队列。 从优先队列中删除元素时,需要考虑优先权的限制。我们来定义一下存储在队列中的元素:

function Person(name, code){
    this.name = name;
    this.code = code; //表示优先级,越小优先级越高
}

现在,需要重新定义dequeue()方法。

function dequeue(){
    let p = this.dataStore[0].code;
    for(let i =1 ; i < this.dataStore.length; ++i){
        if(this.dataStore[i].code < p){
            p = i
        }
    }
    return this.dataStore.splice(p, 1);
}

链表:有时候我们使用数组效率比较低的时候就可以考虑使用链表。当然,如果你的场景需要随机访问,数组仍然比链表更合适。

链表元素依靠相互之间的关系进行引用。遍历链表,就是跟着链表,从链表的首元素一直走到尾元素,链表的尾元素一般指向null。

下图演示了在eggs后插入cookies操作

下图演示了删除操作

从上面的几张图,我们能直观的感受到,链表做这些插入和删除操作比数组要便捷的多。因为数组执行插入删除操作时还需要对应的改变数组中其他元素的索引。而链表只需要改变对应节点的指向即可完成操作,两者不可同日而语。

明白了链表的好处,接下来我们来设计实现一个链表:

我们设计的链表包含两个类。Node类用来表示节点,LinkedList类提供插入,删除,显示元素等方法。

function Node(e){
    this.element = e;
    this.next = null;
}
function LList(){
    this.head = new Node("head");
    this.find = find; //遍历链表,查找给定数据
    this.findPrev = findPrev; //遍历链表,查找给定数据前面一个节点
    this.insert = insert; //插入一个节点
    this.remove = remove; //删除
    this.display = display; //显示
}

实现上面的方法:

function find(e){
    let currNode = this.head;
    while(currNode.element !== e){
        currNode = currNode.next;
    }
    return currNode;
}

function insert(newEl, e){
    let newNode = new Node(newEl);
    let current = this.find(e);
    newNode.next = current.next;
    current.next = newNode;
}

function display(){
    let currNode = this.head;
    while(currNode.next !== null){
        console.log(currNode.next.element);
        corrNode = currNode.next;
    }
}

function findPrev(e){
    let currNode = this.head;
    while(currNode.next !== null && currNode.next.element !== e){
        currNode = currNode.next;
    }
    return currNode;
}

function remove(e){
    let prev = this.findPrev(e);
    if(prev.next !== null){
        prev.next = prev.next.next;
    }
}

上面定义的基础链表存在一个问题:很难从后向前遍历

这时,我们可以考虑实现一个双向链表:

按照上图的理解:我们先要给Node类增加一个先前连接的属性

function Node(element) {
    this.element = element;
    this.next = null;
    this.previous = null;
}

我们还要修改对应的insert方法,因为他现在需要建立两个连接

function insert(newElement, item) {
    var newNode = new Node(newElement);
    var current = this.find(item);
    newNode.next = current.next;
    newNode.previous = current;
    current.next = newNode;
}

双向链表的remove()方法比单向链表的效率更高,因为不需要查找前驱节点了。

function remove(item) {
    var currNode = this.find(item);
    if (!(currNode.next == null)) {
        currNode.previous.next = currNode.next;
        currNode.next.previous = currNode.previous;
        currNode.next = null;
        currNode.previous = null;
    }
}

还有一种链表是循环链表:和单向链表类似,唯一的区别是,在创建循环列表时,让其头结点的next属性指向它本身。

创建循环列表,我们只需要改变一下LList类即可:

function LList() {
    this.head = new Node("head");
    this.head.next = this.head; //让头结点的next指向它自己
    this.find = find;
    this.insert = insert;
    this.display = display;
    this.findPrevious = findPrevious;
    this.remove = remove;
}

字典:字典是一种以键值对形式存储数据的数据结构。

JavaScript中的Object类就是以字典形式设计的。所以字典的实现就像对象一样,感觉实在没什么值得写的,这里给一个demo。

function Dictionary(){
    this.dataStore = new Array();
}

function add(key, value){
    this.dataStore[key] = value;
}

function find(key){
    return this.dataStore[key];
}

function remove(key){
    delete this.dataStore[key]
}

function show(){
    for( let key in Object.keys(this.dataStore)){
        console.log(key + this.dataStore[key])
    }
}