double用byte数组如何表示_如何用JS实现数组排序可视化

3c62f8b5e065c1b93b4d68776234d9f8.png

要实现数组排序可视化,首先要明白“帧”是什么。对于原地排序,数组中元素的每一次交换,就对应了动画中的一帧。将交换后的数组的每一次的临时状态推入队列末尾(注意,推入的应该是当前数组的深拷贝),同时从队列的头部以固定的时间间隔拿元素进行渲染,就能将整个排序的过程可视化出来。

由于JS是单线程的,创建中间状态数组队列的过程和渲染过程要做到同时执行可能比较难(中间涉及到很复杂的异步调度),可以考虑先创建队列缓存住,再进行渲染。渲染可以考虑使用requestAnimationFrame方法。

实际设计中,考虑实现一个SwapAspect来封装数组的交换操作,这样对数组的中间状态保存对于实际的排序算法来说就是透明的了。

class SwapAspect {
    constructor(frameFunc) {
        if (!frameFunc || typeof frameFunc !== 'function')
            throw new Error(frameFunc + ' is not a function!');
        this.frameFunc = frameFunc;
        this.queue = [];
        this.counter = 0;
    }

    swap(a, i, j) {
        if (i === j)
            return;
        const temp = a[i];
        a[i] = a[j];
        a[j] = temp;
        this.queue.push(a.slice());
        this.counter++;
    }

    clear() {
        this.counter = 0;
        this.queue.length = 0;
    }

    showProcess() {
        const queue = this.queue;
        const frameFunc = this.frameFunc;
        const sortAnimationFrame = function () {
            const a = queue.shift();
            if (a) {
                frameFunc(a);
                requestAnimationFrame(sortAnimationFrame);
            }
        };
        requestAnimationFrame(sortAnimationFrame);
        return this.counter;
    }
}

数组的形式可以用柱状图来表达,当然可以用Canvas直接手写实现;当然为了避免重新造轮子,也可以用现有的框架。这里使用Echarts来绘制柱状图。

const commonOption = {
            backgroundColor: '#0f375f',
            xAxis: {
                type: 'category'
            },
            yAxis: {
                type: 'value'
            },
            series: [{
                type: 'bar',
                itemStyle: {
                    normal: {
                        barBorderRadius: 5,
                        color: new echarts.graphic.LinearGradient(
                            0, 0, 0, 1,
                            [
                                {offset: 0, color: '#14c8d4'},
                                {offset: 1, color: '#43eec6'}
                            ]
                        )
                    },
                    emphasis: {
                        color: new echarts.graphic.LinearGradient(
                            0, 0, 0, 1,
                            [
                                {offset: 0, color: '#2378f7'},
                                {offset: 0.7, color: '#2378f7'},
                                {offset: 1, color: '#83bff6'}
                            ]
                        )
                    }
                }
            }],
            animation: false
        }

注意,在使用Echarts的时候要将animation设置为false,否则在requestAnimationFrame的时候会发生问题。

这样,就可以定义通用的排序方法:

//handle为Echarts实例对象,sortFunc为实际的排序方法
const sortWeaver = function (handle, sortFunc) {
        const frameFunc = function (a) {
            const option = {
                series: {
                    data: a
                }
            };
            handle.setOption(option);
        };
        const swapAspect = new SwapAspect(frameFunc);
        return function (a) {
            if (!Array.isArray(a))
                return;
            handle.clear();
            handle.setOption(commonOption);
            frameFunc(a);
            sortFunc(a, swapAspect);
            animationInProcessQueue.push(swapAspect);
            swapAspect.showProcess();
        };

    };

冒泡排序:

const bubbleSort = sortWeaver(bubbleHandle, function (a, swapAspect) {
        const len = a.length;
        for (let i = len - 1; i > 0; i--) {
            for (let j = 0; j < i; j++) {
                if (a[j] > a[j + 1]) {
                    swapAspect.swap(a, j + 1, j);
                }
            }
        }
    });

选择排序:

const selectionSort = sortWeaver(selectionHandle, function (a, swapAspect) {
        const len = a.length;
        for (let i = len - 1; i > 0; i--) {
            let maxJ = i;
            for (let j = 0; j < i; j++) {
                if (a[j] > a[maxJ]) {
                    maxJ = j;
                }
            }
            swapAspect.swap(a, maxJ, i);
        }
    });

插入排序:

const insertionSort = sortWeaver(insertionHandle, function (a, swapAspect) {
        const len = a.length;
        for (let i = 1; i < len; i++) {
            for (let j = i; j > 0 && a[j] < a[j - 1]; j--)
                swapAspect.swap(a, j, j - 1);
        }
    });

快速排序:

const quickSort = sortWeaver(quickHandle, function (a, swapAspect) {
        quickSort0(a, 0, a.length - 1, swapAspect);
    });

    const quickSort0 = function (a, left, right, swapAspect) {
        if (left >= right)
            return;
        const pivotIndex = (left + right) >>> 1;
        const pivotNewIndex = partition(a, left, right, pivotIndex, swapAspect);
        quickSort0(a, left, pivotNewIndex - 1, swapAspect);
        quickSort0(a, pivotNewIndex + 1, right, swapAspect);
    };

    const partition = function (a, left, right, pivotIndex, swapAspect) {
        const pivot = a[pivotIndex];
        swapAspect.swap(a, pivotIndex, right);
        let storeIndex = left;
        for (let i = left; i < right; i++) {
            if (a[i] < pivot) {
                swapAspect.swap(a, storeIndex++, i);
            }
        }
        swapAspect.swap(a, storeIndex, right);
        return storeIndex;
    };

堆排序:

const heapSort = sortWeaver(heapHandle, function (a, swapAspect) {
        const len = a.length;
        for (let i = (len >>> 1) - 1; i >= 0; i--)
            maxHeapify(a, i, len - 1, swapAspect);
        for (let i = len - 1; i > 0; i--) {
            swapAspect.swap(a, 0, i);
            maxHeapify(a, 0, i - 1, swapAspect);
        }
    });

    const maxHeapify = function (a, start, end, swapAspect) {
        let parent = start;
        let child = (parent << 1) + 1;
        while (child <= end) {
            if (child + 1 <= end && a[child] < a[child + 1])
                child += 1;
            if (a[parent] > a[child])
                return;
            else {
                swapAspect.swap(a, parent, child);
                parent = child;
                child = (parent << 1) + 1;
            }
        }
    };

梳排序:

const combSort = sortWeaver(combHandle, function (a, swapAspect) {
        const len = a.length;
        const shrinkFactor = 0.8;
        let gap = len, swapped = true;
        while (gap > 1 || swapped) {
            if (gap > 1)
                gap = Math.trunc(gap * shrinkFactor);
            swapped = false;
            for (let i = 0; i + gap < len; i++) {
                if (a[i] > a[i + gap]) {
                    swapAspect.swap(a, i, i + gap);
                    swapped = true;
                }
            }
        }
    });

这里需要说明的是,选择排序的时间复杂度是

,但这个是针对
比较次数的,实际 交换数组元素的复杂度是
,因此动画表现中它反而是最快的。

4db04c255e20582df8fe5e18229c563a.png

项目在线演示地址:https://elasticdogs.com/sort/index.html

源码GitHub地址:https://github.com/YuyuZha0/in-place-sort-animation

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值