JavaScript数据结构与算法+面试题——线性结构(栈结构,队列结构,数组结构,链表结构)

61 阅读3分钟

线性结构

概念:线性结构(英語:Linear List)是由n(n≥0)个数据元素(结点)a[0],a[1],a[2],a[n-1]组成的有限序列。

常见的线性结构

  • 数组结构(Array)
  • 栈结构(stack)
  • 队列结构(queue)
  • 链表结构(linkedlist)

一.栈结构

栈结构是一种常见的线性结构,栈和队列属于受限性的线性结构,即(先进后出,后进先出)

  • 先入栈的会进入到栈底,栈底是需要在栈底上面的栈全部清空的时候才可以出栈的,也就是先进入的后出。

image.png

面试题

image.png

  • 题解:正确答案是C,因为进入栈的顺序是6 5 4 3 2 1,但是并不是需要全部进栈之后才可以出栈,而是可以边进栈边出栈。按照顺序应该是5先出栈,因为此时6还处在栈底,6的上面还有5,所以需要5出栈之后6才可以出栈,所以正确的顺序中有一种出栈顺序应该是3 4 5 6 2 1 2.使用栈-进行十进制转换为二进制
//1.首先定义一个栈结构的类,并将其导出
import iStack from "./iStack";

class ArrayStack<T> implements iStack<T>{
    private data: T[] = [];
    push(elment: T): void {
        this.data.push(elment);
    }
    pop(): T | undefined {
        return this.data.pop();
    }
    peek(): T | undefined {
        return this.data[this.data.length - 1];
    }
    isEmpty(): boolean {
        return this.data.length === 0;
    }
    count(): number {
        return this.data.length;
    }

}

export default ArrayStack;

2.实现一个十进制转换二进制的方法

import ArrayStack from "./04.栈结构"

function dectob(num: number): string {
//初始化一个栈结构
    const stack = new ArrayStack<number>()
    while (num > 0) {
        const result = num % 2
        stack.push(result)
        num = Math.floor(num / 2)
    }
    let resultstring = ''
    while (!stack.isEmpty()) {
        resultstring += stack.pop()
    }
    return resultstring
}
console.log(dectob(35))//100011

3.判断是否为有效括号:()前面的括号和后面的括号类型对应为有效括号,互相包含为无效或者不对应为无效例如({)},[]{}(,这样的都是无效的:

import ArrayStack from "./04.栈结构";

function isvalid(value: string): boolean {
    // 定义一个栈结构
    const stack = new ArrayStack<string>();
    for (let i = 0; i < value.length; i++) {
        let c = value[i]

        switch (c) {
            case '(':
            //每当遇到一个左括号就向栈当中入栈一个对应的右括号
                stack.push(')')
                break
            case '{':
                stack.push('}')
                break
            case '[':
                stack.push(']')
                break
            default:
            //如果c与弹出栈的括号类型不一致则直接返回false说明是无效括号,并没有一一对应
                if (c !== stack.pop()) return false
        }
    }
    //判断栈是否为空,如果为空前面也是对应的则是有效括号
    return stack.isEmpty()
}

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

队列结构

链表结构

链表和数组一样都是储存大量数据的结构,但是不同于数据的是,链表可以不是一个连续的内存片段。链表的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(有些语言称为指针或者链接)组成。链表的优势就是:1.插入删除数据的时候不会影响后续的数据,时间复杂度可以达到O(1)。2.可以充分利用计算机的内存,实现灵活的内存动态管理。3.链表不必在创建时就确定大小,并且大小可以无限的延伸下去。

image.png

链表的实现

// 1.节点模块类
class Node<T>{
    value: T
    next: Node<T> | null = null
    constructor(value: T) {
        this.value = value
    }
}

// 2.创建linkedList的类
class linkedList<T>{
    head: Node<T> | null = null
    size: number = 0
    get length(): number {
        return this.size
    }

    // 添加链表值
    append(value: T) {
        const newNode = new Node(value)
        if (!this.head) {
            this.head = newNode
        } else {
            let current = this.head
            while (current.next) {
                current = current.next
            }
            // 让current指向最后一个节点
            current.next = newNode
        }
        this.size++

    }
    // 遍历链表
    traverse() {
        let current = this.head
        const values: T[] = []
        while (current) {
            values.push(current.value)
            current = current.next
        }
        console.log(values)


    }
    // 指定位置添加元素
    insert(value: T, position: number) {
        if (position < 0 || position > this.size) return false
        const newNode = new Node(value)
        if (position === 0) {
            newNode.next = this.head
            this.head = newNode
        } else {
            let index = 0
            let current = this.head
            let previous: Node<T> | null = null
            while (index++ < position && current) {
                current = current.next
                previous = current
            }
            newNode.next = current
            previous!.next = newNode
        }
        this.size++
    }
    // 删除指定位置元素
    delete(position: number) {
        if (position < 0 || position >= this.size) return false
        let current = this.head
        if (position === 0) {
            this.head = this.head?.next || null
        } else {
            let index = 0
            let previous: Node<T> | null = null
            while (index++ < position && current) {
                previous = current
                current = current.next
            }
            previous!.next = current?.next ?? null
        }
        this.size--
        return current?.value
    }
    // 根据索引获取值
    getValue(position: number) {
        if (position < 0 || position >= this.size) return null
        let index = 0
        let current = this.head
        while (index++ < position && current) {
            current = current.next
        }
        return current?.value
    }

    // 指定位置更新值
    update(value: any, position: number) {
        if (position < 0 || position >= this.size) return false

        let index = 0
        let current = this.head
        while (index++ < position && current) {
            current = current.next
        }
        current!.value = value
    }
    // 根据值返回索引
    indexOf(value: any) {
        let current = this.head
        let index = 0
        while (current) {
            if (current.value === value) {
                return index
            }
            current = current!.next
            index++
        }
        return -1

    }

}

const link = new linkedList<string>()
link.append('aaa')
link.append('bbb')
link.append('ccc')
link.append('ddd')


console.log(link.delete(1))  //bbb

link.traverse() //[ 'aaa', 'ccc', 'ddd' ]
console.log(link.getValue(1))//ccc
link.update(1111, 1)
link.traverse()//[ 'aaa', 1111, 'ddd' ]

console.log(link.indexOf('ddd'))//2



export { }