数据结构(一)

163 阅读4分钟

一、栈

先进后出,后进先出,栈满无法进栈,栈空无法再进行提取

4367b6ebdcc44ed793f4c9bccc212cac.<img src="png" alt="" width="50%" />

1、封装一个栈:
  ** class stack{
    constructor(){
        // 返回数组对象原型的函数
        this.item=[];  //我在这里设定栈的大小
        //这边设置栈的长度
        this.items =[3];
    }
    push(el){
        //向栈顶添加元素
        //因为栈的先进后出,而且它添加的方式都是从栈顶开始
         this.item.push(el);  
         
         //这边模仿一个栈满的情况
         if(this.items.length !=3){
             this.items.push(el)
         }else if(this.items.length =3){
             console.log("栈满,停止进栈",this.item);
         }
    }
    pop(){
        //pop删除最后一个元素
        // 相当于出栈
        return  this.item.pop();
    }
    size(){
        // 获取栈的元素个数
       return  this.item.length;
    }
    toString(){
        // 将元素转字符串类型
        return this.item.join('-');
    }
    isEmpty(){
        // 判断栈是否为空
        return this.item.length === 0;
    }
    find(){
        return this.item[this.item.length - 1];
    }
}    
        
   
     let zhan = new stack();
    zhan.push('crt'); //0
    zhan.push('boy'); //1
    zhan.push('18');  //2
    // zhan.push('man'); 因为我在上方设置了栈的长度,所以超过这个长度就无法存储
    console.log(zhan);
    console.log(zhan.pop(),zhan); //删除栈
    console.log(zhan.size()); // 2  0,1,2
    console.log(zhan.find()); // crt
    console.log(zhan.toString());**
2、栈的应用
//利用栈实现一个十进制转二进制的函数
// 十进制转二进 就是 用2除去十进制数,然后然后得到商和余数记录下来 然后新商再除以二 直到商为0
// 每次把求出来余 放入栈中,然后再把它提取出来进行计算
function getNumber(decimalNumber){
let foot = new stack();
// 创建一个实例对象
while(decimalNumber > 0){
    foot.push(decimalNumber % 2); //这边将余数存放在 栈中 
    decimalNumber = Math.floor(decimalNumber / 2);  // 这2条相当于就是执行了十进二的操作
    }
let result = '';
while(!foot.isEmpty()){  //这边判断它如果不是为空那就将它放在result这个容器中
result += foot.pop();
    }
    return result;
}
    console.log(getNumber(100)); //1100100
    console.log(getNumber(26)); // 11010

二、队列的应用

封装一个队列
class King{
    constructor() {
    this.items = [];
}
add(el) {
    this.items.push(el);
}
delet() {
    return this.items.shift();
    // shift 是删除头部 
    // pop 跟它相反 是删除尾部
}
front() {
    return this.items[0];
}
size() {
    return this.items.length;
}
}
let ququ = new King();
ququ.add("red");
ququ.add("blue");
ququ.add("green");
console.log(ququ.delet(),ququ); 
队列的应用
**队列应用击鼓传花
击鼓传花规则:
           围成一圈数数,比如规定数到5的人淘汰,
           那么淘汰的人后面的人从1开始数,然后数到5的人再淘汰,
           以此类推,直到剩下最后一个人,求该人的位置

思路:围成一圈的话,可以看成一个队列,从第一个人开始数,
      那么在数到`num`之前的人可以依次从队头删除,加入队尾(保持环形结构)。
      直到被数到的这个人到队头,然后把它删除就行了。**
    let queue = new King();
        //定义一个列队,然后将数据依次加入队列
    for(let i = 0; i < nameList.length; i++) {
        queue.add(nameList[i]);
    }
    console.log('初始队列元素:',queue.items);
    //从第一个人开始数
    while(queue.size() > 1) {
        for(let i = 0; i < num - 1; i++) {
            queue.add(queue.delet());
        }
        // 主要点:从列头部删除的元素,然后将删除的元素添加到列尾部
        queue.delet();
        console.log(`人数变化:`,queue.items);
    }
    //4.把剩下那个人的名字和位置找出来
    let leftBoy = queue.front();
    let index = nameList.indexOf(leftBoy);
    console.log(`剩下的哥们儿是${leftBoy},他的初始位置是:${index}`);
    return index;
    }
    passGame(['张三','李四','王五','赵六','田七','郭八'], 5);

3、封装优先队列
封装队列:
    1、这个是什么东西
        就是有人可以进行一个插队,封装的时候要带上一个标识符,所以标识的元素可以进行插队/往前靠,
        所以说明这需要两个参数。`element(元素)`和`priority(优先级标识)`
    
   2、封装逻辑:
       先声明一个内部元素类,用来存储(元素,优先级)两个属性
       封装优先队列,通过继承父类的属性和方法(使用super关键字去调用父类方法)
       重写插入方法
       

重写的逻辑:
(1)首先判断是否为空,空就直接队尾插入;
(2)不为空,就要遍历当前队列元素,插入的新值依次比较,如果新值优先级较低,那么就使用splice插入到当前 位置的前面(把其他的顶到后面),同时跳出循环。
(3)如果比较完之后新插入元素优先级最高,那么直接插入到队尾。

    class QueueElement {
    constructor(element, priority) {
        this.element = element;
        this.priority = priority;
    }
}

//封装优先级队列
class PriorityQueue extends King  {
    constructor() {
        super(); //super关键字调用父类的构造函数
    }
    //重写add方法
    add(element, priority) {
        //1.根据传入的元素,创建一个元素对象实例
        let queueElement = new QueueElement(element, priority);
        //2.1判断队列是否为空,如果为空那么直接push进去
        if (this.isEmpty()) {
            this.items.push(queueElement);
        } else {
            //2.2如果不为空,那么比较优先级(这里默认小的优先)
            let added = false;  //定义一个标识,判断是否已经插入
            for (let i = 0; i < this.items.length; i++) {
                //3.如果插入的元素优先级更小,那么就在当前位置插入(其他的被挤到后面)
                if (queueElement.priority < this.items[i].priority) {
                    this.items.splice(i, 0, queueElement);
                    //splice的用法:splice(位置,删除几个,替换的元素)
                    added = true; //插入完成,修改标识
                    break; //如果插入完成,就跳出循环
                }
            }
            //4.如果遍历结束后,新元素始终最大,那么就添加到队尾
            if(!added) {
                this.items.push(queueElement);
            }
        }
    }

    //其他方法全部继承(转字符串需要小改)
    delte() {
        return super.delte(); //super关键字调用父类的函数
    }

    front() {
        return super.front();
    }

    isEmpty() {
        return super.isEmpty();
    }

    size() {
        return super.size();
    }

    toString() {
        let result = "";
        for(let item of this.items) {
            result += `${item.element}-${item.priority} `;
        }
        return result;
    }
}

//测试代码
const queue = new PriorityQueue();
queue.add('zzy',10);  //参数1为元素,参数2为优先级
queue.add('nba',50);
queue.add('cba',20);
queue.add('dj',40);
queue.add('ht',30);
console.log(queue.items);
console.log(queue.toString()); //zzy-10 cba-20 ht-30 dj-40 nba-50