为了账号安全,请及时绑定邮箱和手机立即绑定

深度实战玩转算法 -- 选择排序算法可视化

标签:
Java JavaScript

看了 @Liuyubobo 老师的 深度实战玩转算法 课程,感觉对自己解决问题的思路提供了很多帮助。本着毫不为己专门为人的精神[自己都脸红],在不破坏算法本身和最大限度保持和课程代码一致的前提下,使用canvas重写了这些课程中的代码。希望能给大家提供一些思路,写的可能不太好,希望大家多包涵,如果有好的思路和建议也可以在下面留言告诉我。

说明

本来的渲染应该是一边执行算法一边渲染的,但是js中没有sleep这种线程休眠的方法。尝试了一些类似的手段,结果发现会导致浏览器卡住。为了不破坏算法本身的意义,就采用了一种比较作弊的手段。先计算出所有的数据,保存在一个数组里,然后再使用setInterval来进行渲染。

如果大家有更好的实现方式,可以在下面留言告诉我。

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>选择排序算法动画</title>
</head>
<body style="height: 100%, width: 1000px;">
    <canvas id="canvas" style="height: 100%">
        当前浏览器不支持Canvas,请更换浏览器后再试
    </canvas>

    <script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/AlgoVisHelper.js"></script>
    <script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/AlgoFrame.js"></script>
    <script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/SelectionSortData.js"></script>
    <script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/AlgoVisualizer.js"></script>
    <script type="text/javascript">
        window.onload = function(){
            var sceneWidth = 1000;
            var sceneHeight = 600;

            var canvas = document.getElementById('canvas');
            var g2d = canvas.getContext('2d');

            canvas.width = sceneWidth;
            canvas.height = sceneHeight;

            var N = 100;

            var visualizer = new AlgoVisualizer(g2d, sceneWidth, sceneHeight, 100);
        }
    </script>
</body>
</html>

AlgoVisHelper.js

class AlgoVisHelper{
    // 构造函数
    constructor() {}

    static fillRectangle(g2d, x, y, w, h){
        g2d.fillRect(x, y, w, h);
    }

    static setColor(g2d, color){
        g2d.fillStyle = color;
    }
}

// 类中的静态属性
AlgoVisHelper.Red = "#F44336";
AlgoVisHelper.Pink = "#E91E63";
AlgoVisHelper.Purple = "#9C27B0";
AlgoVisHelper.DeepPurple = "#673AB7";
AlgoVisHelper.Indigo = "#3F51B5";
AlgoVisHelper.Blue = "#2196F3";
AlgoVisHelper.LightBlue = "#03A9F4";
AlgoVisHelper.Cyan = "#00BCD4";
AlgoVisHelper.Teal = "#009688";
AlgoVisHelper.Green = "#4CAF50";
AlgoVisHelper.LightGreen = "#8BC34A";
AlgoVisHelper.Lime = "#CDDC39";
AlgoVisHelper.Yellow = "#FFEB3B";
AlgoVisHelper.Amber = "#FFC107";
AlgoVisHelper.Orange = "#FF9800";
AlgoVisHelper.DeepOrange = "#FF5722";
AlgoVisHelper.Brown = "#795548";
AlgoVisHelper.Grey = "#9E9E9E";
AlgoVisHelper.BlueGrey = "#607D8B";
AlgoVisHelper.Black = "#000000";
AlgoVisHelper.White = "#FFFFFF";

AlgoFrame.js

class AlgoFrame{

    constructor(g2d, title, canvasWidth, canvasHeight) {
        this.g2d = g2d;
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;
    }

    getCanvasWidth() {
        return this.canvasWidth;
    }

    getCanvasHeight() {
        return this.canvasHeight;
    }

    repaint(){
        // 具体绘制
        this.g2d.clearRect(0, 0, this.canvasWidth, this.canvasHeight);

        var w = this.canvasWidth / this.data.N();

        for (var i = 0; i < this.data.N(); i++) {
            if(i < this.data.orderedIndex){
                AlgoVisHelper.setColor(this.g2d, AlgoVisHelper.Red);
            }else{
                AlgoVisHelper.setColor(this.g2d, AlgoVisHelper.Grey);
            }
            if(i == this.data.currentCompareIndex){
                AlgoVisHelper.setColor(this.g2d, AlgoVisHelper.LightBlue);
            }
            if(i == this.data.currentMinIndex){
                AlgoVisHelper.setColor(this.g2d, AlgoVisHelper.Indigo);
            }
            AlgoVisHelper.fillRectangle(this.g2d, i * w, this.canvasHeight - this.data.get(i), w - 1, this.data.get(i));
        }
    }

    render(data) {
       this.data = data;    
       this.repaint();
    }
}

SelectionSortData.js

class SelectionSortData{

    constructor(N, randomBound){

        this.numbers = new Array(N);
        this.orderedIndex = -1;         // [0...orderedIndex) 是有序的
        this.currentCompareIndex = -1;  // 当前正在比较的元素索引
        this.currentMinIndex = -1;      // 当前找到的最小元素的索引

        for( var i = 0 ; i < N ; i ++){
            this.numbers[i] = parseInt((Math.random()*randomBound)) + 1;
        }
    }

    N(){
        return this.numbers.length;
    }

    get(index){
        if( index < 0 || index >= this.numbers.length){
            throw ReferenceError("Invalid index to access Sort Data.");
        }

        return this.numbers[index];
    }

    swap(i, j) {

        if( i < 0 || i >= this.numbers.length || j < 0 || j >= this.numbers.length){
            throw ReferenceError("Invalid index to access Sort Data.");
        }

        var t = this.numbers[i];
        this.numbers[i] = this.numbers[j];
        this.numbers[j] = t;
    }

}

AlgoVisualizer.js

class AlgoVisualizer {
    constructor(g2d, sceneWidth, sceneHeight, N) {

        // 动画的执行速度[毫秒]
        this.DELAY = 5;

        this.g2d = g2d;

        // 初始化数据
        this.data = new SelectionSortData(N, sceneHeight);

        // 动画整个存储
        this.data_list = new Array();

        // 初始化视图
        this.frame = new AlgoFrame(g2d, "Selection Sort Visualization", sceneWidth, sceneHeight);
        this.run();
    }

    // 生成数据逻辑
    run() {
        this.setData(0, -1, -1);

        for (var i = 0; i < this.data.N(); i++) {
            // 寻找[i, n) 区间里的最小值的索引
            // [0,1) 前闭后开 0 <= n < 1
            var minIndex = i;
            this.setData(i, -1, minIndex);
            for (var j = i + 1; j < this.data.N(); j++) {
                this.setData(i, j, minIndex);
                if (this.data.get(j) < this.data.get(minIndex)) {
                    minIndex = j;
                    this.setData(i, j, minIndex);
                }
            }
            this.data.swap(i, minIndex);
            this.setData(i + 1, -1, -1);
        }
        this.setData(this.data.N(), -1, -1);
        // 渲染数据
        this.render();
    }

    setData(orderedIndex, currentCompareIndex, currentMinIndex) {
        var data = new SelectionSortData();
        data.orderedIndex = orderedIndex;
        data.currentCompareIndex = currentCompareIndex;
        data.currentMinIndex = currentMinIndex;
        data.numbers = this.data.numbers.slice();
        this.data_list[this.data_list.length] = data
    }

    render(){
        var i = 0;
        setInterval(() => {
            if(i < this.data_list.length){
                this.frame.render(this.data_list[i]);
                i++;
            }
        }, this.DELAY);
    }
}
点击查看更多内容
33人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
全栈工程师
手记
粉丝
1.9万
获赞与收藏
4701

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消