Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重启数据结构与算法1 #6

Open
Ray-56 opened this issue Jun 3, 2020 · 0 comments
Open

重启数据结构与算法1 #6

Ray-56 opened this issue Jun 3, 2020 · 0 comments

Comments

@Ray-56
Copy link
Owner

Ray-56 commented Jun 3, 2020

重启数据结构与算法1

重启数据结构与算法,一些代码案例使用 Deno 与 TypeScript 实现

  1. 重启数据结构与算法1

线性数据结构

元素之间一对一的线性关系

  • 顺序表
  • 链表 不一定连续,存数据元素以及相邻元素的地址信息

常见有:数组、队列、栈、链表

非线性数据结构

常见有:二维数组、多维数组、广义表,树结构、图结构


稀疏数组

当一个数组中大部分为0,或者为同一个值,可以使用稀疏数组来保存

处理方法:

  • 记录几行几列,有多少个不同的值
  • 不同值元素的行列及值记录在小规模数组中,缩小程序规模

实践案例

简易五子棋:

// 稀疏数组 五子棋
// 因为有读取和写入 所以运行时需要加入 --allow-write --allow-read
const dataFileName = 'sparsearray.data';
type SparseArrayType = number[][];
class SparseArray {
    /**
     * 原始二维数组 11 * 11
     * 0: 没有棋子
     * 1: 黑子
     * 2: 蓝子
     */
    private chessArr1: SparseArrayType;
    constructor() {
        this.chessArr1 = [];
        for (let i = 0; i < 11; i++) {
            this.chessArr1[i] = new Array(11).fill(0);
        }
        this.chessArr1[1][2] = 1;
        this.chessArr1[2][4] = 2;
        // 输出 二维数组
        console.table(this.chessArr1);
    }
    /** 读取本地文件 */
    public ready() {
        const decoder = new TextDecoder("utf-8");
        const data = Deno.readFileSync(dataFileName);
        console.log('读取到的内容: ');
        console.table(JSON.parse(decoder.decode(data)));
    }
    /** 保存本地文件 */
    public save(arr: SparseArrayType) {
        const encoder = new TextEncoder();
        const data = encoder.encode(JSON.stringify(arr));
        Deno.writeFileSync(dataFileName, data);
    }
    /** 二维转稀疏 */
    toSparseArray(): SparseArrayType {
        // 1 找到非 0 的个数
        let sum = 0;
        for (let i = 0; i < 11; i++) {
            for (let j = 0; j < 11; j++) {
                if (this.chessArr1[i][j] !== 0) {
                    sum++;
                }
            }
        }
        console.log(`sum -> ${sum}`);
        // 2.1 创建对应数组
        let intSparseArray: number[][] = [[11, 11, sum]];
        // 2.2 遍历将非 0 存入
        let count = 0; // 稀疏数组下标
        for (let i = 0; i < 11; i++) {
            for (let j = 0; j < 11; j++) {
                if (this.chessArr1[i][j] !== 0) {
                    intSparseArray[++count] = [i, j, this.chessArr1[i][j]];
                }
            }
        }
        console.table('得到的稀疏数组为:');
        console.table(intSparseArray);
        return intSparseArray;
    }
    /** 稀疏数组恢复为二维数组 */
    recover(sp: SparseArrayType): SparseArrayType {
        let chessArr2: SparseArrayType = [];
        // 1. 创建二维数组
        let [rowCount, columnCount, valueCount] = sp[0]; // 第0行的 第一个为行数量, 第二个为列数量,第三个为非空值的数量
        for (let i = 0; i < columnCount; i++) {
            chessArr2[i] = new Array(rowCount).fill(0);
        }
        // 2. 读取稀疏数组后几行数据赋值给新创建的二维数组
        for (let i = 1; i < sp.length; i++) {
            const [rowIndex, columnIndex, value] = sp[i];
            chessArr2[rowIndex][columnIndex] = value
        }
        console.table('恢复后的稀疏数组为:');
        console.table(chessArr2);
        return chessArr2;
    }
}
export default SparseArray;

队列

队列是一个有序列表,可以用数组或者链表来表示,遵循先进先出的原则。常见场景:银行排队办业务,先排队的先办业务。

数组模拟

// 数组模拟队列
class QueueArray {
    public queue: number[];
    private front: number;
    private rear: number;
    private maxSize: number;
    constructor(maxSize: number) {
        this.queue = [];
        this.front = -1;
        this.rear = -1;
        this.maxSize = maxSize;
    }
    public isFull() {
        return this.rear === this.maxSize - 1;
    }
    public isEmpty() {
        return this.front === this.rear;
    }
    public add(value: number) {
        if (this.isFull()) {
            console.log('队列已满');
            return;
        }
        this.queue[++this.rear] = value;
    }
    public pop(): number | undefined {
        if (this.isEmpty()) {
            console.log('队列为空');
            return;
        }
        return this.queue[++this.front];
    }
    public show() {
        if (this.isEmpty()) {
            console.log('队列为空');
            return;
        }
        console.log(this.queue.slice(this.front + 1, this.rear + 1).join(','));
    }
}

export default QueueArray;

问题以及优化:

  • 目前数组使用一次就不能用,没有达到复用
  • 将这个数组使用算法,改进成一个 环形队列 取模:%
