1-数据结构

23 阅读4分钟

学习内容

  • 栈(Stack)
  • 队列(Queue)
  • 链表(Stack)
  • 树(Tree)
  • 哈希表(Hash Table)
  • 图(Graph)

栈(Stack)

栈可以看成是一种受限的线性结构

相关概念

  • 栈顶:允许数据进出的唯一出口
  • 栈底:相对于栈顶的另一端
  • 进栈:把新元素放在栈顶
  • 出栈:把栈顶的元素取出

特点

  • 后进先出(LIFO):指的是最后进入的元素第一个弹出栈空间
  • 次序不变:栈中的各个数据之间的相对位置是不允许发生变化的(对顺序有严格的要求)

栈的实现

实现方式

  • 基于数组实现栈
  • 基于链表实现

栈的方法

  • push: 入栈
  • pop: 出栈
  • peek: 查找栈顶元素
  • isEmpty: 是否为空栈
  • size: 返回栈的大小(元素个数)
  • toString: 打印栈中元素

基于数组实现

class Stack<Element> {
    items: Element[] = [];

    // 入栈
    push(node: Element) {
        this.items.push(node);
    }

    // 出栈
    pop(): Element | null {
        return this.isEmpty() ? null : this.items.pop[this.size() - 1]
    }

    // 是否为空
    isEmpty(): boolean {
        return !!this.size();
    }

    // 栈的大小
    size(): number {
        return this.items.length;
    }

    // 查询栈顶元素
    peek(): Element | null {
        return this.isEmpty() ? null : this.items[this.size() - 1];
    }

    // toString 打印栈中元素
    toString(flag: string): string {
        return this.items.join(flag);
    }
} 

应用一: 进制转换

  • 十进制转n进制
function converter(num: number, bs: number) {
    let left = num;
    const stack = new Stack();
    let result: string = "";
    const digits = "0123456789ABCDEF";
    while(left > 0) {
        stack.push(left % bs);
        left = Math.floor(left / bs);
    }

    while (!stack.isEmpty()) {
        result += digits[stack.pop() as number]; //出栈
    }
    // 也可以用toString 进行反转
    return result;
}

应用二: 有效括号

  • 思路就是前一半入栈,后一半出栈,能出栈完的就是有效括号
  • { 和 } or [ 和 ] or ( 和 ) 这个算匹配上了
type back = null | string;
function isValid(s = ""): back {
  const stack = new Stack();
  for (let i = 0; i < s.length; i++) {
    // 当前字符
    const cur = s[i];
    // 栈顶字符
    const p = stack.peek();
    if (
      (cur == ")" && p == "(") ||
      (cur == "]" && p == "[") ||
      (cur == "}" && p == "{")
    ) {
      stack.pop();
    } else {
      stack.push(cur);
    }
  }

  return stack.isEmpty();
}

console.log(isValid('()'))    // true
console.log(isValid('()[]{}'))  // true
console.log(isValid('(]'))  // false
console.log(isValid('([)]'))  // false
console.log(isValid('{[]}'))  // true

队列(Queue)

队列是除了栈之外的另外一种受限的线性结构

相关概念

  • 对头: 队列的头部,队列中最先进入的元素放在此处
  • 队尾: 队列的尾部,队列中最后进入的元素放在此处

特点:

  • 队列是另外一种受限的线性结构,受限表现在元素必须是先进先出的
  • 表现在其只允许在一端添加元素,而只在另一端删除元素
    --------------------
入队 5 -> 4 -> 3-> 2 -> 1  出队
    ---------------------

队列的实现

  • 基于js数组实现

队列的方法

  • enqueue: 向队尾添加元素
  • dequeue: 队头出队
  • front: 查询队头元素
  • isEmpty: 判断队列是否为空
  • size: 队列的元素个数
  • toString:打印队列中的元素
class Queue<Element> {
    items: Element[] = [];

    // 入队
    enqueue(node: Element): void {
        this.items.push(node);
    }

    // 出队
    dequeue(): Element | null {
        return this.isEmpty() ? null : this.items.shift()!;
    }

    // 是否为空队列
    isEmpty(): boolean {
        return this.items.length > 0;
    }

    // 查看头部元素
    front(): Element | null {
        return this.isEmpty() ? null : this.items[0];
    }

    // 返回元素个数
    size(): number {
        return this.items.length;
    }

    // 打印元素
    toString(flag: string): string {
        return this.items.join(flag);
    }
}

应用击鼓传花

  • 制定规则,每次都是报数为3的同学淘汰
function passGame<T>(stuList: T[], num: number) {
    const queue = new Queue1<T>();
    // 初始化数据
    stuList.forEach(el => {
        queue.enqueue(el);
    });

    // 核心逻辑
    while(queue.size() > 1) {
        for(let i = 0; i < num - 1; i++) {
            // 把通过的学生出队列再入队列
            queue.enqueue(queue.dequeue() as T);
        }
        // 这个是淘汰的
        queue.dequeue();
    }
    return queue.front();
}
const stuList = [
    "stu1",
    "stu2",
    "stu3",
    "stu4",
    "stu5",
    "stu6",
    "stu7",
]
passGame<string>(stuList, 3);

优先级队列

  • 优先级队列相比于普通队列,其特殊之处表现在:队列中的每个元素都有一个优先级,新插入的新元素不一定在队尾,而是根据其优先级确定正确的位置。
    • 意思就是可以搞特殊化(开后门) todo

概念

  • 优先级:指的是队列中元素的权重,是排序的依据

特点

  • 相比于普通队列,元素不遵守先进先出的原则,而是根据优先级/权重决定顺序

代码实现

interface IQueueElement<T> {
    value: T;
    priority: number;
}
// 优先级队列
class PriorityQueue<T> {
    items: IQueueElement<T>[] = [];
    static QueueElement = class<T> {
        public priority : number;
        constructor(public value: T, priority: number) {
            this.value = value;
            this.priority = priority;
        }
    }
    enqueue(value: T, priority: number): void {
        // 创建一个优先队列的节点
        const element: IQueueElement<T> = new PriorityQueue.QueueElement(value, priority); 
        if(this.isEmpty()) {//如果为空直接加
            this.items.push(element);
        }else {
            let len = this.size();
            let isAdd = false;
            for(let i = 0; i < len; i++) {
                const curPriority = this.items[i].priority;
                // 如果我的权重 > 当前的就排在这个位置
                if(priority > curPriority) {
                    this.items.splice(i, 0, element);
                    isAdd = true;
                    break;
                }
            }

            // 所有的循环完了都没插入就插在末尾
            if(!isAdd) {
                this.items.push(element);
            }
        }

    }

    // 出队
    dequeue(): IQueueElement<T> | null {
        return this.isEmpty() ? null : this.items.shift()!;
    }

    // 是否为空队列
    isEmpty(): boolean {
        return this.items.length > 0;
    }

    // 查看头部元素
    front(): IQueueElement<T> | null {
        return this.isEmpty() ? null : this.items[0];
    }

    // 返回元素个数
    size(): number {
        return this.items.length;
    }

    // 打印元素
    toString(flag: string): string {
        return this.items.map(item => item.value).join(flag);
    }
}

链表(Stack)

树(Tree)

哈希表(Hash Table)

图(Graph)

跟着下面这个前辈学习 image.png