import { readLines } from "https://deno.land/std/io/mod.ts";

// 环形队列
class CircleArrayQueue {
    private maxSize: number;
    private queue: number[];
    private front: number;
    private rear: number;
    constructor(maxSize: number) {
        this.maxSize = maxSize;
        this.queue = [];
        this.front = 0;
        this.rear = 0;
    }
    private isFull() {
        return (this.rear + 1) % this.maxSize === this.front;
    }
    private isEmpty() {
        return this.front === this.rear;
    }
    public add(value: number) {
        if (this.isFull()) {
            console.log('队列已满');
            return;
        }
        this.queue[this.rear++] = value;
    }
    public pop() {
        if (this.isEmpty()) {
            console.log('队列为空');
            return;
        }
        const ret = this.queue[this.front];
        this.front++;
        return ret;
    }
    public show() {
        if (this.isEmpty()) {
            console.log('队列为空');
            return;
        }
        const printList = [];
        let count = 0;
        for (let i = this.front; i < this.front + this.size() - 1; i++) {
            printList[count++] = this.queue[i];
        }
        console.table(printList);
    }
    /** 有效数据个数 */
    private size() {
        return this.rear - this.front + 1;
    }
}

const commandDict = {
    a: 'add -入队',
    p: 'pop -出队',
    s: 'show -显示队列',
    '-h': 'help -显示帮助',
}
window.onload = async function main() {
    console.table(commandDict);
    const q = new CircleArrayQueue(4); // 提前规定,4 是给 rear 空出一个位置
    for await (const line of readLines(Deno.stdin)) {
        switch (line) {
            case '-h':
                console.table(commandDict);
                break
            case 'a':
				console.log('请输入一个数字:');
                const ret = await readLines(Deno.stdin).next();
                const value = Number(ret.value.trim());
				if (typeof value !== 'number' || isNaN(value)) {
					console.log('输入内容不为数字!');
					break;
				}
				q.add(value);
				break;
			case 'p':
				const valuePop = q.pop();
				console.log(`${valuePop},出队`);
				break;
			case 's':
				q.show();
				break;
        }
    }
}

链表

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

实践案例

使用单向链表做一个水浒英雄榜,实现了增删改查,以及按序号加入:

class HeroNode {
    public no: number;
    public name: string;
    public nickName: string;
    public next: HeroNode | null;
    constructor(no: number, name: string, nickName: string) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
        this.next = null;
    }

    public show() {
        console.log(`no = ${this.no}; name = ${this.name}; nickName = ${this.nickName}`);
    }
}

class SingleLinkedList {
    private head: HeroNode;
    constructor() {
        this.head = new HeroNode(0, '', '');
    }
    /** 加入到最后 */
    public add(node: HeroNode) {
        let temp = this.head;
        while (temp.next) {
            temp = temp.next;
        }
        temp.next = node;
    }
    /**
     * 按照编号加入
     * 1. 已存在就提示
     * */
    public addByOrder(heroNode: HeroNode) {
        let temp: HeroNode | null = this.head;
        let flag = false; // 是否存在

        // 找到要插入的上一个位置
        while (temp.next !== null) {
            if (temp.next.no === heroNode.no) {
                flag = true;
                break;
            }
            if (temp.next.no > heroNode.no) {
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            console.log(`编号:${heroNode.no},已存在`)
            return;
        }
        heroNode.next = temp.next;
        temp.next = heroNode;
    }
    /** 修改 */
    public update(newHeroNode: HeroNode) {
        let temp = this.head;
        let flag = false; // 是否存在
        while (temp.next !== null) {
            if (temp.no === newHeroNode.no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
            return;
        }
        console.log('要修改的英雄不存在');
    }
    /** 删除 */
    public del(no: number) {
        let temp = this.head;
        let flag = false; // 是否存在

        // 找上一个
        while (temp.next) {
            if (temp.next.no === no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag && temp.next !== null) {
            temp.next = temp.next.next;
            return;
        }
        console.log('要删除的英雄不存在');
    }
    /** 展示链表中的内容 */
    public show() {
        let temp = this.head;
        while (temp.next) {
            temp = temp.next;
            temp.show();
        }
    }
}

window.onload = function main() {
    const hero1 = new HeroNode(1, '宋江', '及时雨');
    const hero2 = new HeroNode(2, '卢俊义', '玉麒麟');
    const hero3 = new HeroNode(3, '吴用', '智多星');
    const hero4 = new HeroNode(4, '公孙胜', '入云龙');
    const singleLinkedList = new SingleLinkedList();
    // singleLinkedList.add(hero1);
    // singleLinkedList.add(hero2);
    // singleLinkedList.add(hero3);
    // singleLinkedList.add(hero4);
    singleLinkedList.addByOrder(hero2);
    singleLinkedList.addByOrder(hero1);
    singleLinkedList.addByOrder(hero4);
    singleLinkedList.addByOrder(hero3);
    singleLinkedList.del(5);
    singleLinkedList.show();
}

下期预告

下期针对单向链表做几个面试题:

  • 求单链表中节点个数

  • 查找单链表中的倒数第k个节点

  • 单链表反转

  • 从尾到头打印单链表

    反向遍历

    Stack栈

  • 合并有序单链表,合并后依然有序

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant