《数据结构》课程设计-js实现排序算法可视化

先上效果图:

数据可视化设计pdf电子书 数据可视化课程设计_算法

一.数据可视化

1.什么是数据可视化

数据可视化主要旨在借助于图形化手段,清晰有效地传达与沟通信息。但是,这并不就意味着数据可视化就一定因为要实现其功能用途而令人感到枯燥乏味,或者是为了看上去绚丽多彩而显得极端复杂。为了有效地传达思想概念,美学形式与功能需要齐头并进,通过直观地传达关键的方面与特征,从而实现对于相当稀疏而又复杂的数据集的深入洞察。然而,设计人员往往并不能很好地把握设计与功能之间的平衡,从而创造出华而不实的数据可视化形式,无法达到其主要目的,也就是传达与沟通信息。
数据可视化与信息图形、信息可视化、科学可视化以及统计图形密切相关。当前,在研究、教学和开发领域,数据可视化乃是一个极为活跃而又关键的方面。“数据可视化”这条术语实现了成熟的科学可视化领域与较年轻的信息可视化领域的统一。

2.什么时候需要将数据可视化

你的程序有多么依赖数据?即使应用程序不完全面向业务,你也可能需要管理面板、仪表板、性能跟踪以及用户非常喜欢的类似分析功能的数据。

对于 JS 开发人员来说,可视化数据的能力与制作交互式网页一样有价值。特别是两者经常同时出现。

我们利用视觉获取的信息量,远远比别的感官要多的多

将数据转换成图形,能给客户更大的视觉冲击以及更好的体验

3.如何将数据可视化

作为以“成为Java全栈工程师”为目标而拼命前进的人,数据可视化特指web前端的数据可视化。随着JavaScript在数据可视化领域的活跃,市面上出现了众多可视化框架,如:D3.js、hightcharts.js、echarts.js…………

我选择了14个用于数据可视化的 JavaScript 库(这些库目前是最受欢迎或最有趣的),看看哪个最适合我们的项目。在这里要考虑许多因素:

  • 我想要什么样的图表?饼图,地理图,折线图, 条形图?
  • 有些库只支持少数几种类型。首先要知道自己到底需要哪些。
  • 数据集有多大?
  • 基于 SVG 的库通常更适合中小型数据集,因为每个元素都是唯一的节点并存在于 DOM 树中。这也意味着它们允许被直接访问,从而具有更多的灵活性。虽然你可以借助一些数据聚合算法、智能内存管理和其他花哨的技巧使它们能够处理大型数据集,但是使用基于 Canvas 的大型数据集工具是更可靠的选择。Canvas 非常快。
  • 该应用是用于Web端、移动端还是两者兼而有之?
  • 有些库在响应性方面更好,而其他一些库有自己的 React Native 版本,如 Victory。
  • 浏览器支持给定的库吗?
  • 你使用哪种 JavaScript 框架?
  • 确保你的数据库库能够顺利运行。如果你在用 React,那么使用特定于 React 的库可能比使用包装器更好。
  • 你需要什么样的外观?
  • 如果你需要一些高级动画,也应该考虑到这一点。

在某些情况下,你可能根本不需要数据可视化库。有时用原生 JavaScript 从头开始编写是个好主意。虽然开始代价很大(特别是在你第一次这样的时候),但对于那些带有自定义定制图表的项目来说,可能会在未来获得回报。

那么什么情况下使用库?

当你的项目中有一个奇怪的图表,一个需要漂亮的界面基本功能,有所有的花里胡哨(工具提示、图例、X/Y 轴等)的东西,或当应用需要标准化、响应和详细的图表,特别是需要有多种类型时。

最后,我们用库来避免一次又一次地重新发明轮子,并且大多数库已经存在了很久,并考虑到了大多数的使用情况。对了,他们也带有内置的动画效果。

在项目中尝试所有这些库是不可能的,下面是我根据自己和其他人的经验列出的的列表。请记住,在深入了解一个库之前,搞清楚怎样把它集成到你的项目中总是一个好主意。最后,选哪个是你的事 —— 这只是一个清单!

4.免费数据可视化库

如果你不是一家大公司,那么开源库提供的选择就足够多了。加入你能够回答我上面提到的问题,会很容易找到完美的匹配。

1、D3.js

D3 是最受欢迎的 JS 库之一,不仅可用于数据可视化,还包括动画、数据分析、地理和数据实应用。使用HTML,SVG 和 CSS 等技术。它有一堆庞大的 API,有些人认为它根本不是数据可视化库 。凡是你能想到的,都可以通过这个库完成,但它也有其缺点,学习曲线非常陡峭,而且文档已经过时,这很容易导致混乱。

大部分 API 都暴露了对 DOM 的直接访问,这可能与 React 或 Vue 等现代前端框架的工作方式产生冲突。但还是有办法解决这个问题的。有一份声明性数据驱动文档(简称d4)建议让框架操纵 DOM 并严格使用 D3 作为数据 API。你可以在这里找到更多信息和例子

2、Recharts

为 React 专用。 Recharts 在使用 D3 作为引擎,并导出了声明性组件。它非常轻巧,可以通过渲染 SVG 元素来创建漂亮的交互式图表。它易于使用而且文档完整。图表是可自定义的,库本身提供了一些很好的例子。它的静态图表性能表现非常出色,并包含内置的通用图表工具,比如:图例工具提示和标签。在同一页面和大型数据集上处理多个动画图表时可能会出现滞后现象,不过它仍适用于大多数应用场合。

有点令人不安的是在 GitHub 上有大量未解决的问题。这些问题可能并不重要,但作者似乎并不是很热血回答这些问题。所以如果你遇到困难,请做好深入研究代码的准备。

3、Victory

这是一组专为 React 和 React Native 设计的模块化图表组件。 Victory 在基础方面做得很好 —— 例如坐标轴定制、标签、为单个图表传递不同的数据集都非常简单,并且对样式和行为进行调整时非常简单直观。它真的很好用,能让你用最少的代码创建漂亮的图表。这是绝对值得一试的跨平台的库。

4、React-vis

这是 Uber 开发的一个简单的可视化库,它允许你创建所有常用的图表类型:条形图,树形图,折线图,面积图等。它的 API 非常简单,而且非常灵活。它很轻量,动画简单但流畅。还允许基于现有元素编写自定义图表。

然而,它极简主义的造型可能并不适合每个人的口味(但我喜欢它!)。虽然反响平平,但其文档简单易读。

适用于:React

GitHub:https://uber.github.io/react-… 例子:https://uber.github.io/react-…

5、ApexCharts

这是一个相当简洁的 SVG 图表库,还附带 Vue.js 和 React 包装器。它在不同设备上的效果看起来都很不错,而且该库允许自定义徒步,并提供全面的文档。在较大的数据集上性能可能会受到影响,因此请确保它确实适合你的项目。根据 ApexCharts 的作者 Juned Chhipa 的说法,该库是为了更容易缩放、平移、滚动数据、在图表上放置信息性注释等目的而写的。

这个库比较新,还有很大的发展空间,但如果响应性和互动性对你来说特别重要,那么这个漂亮的库是值得一试的!

适用于:React,Vue.js,纯 JavaScript

GitHub:https://github.com/apexcharts
例子:https://apexcharts.com/javasc…

6、Chart.js

一个非常受欢迎的开源库,在GitHub上超过 4 万 star。它是轻量级的,允许你用 HTML5 Canvas 元素构建响应式图表。可以轻松地对折线图和条形图进行混合和匹配以组合不同的数据集,这是非常棒的功能。

Chart.js 默认为你提供六种不同的图表类型,它支持响应式,并且对初学者非常友好。它也是我处理大数据集时的首选库。绝对是最有趣的开源库之一。

适用于:React,Vue.js

GitHub:https://github.com/chartjs/Ch… 例子:https://www.chartjs.org/sampl…

7、Echarts

百度创建的这个库对于 Web 的数据可视化非常有用。它也提供英文版本,适用于大数据集。它还支持 SVG 和 Canvas 渲染。

适用于:所有环境

GitHub:https://github.com/ecomfe/ech… 例子:https://ecomfe.github.io/echa…

8、Frappe Charts

这是一个非常简单的库,用于零依赖关系的图表。它是开源的,只有 17 个贡献者,是本列表中最小的库之一。

Frappe Charts 的灵感来自一个类似 GitHub 视觉效果的软件包,支持折线图,条形图和其他类型的图表。如果你正在找一个小巧轻量的包,这就是其中一个!

适用于:任何环境

GitHub:https://github.com/frappe/charts
官网:https://frappe.io/charts

9、Nivo

Nivo 是一个基于 D3 和 React 的漂亮框架,提供十四种不同类型的组件来呈现你的数据。它于 2017 年发布,在2017 年 8 月 20 日作为 ProductHunt 的产品推出。

Nivo 提供了许多自定义选项和三个渲染选项:Canvas,SVG,甚至基于 API 的HTML。它的文档非常出色,Demo 可配置且有趣。这是一个高级库,非常简单,不过提供自定义可视化的余地很小。

适用于:React

GitHub:https://github.com/plouc/nivo
官网:https://nivo.rocks/

10、Google Charts

一个非常流行的图表Web服务,我根本无法把它从列表中删除。对于许多人来说,它是首选的 JS 库,因为它提供了多种预先构建的图表类型,例如条形图、折线图、区域图、日历图、地理图表等等。然而,对我来说,在大多数情况下,这个库有点过分,坦率地说我不建议使用它。

它是免费的,但不是开源的(和每个 Google 产品一样)。它在默认情况下不是响应式的,但你可以使用代码调整图表大小。根据图表类型,有不同的自定义选项,它并不完全适合初学者。而且你必须直接从 Google URL 而不是 NPM 包加载它。

适用于:任何环境

Google Charts 官网:https://developers.google.com…
文档:https://developers.google.com…

5.商业图表库

有些库为个人提供了免费版,但你需要付费才能在商业应用中使用它们。下面列出的都是大公司常用的。因为它们都是真正全面的、可定制的,并提供了很好的客户支持。如果你是一个企业,你应该检查出来。

11、amCharts

这是最热门的图表库之一。它漂亮设计确实能够使它在竞争中脱颖而出。 苹果、亚马逊、美国宇航局和许多知名公司都是 amCharts 的用户,这是非常令人印象深刻的。

amCharts 是一种商业工具,每个网站许可的起价为 180 美元。作为投入的回报,你可以获得所需的所有类型的图表,包括地理地图和出色的用户支持,平均响应时间少于3小时。这对大公司来说是一个很好的解决方案。

适用于:Angular,React,Vue.js,普通JS应用,TypeScript

GitHub:https://github.com/amcharts/a… 官网:https://www.amcharts.com/

12、CanvasJS

这是另一种商业工具,提供能够跨设备和浏览器的精美图表。不过它缺少一些图表类型,例如网络图表、迷你图和仪表图。此外它的学习曲线非常陡峭。

另一方面,即使有多达 100k+ 的数据点,也非常的快,并且工作流畅。提供了四个默认主题,应该能够适合大多数场景。他们的客户名单也令人印象深刻:Apple、博世、西门子、惠普、微软等。

适用于:Angular,React,jQuery,纯 JavaScript

官网:https://canvasjs.com/
演示:https://canvasjs.com/javascri…

13、Highcharts

一个发布于 2009 年的 JS 库,基于 SVG ,支持旧版浏览器的 VML 和 Canvas。 它提供了不同的项目模板。 Highcharts 能够与旧版浏览器兼容,其中包括 Internet Explorer 6。

对于非开发人员来说,这是一个很好的解决方案,因为它有一个集成的 WYSIWYG(所见即所得)图表编辑器。它的学习曲线非常流畅,并被许多主要参与者使用,如 Facebook 或微软 —— 甚至有人声称世界上最大的 100 家公司中有 72 家曾经使用过它。这是一个付费的解决方案,价格为1500美元,无限制的商业许可证。

然而在光鲜的外表之下,感觉就像它是在 2009 年写的。最近我的一位同事带我体验了它,让我告诉你,这并不是很愉快。当你不是在深入到代码层摆弄它时,它很好用,但是当你想要。。。这是一件苦差事。

适用于:任何环境

GitHub:https://github.com/highcharts… 官网:https://www.highcharts.com/

14、Zoomcharts

另一个商业 JS 数据工具,自称为 “世界上最具交互性的 JavaScript 图表库。” 除了反应灵敏之外,它主要关注多点触控手势和在各种设备上的原生感觉。该库自诩为美观的可视化,只需很少的代码就可以轻松地部署在你的产品中。
Zoomchatrts 是基于 Canvas 的,在相同的数据量下,使用默认设置,它的速度比基于 SVG 的竞争对手快20倍。我找不到 Zoomcharts 的确切价格,但我发现有一些评论称它 “价格昂贵”。但无论价格怎样,作为回报,你都会得到惊人的互动性、多点触控手势和高品质的用户支持。

适用于:任何环境

官网:https://zoomcharts.com/en/
Demo:https://zoomcharts.com/en/jav…

二.常见的排序算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZgfmM4fA-1591885120158)(…/images/image-20200601175024501.png)]

1.冒泡排序

最简单的一种排序算法。假设长度为n的数组arr,要按照从小到大排序。则冒泡排序的具体过程可以描述为:首先从数组的第一个元素开始到数组最后一个元素为止,对数组中相邻的两个元素进行比较,如果位于数组左端的元素大于数组右端的元素,则交换这两个元素在数组中的位置,此时数组最右端的元素即为该数组中所有元素的最大值。接着对该数组剩下的n-1个元素进行冒泡排序,直到整个数组有序排列。算法的时间复杂度为O(n^2)。

数据可视化设计pdf电子书 数据可视化课程设计_html_02

2.选择排序

严蔚敏版《数据结构》中对选择排序的基本思想描述为:每一趟在n-i+1(i=1,2,…,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。具体来说,假设长度为n的数组arr,要按照从小到大排序,那么先从n个数字中找到最小值min1,如果最小值min1的位置不在数组的最左端(也就是min1不等于arr[0]),则将最小值min1和arr[0]交换,接着在剩下的n-1个数字中找到最小值min2,如果最小值min2不等于arr[1],则交换这两个数字,依次类推,直到数组arr有序排列。算法的时间复杂度为O(n^2)。

3.插入排序

插入排序的基本思想就是将无序序列插入到有序序列中。例如要将数组arr=[4,2,8,0,5,1]排序,可以将4看做是一个有序序列(图中用蓝色标出),将[2,8,0,5,1]看做一个无序序列。无序序列中2比4小,于是将2插入到4的左边,此时有序序列变成了[2,4],无序序列变成了[8,0,5,1]。无序序列中8比4大,于是将8插入到4的右边,有序序列变成了[2,4,8],无序序列变成了[0,5,1]。以此类推,最终数组按照从小到大排序。该算法的时间复杂度为O(n^2)。

4.希尔排序

希尔排序(Shell’s Sort)在插入排序算法的基础上进行了改进,算法的时间复杂度与前面几种算法相比有较大的改进。其算法的基本思想是:先将待排记录序列分割成为若干子序列分别进行插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。

5.快速排序

快速排序的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,已达到整个序列有序。一趟快速排序的具体过程可描述为:从待排序列中任意选取一个记录(通常选取第一个记录)作为基准值,然后将记录中关键字比它小的记录都安置在它的位置之前,将记录中关键字比它大的记录都安置在它的位置之后。这样,以该基准值为分界线,将待排序列分成的两个子序列。

一趟快速排序的具体做法为:设置两个指针low和high分别指向待排序列的开始和结尾,记录下基准值baseval(待排序列的第一个记录),然后先从high所指的位置向前搜索直到找到一个小于baseval的记录并互相交换,接着从low所指向的位置向后搜索直到找到一个大于baseval的记录并互相交换,重复这两个步骤直到low=high为止。

6.归并排序

“归并”的含义是将两个或两个以上的有序序列组合成一个新的有序表。假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到(表示不小于x的最小整数)个长度为2(或者是1)的有序子序列,再两两归并。如此重复,直到得到一个长度为n的有序序列为止。这种排序方法称为2-路归并排序。

7.堆排序

  • 堆的概念
    堆:本质是一种数组对象。特别重要的一点性质:任意的叶子节点小于(或大于)它所有的父节点。对此,又分为大顶堆和小顶堆,大顶堆要求节点的元素都要大于其孩子,小顶堆要求节点元素都小于其左右孩子,两者对左右孩子的大小关系不做任何要求。
    利用堆排序,就是基于大顶堆或者小顶堆的一种排序方法。下面,我们通过大顶堆来实现。
  • 基本思想:
    堆排序可以按照以下步骤来完成:
  1. 首先将序列构建称为大顶堆;
    (这样满足了大顶堆那条性质:位于根节点的元素一定是当前序列的最大值)

数据可视化设计pdf电子书 数据可视化课程设计_数据可视化_03

构建大顶堆.png
  1. 取出当前大顶堆的根节点,将其与序列末尾元素进行交换;
    (此时:序列末尾的元素为已排序的最大值;由于交换了元素,当前位于根节点的堆并不一定满足大顶堆的性质)
  2. 对交换后的n-1个序列元素进行调整,使其满足大顶堆的性质;

数据可视化设计pdf电子书 数据可视化课程设计_数据可视化_04

  1. 重复2.3步骤,直至堆中只有1个元素为止

三.程序设计

JavaScript实现各种排序

1.冒泡排序

function bubbleSort(arr) {
  var i = arr.length, j;
  var tempExchangVal;
  while (i > 0) {
    for (j = 0; j < i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        tempExchangVal = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tempExchangVal;
      }
    }
    i--;
  }
  return arr;
}
var arr = [3, 2, 4, 9, 1, 5, 7, 6, 8];
var arrSorted = bubbleSort(arr);
console.log(arrSorted);
alert(arrSorted);

2.选择排序

var arr = new Array(1, 3, 2, 8, 9, 1, 5);
function SelectionSort(arr) {
    if (arr == null || arr.length < 2) {
         return arr;
    }
    for (var i = 0; i < (arr.length - 1); i++) {
        let minIndex = i;
        for (let j = i + 1; j < arr.length; j++) {
            minIndex = arr[j] < arr[minIndex] ? j : minIndex;
        }
        let temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}
console.log(arr);
SelectionSort(arr);
console.log(arr);

3.插入排序

function InsertionSort(array) {
  var length = array.length;
  for (var i = 0; i < length - 1; i++) {
    //i代表已经排序好的序列最后一项下标
    var insert = array[i+1];
    var index = i + 1;//记录要被插入的下标
    for (var j = i; j >= 0; j--) {
      if (insert < array[j]) {
        //要插入的项比它小,往后移动
        array[j+1] = array[j];
        index = j;
      }
    }
    array[index] = insert;
    console.log(array);
  }
  return array;
}

var arr = [100,90,80,62,80,8,1,2,39];
var result = InsertionSort(arr);
console.log(result);

4.希尔排序

function ShellSort(array) {
  var length = array.length;
  var gap = Math.round(length / 2);
  while (gap > 0) {
    for (var i = gap; i < length; i++) {
      var insert = array[i];
      var index = i;
      for (var j = i; j >= 0; j-=gap) {
        if (insert < array[j]) {
          array[j+gap] = array[j];
          index = j;
        }
      }
      array[index] = insert;
    }
    console.log(array);
    console.log("-----------------------");
    gap = Math.round(gap/2 - 0.1);
  }
  return array;
}
 
var arr = [ 13, 14, 94, 33, 82, 25, 59, 94, 65, 23, 45, 27, 73, 25, 39, 10 ];
var result = ShellSort(arr);
console.log(result);

5.快速排序

function QuickSort(array) {
  var length = array.length;
  if (length <= 1) {
    return array;
  } else {
    var smaller = [];
    var bigger = [];
    var base = [array[0]];
    for (var i = 1; i < length; i++) {
      if (array[i] <= base[0]) {
        smaller.push(array[i]);
      } else {
        bigger.push(array[i]);
      }
    }
    console.log(smaller.concat(base.concat(bigger)));
    console.log("-----------------------");
    return QuickSort(smaller).concat(base.concat(QuickSort(bigger)));
  }
}
 
 
var arr = [ 8, 10, 100, 90, 65, 5, 4, 10, 2, 4 ];
var result = QuickSort(arr);
console.log(result);

6.归并排序

function MergeSort(array) {
  var length = array.length;
  if (length <= 1) {
    return array;
  } else {
    var num = Math.ceil(length/2);
    var left = MergeSort(array.slice(0, num));
    var right = MergeSort(array.slice(num, length));
    return merge(left, right);
  }
}
 
function merge(left, right) {
  console.log(left);
  console.log(right);
  var a = new Array();
  while (left.length > 0 && right.length > 0) {
    if (left[0] <= right[0]) {
      var temp = left.shift();
      a.push(temp);
    } else {
      var temp = right.shift();
      a.push(temp);
    }
  }
  if (left.length > 0) {
    a = a.concat(left);
  }
  if (right.length > 0) {
    a = a.concat(right);
  }
  console.log(a);
  console.log("-----------------------------");
  return a;
}
 
var arr = [ 13, 14, 94, 33, 82, 25, 59, 94, 65, 23, 45, 27, 73, 25, 39, 10 ];
var result = MergeSort(arr);
console.log(result);

7.堆排序

/*
维护最大堆性质
@param arr 数组
@param index 元素下标
@param heapSize 堆大小
*/
function maxHeap(arr, index, heapSize){
  var tem = index; //记录入参元素下标
  var leftChildIndex = 2 *i ndex + 1; //元素的左子树的元素下标
  var rightChildeIndex = 2 * index + 2;//元素的右子树的元素下标
  if( leftChildIndex < heapSize && arr[leftChildIndex] > arr[index]){
    index = leftChildIndex;
  }
  if( rightChildeIndex < heapSize && arr[rightChildeIndex] > arr[index]){
    index = rightChildeIndex;
  }
  if(index != tem){
    var t = arr[tem];
    arr[tem] = arr[index];
    arr[index] = t;
    maxHeap(arr, index, heapSize);
  }
}
/*
构建最大堆
@param arr 数组
*/
function buildMaxHeap(arr){
  var lastFather = Math.floor(arr.length / 2) - 1;//堆的最后一个父节点
  for(var i = lastFather; i > 0; i --){
    maxHeap(arr, i, arr.length);
  }
}
/*
堆排序
@param arr 待排序数组
*/
function heapSort(arr){
  var len = arr.length;
  var tem;
  buildMaxHeap(arr);
  for(var i = len - 1; i > 0; i --){
    tem = arr[i];
    arr[i] = arr[0];
    arr[0] = tem;
    maxHeap(arr, 0 , --len);
  }
  return arr;
}

动画展示

使用html+css

给下面这些元素添加高亮,通过js实现动态,形成展示动画

  • 每趟的关键目标元素target
  • 正在比较的元素checked
  • 经过比较,需要与target元素进行交换的checked元素

如何将算法用动画展示

使用html+css实现排序动画,就是把排序的每一步的状态同步到dom上。

有三种思路

  1. 让js暂停,排序代码走一步,html刷新一次。
  2. 使用回调函数加定时器。
  3. 创建数组将排序过程的每一步保存下来,然后逐步复现到dom.

一上来,我们可能回想,那就排一步,展示一步呗。但是,排一步,展示一步就等于是让js引擎走一步,然后暂停js引擎,再让渲染引擎走一步,如此循环往复直到结束。但是js引擎开弓没有回头箭,我们还没有一个方法,可以让它先停下来歇一歇。

有的同学可能说,用定时器,但是,往深处一想就会发现定时器是不行的,它有问题:

  • js引擎单线程,导致使用定时器的结果跟我们期望的不同。
  • 作用域问题

如果使用定时器,必然要大量使用,因为每一步dom操作,都需要一个定时器,在设计过程解决上边的问题,肯定一头包。

所以,我们要换一个思路

我们都知道,动画其实就是一帧一帧的静态画面,也就是一个个状态的变换。那么我们其实可以在对纯数组进行排序的过程中,记录下我们需要的所有帧,然后等排序结束后,对我们收集到的所有帧,进行从头到尾的展示

三.程序设计-使用定时器实现预期效果

对应于算法分析的第一种方式,即排序走一步,html刷新一次,再加上定时器,实现对排序速度的控制。

缺陷:

1.排序过程中,控制面板会被锁死。因为排序的动画过程无法停下(也并不是完全无法停下,只是太麻烦并且会使程序变得异常繁琐)。

2.由于js的单线程引擎设计,一次只能实现一种算法的排序,无法做到各种排序算法在一个页面同时进行,从而更直观的比较比较排序速度的效果。

流程图:

数据可视化设计pdf电子书 数据可视化课程设计_js_05

排序函数(以冒泡排序为例)

冒泡排序的实现前面已经详细讲过,这里不多赘述。我们主要来看排序内部是如何增加绘图过程的。

var bubbleSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        for(let i=1;i<out.length;i++){
            for(let j=0;j<out.length-i;j++){
                if(typeof showSort === "function"){
                    showSort(out,j,j+1);
                }
                if(order*out[j] > order*out[j+1]){
                    let tem = out[j];
                    out[j] = out[j+1];
                    out[j+1] = tem;
                }
                if(typeof showSort === "function"){
                    showSort(out,null,null);
                }
            }
        }
        //console.log(out);
        return out;
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4hmqRbgH-1591885120165)(file://C:/Users/Guolicheng/Desktop/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E5%8F%AF%E8%A7%86%E5%8C%96/images/image-20200604121854849.png?lastModify=1591884426)]

动画绘制函数

//延时动画
var showSort = function(arr,m,n){
    var array = [];
    for(let i=0;i<arr.length;i++){
        array[i] = arr[i];
    }
    var callback = function(array,m,n){
        animation_flag = true;
        showArr(array,m,n);
        deley_count++;
        if(deley_count>=show_count){
            setTimeout(function(){
                showArr(array);
                animation_flag = false;//动画执行完毕
                console.log("排序完成");
                show_count=0;
                deley_count=0;
            },deley_space);
        }
    }
    setTimeout(callback,deley_space*show_count++,array,m,n);
}

showSort()是动画绘制的逻辑,即什么时候绘制动画,什么时候停止。通过定时器setTimeout()实现绘制的速度控制。而绘制的实际过程调用函数showArr()实现。

下面我们来看showArr()

//将数组用SVG显示数组
var showArr = function(arr,m,n){
    var svg = document.getElementById("svg");
    var rectStr = "",textStr = "",lineStr = "";//矩形、文本和线的HTML字符串
    var height = svg.getAttribute("height"),width = svg.getAttribute("width");//画布宽高
    var rectWidth = 0, rectHeight = 0, spaceWidth = 0;//矩形宽度、间隔宽度
    var margin_level = 20,margin_veticle = 40;//水平、垂直边距
    var maxValue = 0;
    rectWidth = (width-margin_level*2)/(arr.length)*0.6;
    spaceWidth = rectWidth*2/3;
    svg.innerHTML = "";

    var getMax = function(arr){
        var max = 0;
        for(let i=0;i<arr.length;i++){
            if(max < arr[i]){
                max = arr[i];
            }
        }
        return max;
    }
    var getHeight = function(h){
        return (height-2*margin_veticle)*(h/maxValue);
    }
    maxValue = getMax(arr);
    
    //画线和数字
    for(let i=0;i<arr.length;i++){
        let cx = 0, cy = 0;//当前结点的定位像素坐标
        let color = "#3C3C3C";
        rectHeight = getHeight(arr[i]);
        cx = i * (spaceWidth + rectWidth) + margin_level;
        cy = height - rectHeight - margin_veticle;

        if(i===m){
            color = "#CD0000";
        }else if(i===n){
            color = "#39E939";
        }
        if(i===m&&i===n){
            color = "#3F3FE2";
        }
        var font_size = rectWidth/2>20?20:rectWidth/2
        rectStr += '<rect x="'+cx+'" y="'+cy+'" width="'+rectWidth+'" height="'+rectHeight+'" fill="'+color+'"/>';
        //lineStr += '<line x1="'+startcx+'" y1="'+startcy+'" x2="'+endcx+
        //  '" y2="'+endcy+'" style="stroke:#999;stroke-width:2" />';

        textStr += '<text x="'+(cx+rectWidth/2)+'" y="'+(cy-6)+'" fill="#171717"  style="font-size:'+font_size+'pt;text-anchor: middle">'+arr[i]+'</text>';
        textStr += '<text x="'+(cx+rectWidth/2)+'" y="'+(height-25+font_size)+'" fill="#171717"  style="font-size:'+font_size+'pt;text-anchor: middle">'+(i+1)+'</text>';
    }
    lineStr += '<line x1="0" y1="0" x2="0" y2="'+(height-30)+'" style="stroke:#666;stroke-width:4" />'
    +'<line x1="0" y1="'+(height-margin_veticle+10)+'" x2="'+width+'" y2="'+(height-margin_veticle+10)+'" style="stroke:#666;stroke-width:2" />';
    svg.innerHTML = lineStr+rectStr+textStr;
}

没什么好说的,都是js原生代码。本来准备用D3库来做,前面也确实是这么准备的,但是时间比较仓促,来不及熟悉D3,干脆就用原生代码了。

完整代码

u1s1,我水平不行,代码估计不怎么漂亮(各种意义上的不漂亮)。贴在这里图一乐,真正要学,还是得看大佬。

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>VisualSorting</title>
    <link href="xin.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="container">
    <div class="control-panel">

        <div class="panel-header">
            <h3>VisualSorting</h3>
            <p>作者:心</p>
        </div>
        <div class="panel-array">
            <div class="panel-size">
                <label>ArraySize</label> 
                <input id="array-size" type="number" value="10">
            </div>
            <div class="panel-set">
                <label>SetArray</label>
                <input id="setArray" type="button" value="Set!">
            </div>  
        </div>
        <div class="panel-selection">
            <div class="panel-order">
                <label>SortOrder</label>
                <select id="order">
                    <option>升序</option>
                    <option>降序</option>
                </select>
            </div>
            <div class="panel-select">
                <label>SortTypes</label>
                <select id="select">
                    <option>冒泡排序</option>
                    <option>直接插入排序</option>
                    <option>希尔排序</option>
                    <option>快速排序</option>
                    <option>选择排序</option>
                    <option>堆排序</option>
                    <option>归并排序</option>
                    <option>桶排序</option>
                    <option>基数排序</option>
                </select>
            </div> 
            <div class="panel-delay">
                <label>Delay</label>
                <input id="delay" type="range" value="200" min="1" max="1000">  
            </div>
            <div class="panel-run">
                <!-- <label>Run!</label> -->
                <input id="runSort" type="button" value="Run!">
            </div>
        </div>
        
    </div>
    <div class="animation">
        <svg class="svg" id="svg" width="800" height="500"></svg>
    </div>
</div>


<script type="text/javascript" src="sort.js"></script>
</body>
</html>
.container{
    width: 100%;
    margin: 0 auto;
}
.control-panel{
    /* writing-mode: vertical-lr; */
    float: left; 
    width: 200px;
    margin-left: 30px;
    margin-top: 30px;
    padding: 10px;
    border: 5px solid gray; 
}

.panel-array{
    /* writing-mode: horizontal-tb; */
    float: left;
    margin-top: 10px;
}

.panel-size{
    float: left;
    /* margin-top: 10px; */
}

.panel-set{
    float: left;
    margin-top: 10px;
}

.panel-selection{
    /* writing-mode: horizontal-tb; */
    float: left;
    margin-top: 10px;
    margin-bottom: 10px;
}

.panel-order{
    float: left;
    margin-top: 10px;
}

.panel-select{
    float: left;
    margin-top: 10px;
}

.panel-delay{
    float: left;
    margin-top: 10px;
}

.panel-run{
    float: left;
    margin-top: 50px;
}

.animation{
    float: left;
    margin-left: 100px;
    margin-top: 100px;
}

.svg{
    position: relative;
}

input[type="number"]{
    width: 60px;
}
"use strict";//严格模式
(function(){
var animation_flag = false;//正在执行动画
//计算加入事件队列的时刻,与执行延时函数的次数,相等时表示动画执行完毕
var show_count = 0;
var deley_count = 0;
var deley_space = 200;//动画延时间隔
var select = document.getElementById("select");
var button = document.getElementById("runSort");
var orderSelect = document.getElementById("order");
var getArrDom = document.getElementById("setArray");
var numberDom = document.getElementById("array-size");
var deleyDom = document.getElementById("delay");

//随机生成指定长度,固定区间的数组
var random = function(n,max,min){
    var arr = [];
    if(typeof max !== "number"||max<100){
        max = 100;
    }
    if(typeof min !== "number"){
        min = 0;
    }
    for(let i=0;i<n;i++){
        arr[i] = parseInt(Math.random()*(max-min+1))+min;
    }
    return arr;
}
//将数组用SVG显示数组
var showArr = function(arr,m,n){
    var svg = document.getElementById("svg");
    var rectStr = "",textStr = "",lineStr = "";//矩形、文本和线的HTML字符串
    var height = svg.getAttribute("height"),width = svg.getAttribute("width");//画布宽高
    var rectWidth = 0, rectHeight = 0, spaceWidth = 0;//矩形宽度、间隔宽度
    var margin_level = 20,margin_veticle = 40;//水平、垂直边距
    var maxValue = 0;
    rectWidth = (width-margin_level*2)/(arr.length)*0.6;
    spaceWidth = rectWidth*2/3;
    svg.innerHTML = "";

    var getMax = function(arr){
        var max = 0;
        for(let i=0;i<arr.length;i++){
            if(max < arr[i]){
                max = arr[i];
            }
        }
        return max;
    }
    var getHeight = function(h){
        return (height-2*margin_veticle)*(h/maxValue);
    }
    maxValue = getMax(arr);
    
    //画线和数字
    for(let i=0;i<arr.length;i++){
        let cx = 0, cy = 0;//当前结点的定位像素坐标
        let color = "#3C3C3C";
        rectHeight = getHeight(arr[i]);
        cx = i * (spaceWidth + rectWidth) + margin_level;
        cy = height - rectHeight - margin_veticle;

        if(i===m){
            color = "#CD0000";
        }else if(i===n){
            color = "#39E939";
        }
        if(i===m&&i===n){
            color = "#3F3FE2";
        }
        var font_size = rectWidth/2>20?20:rectWidth/2
        rectStr += '<rect x="'+cx+'" y="'+cy+'" width="'+rectWidth+'" height="'+rectHeight+'" fill="'+color+'"/>';
        //lineStr += '<line x1="'+startcx+'" y1="'+startcy+'" x2="'+endcx+
        //  '" y2="'+endcy+'" style="stroke:#999;stroke-width:2" />';

        textStr += '<text x="'+(cx+rectWidth/2)+'" y="'+(cy-6)+'" fill="#171717"  style="font-size:'+font_size+'pt;text-anchor: middle">'+arr[i]+'</text>';
        textStr += '<text x="'+(cx+rectWidth/2)+'" y="'+(height-25+font_size)+'" fill="#171717"  style="font-size:'+font_size+'pt;text-anchor: middle">'+(i+1)+'</text>';
    }
    lineStr += '<line x1="0" y1="0" x2="0" y2="'+(height-30)+'" style="stroke:#666;stroke-width:4" />'
    +'<line x1="0" y1="'+(height-margin_veticle+10)+'" x2="'+width+'" y2="'+(height-margin_veticle+10)+'" style="stroke:#666;stroke-width:2" />';
    svg.innerHTML = lineStr+rectStr+textStr;
}
//延时动画
var showSort = function(arr,m,n){
    var array = [];
    for(let i=0;i<arr.length;i++){
        array[i] = arr[i];
    }
    var callback = function(array,m,n){
        animation_flag = true;
        showArr(array,m,n);
        deley_count++;
        if(deley_count>=show_count){
            setTimeout(function(){
                showArr(array);
                animation_flag = false;//动画执行完毕
                console.log("排序完成");
                show_count=0;
                deley_count=0;
            },deley_space);
        }
    }
    setTimeout(callback,deley_space*show_count++,array,m,n);
}
//各种排序算法
var Sort = function(){
    //判断是否为数组
    var isArray = function(arr){
        if(Object.prototype.toString.call(arr) === "[object Array]"){
            return true;
        }else{
            return false;
        }
    }
    //冒泡排序, arr:Array type:Boolean(true为升序,false为降序),默认为升序,showSort:排序可视化
    var bubbleSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        for(let i=1;i<out.length;i++){
            for(let j=0;j<out.length-i;j++){
                if(typeof showSort === "function"){
                    showSort(out,j,j+1);
                }
                if(order*out[j] > order*out[j+1]){
                    let tem = out[j];
                    out[j] = out[j+1];
                    out[j+1] = tem;
                }
                if(typeof showSort === "function"){
                    showSort(out,null,null);
                }
            }
        }
        //console.log(out);
        return out;
    }
    //插入排序
    var insertSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        for(let i=1;i<out.length;i++){
            let temp = out[i];
            for(var j=i-1;j>=0;j--){
                if(order*out[j]>order*temp){
                    out[j+1] = out[j];
                    out[j] = temp;
                    if(typeof showSort === "function"){
                        showSort(out,j,null);
                    }
                }else{
                    break;//找到比temp小的则跳出循环
                }
            }
            out[j+1] = temp;//在比temp小的值后面插入temp值
            if(typeof showSort === "function"){
                showSort(out,j+1,j+1);
            }
        }
        return out;
    }
    //希尔排序
    var shellSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        var half = parseInt(out.length/2);
        for(let d=half;d>=1;d=parseInt(d/2) ){
            for(let i=d;i<out.length;i++){
                for(let j=i-d;j>=0;j-=d){
                    if(typeof showSort === "function"){
                        showSort(out,j,j+d);
                    }
                    if(order*out[j+d] < order*out[j]){
                        let tem = out[j+d];
                        out[j+d] = out[j];
                        out[j] = tem;
                    }
                    if(typeof showSort === "function"){
                        showSort(out,null,null);
                    }
                }
            }
        }
        return out;
    }
    //快速排序
    var quickSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        //快速排序
        var quick = function(out,first,end){
            if(first<end){
                let i=first, j=end, temp=0;
                //一个循环完成一趟扫描
                while(i<j){
                    while(i<j&& order*out[i]< order*out[j]){
                        if(typeof showSort === "function"){
                            showSort(out,i,j);
                        }
                        j--;
                    }
                    if(i<j){
                        if(typeof showSort === "function"){
                            showSort(out,i,j);
                        }
                        temp = out[i];
                        out[i] = out[j];
                        out[j] = temp;
                        if(typeof showSort === "function"){
                            showSort(out,null,null);
                        }
                        i++;
                    }
                    while(i<j&& order*out[i]<order*out[j]){
                        if(typeof showSort === "function"){
                            showSort(out,j,i);
                        }
                        i++;
                    }
                    if(i<j){
                        if(typeof showSort === "function"){
                            showSort(out,j,i);
                        }
                        temp = out[i];
                        out[i] = out[j];
                        out[j] = temp;
                        if(typeof showSort === "function"){
                            showSort(out,null,null);
                        }
                        j--;
                    }
                }
                if(typeof showSort === "function"){
                    showSort(out,i,i);
                }
                quick(out,first,i-1);
                quick(out,i+1,end);
            }
            return out;
        }
        
        return quick(out,0,out.length-1);
    }
    //选择排序
    var selectSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        for(let i=0;i<out.length;i++){
            let index = i;
            for(let j=i;j<out.length;j++){
                if(typeof showSort === "function"){
                    showSort(out,index,j);
                }
                if(order*out[j]<order*out[index]){
                    index = j;
                }
            }
            let temp = out[i];
            out[i] = out[index];
            out[index] = temp;
            if(typeof showSort === "function"){
                showSort(out,null,null);
            }
        }
        
        return out;
    }
    //堆排序
    var heapSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        var len = out.length;
        //建立堆
        var sift = function(out, k, m){
            let i = k, j = 2*k+1;
            while(j <= m && j!=len){
                if(j<m && out[j+1] && order*out[j]<order*out[j+1]){
                    j++;
                }
                if(order*out[i] > order*out[j]){
                    break;
                }else{
                    if(typeof showSort === "function"){
                        showSort(out,i,j);
                    }
                    let temp = out[i];
                    out[i] = out[j];
                    out[j] = temp;
                    if(typeof showSort === "function"){
                        showSort(out,null,null);
                    }
                    i = j;
                    j = 2*i+1;
                }
            }
        }
        
        let half = parseInt(len/2);
        //初始建堆
        for(let i=half-1;i>=0;i--){
            sift(out, i, len);
        }
        for(let i=0;i<len-1;i++){
            if(typeof showSort === "function"){
                showSort(out,0,len-1-i);
            }
            let temp = out[0];
            out[0] = out[len-1-i];
            out[len-1-i] = temp;
            if(typeof showSort === "function"){
                showSort(out,null,null);
            }
            sift(out, 0, len-1-i-1);
        }
        
        return out;
    }
    //归并排序
    var mergeSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        var order = type!==true?1:-1;
        //一次归并算法
        var merge = function(out, array, s, m, t){
            let i=s, j=m+1, k=s;//i:左边数组的索引,j:右边数组的索引,k:归并结果数组的索引
            //没有数组遍历完
            while(i<=m && j<=t){
                if(order*out[i]<order*out[j]){
                    if(typeof showSort === "function"){
                        showSort(array,i,j);
                    }
                    array[k++] = out[i++];
                    if(typeof showSort === "function"){
                        showSort(array,k-1,k-1);
                    }
                }else{
                    if(typeof showSort === "function"){
                        showSort(array,i,j);
                    }
                    array[k++] = out[j++];
                    if(typeof showSort === "function"){
                        showSort(array,k-1,k-1);
                    }
                }
            }
            //如果左数组没有遍历完,将左数组剩余数据压入arr
            if(i<=m){
                while(i<=m){
                    if(typeof showSort === "function"){
                        showSort(array,null,i);
                    }
                    array[k++] = out[i++];
                    if(typeof showSort === "function"){
                        showSort(array,null,null);
                    }
                }
            }else{
                while(j<=t){
                    if(typeof showSort === "function"){
                        showSort(array,null,j);
                    }
                    array[k++] = out[j++];
                    if(typeof showSort === "function"){
                        showSort(array,k-1,k-1);
                    }
                }
            }
            //console.log(arr);
            return array;
        }
        //一趟归并排序算法
        var mergePass = function(out, array, h){
            var len = out.length;
            let i = 0;
            while(i+2*h<=len){
                merge(out, array, i , i+h-1, i+2*h-1);
                i += 2*h;
            }
            if(i+h<len){
                merge(out, array, i, i+h-1, len-1);
            }else{
                while(i<len){
                    array[i] = out[i];
                    i++;
                }
            }
            //console.log(arr);
            return array;
        }
        //非递归归并排序
        var mergeSortUnrecursion = function(out){
            var len = out.length;
            var array = [];
            for(let i=0;i<arr.length;i++){
                array[i] = out[i];
            }
            var h = 1;
            while(h<len){
                mergePass(out, array, h);
                h = 2*h;
                mergePass(array, out, h);
                h = 2*h;
            }
            //console.log(out);
            return out;
        }
        //递归归并排序
        var mergeSortRecursion = function(out,array, s, t){
            if(s === t){
                array[s] = out[s];
            }else{
                let m = parseInt((s+t)/2);
                mergeSortRecursion(out, array, s, m);
                mergeSortRecursion(out, array, m+1, t);
                merge(array, out, s, m, t);
                //将out复制给array,继续下一轮归并
                for(let i=0;i<out.length;i++){
                    array[i] = out[i];
                }
            }
            return out;
        }
        var array = [];
        return mergeSortUnrecursion(out,array, 0, arr.length-1);
    }
    //桶排序
    var bucketSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        var len = out.length;
        
        var bucket = [], q = [];
        for(let i=0;i<len;i++){
            let m = out[i];
            if(bucket[m] === undefined){
                bucket[m] = 1;
            }else{
                bucket[m]++;
            }
        }
        //out数组索引
        out.splice(0,out.length);
        var index = 0;
        if(type!==true){
            //升序
            for(let i=0;i<bucket.length;i++){
                let temp = bucket[i];
                while(temp>=1){
                    out[index++] = i;
                    if(typeof showSort === "function"){
                        showSort(out,null,null);
                    }
                    temp--;
                }
            }
        }else{
            //降序
            for(let i=bucket.length-1;i>=0;i--){
                let temp = bucket[i];
                while(temp>=1){
                    out[index++] = i;
                    if(typeof showSort === "function"){
                        showSort(out,null,null);
                    }
                    temp--;
                }
            }
        }

        
        return out;
    }
    //基数排序
    var radixSort = function(arr,type,showSort){
        //输入检测
        if(!isArray(arr)){
            throw Error("the input of bubbleSort is not a array");
            return;
        }
        //深拷贝,不改动输入数组
        var out = [];
        for(let i=0;i<arr.length;i++){
            out[i] = arr[i];
        }
        //order==false为升序,否则为降序
        var order = type!==true?1:-1;
        var len = out.length;
        //求所有数中最大的
        var max = 0;
        for(let i=0;i<len;i++){
            if(out[i]>max){
                max = out[i];
            }
        }
        //计算所有数中最大的是几位数
        var max_pow = 1;
        while(max>=10){
            max_pow++;
            max = parseInt(max/10);
        }
        //升序,分配
        var distributeUp = function(out, queue, pow){
            queue.splice(0,queue.length);
            for(let i=0;i<len;i++){
                let m = parseInt(out[i]/pow)%10;
                if(Object.prototype.toString.call(queue[m]) !== "[object Array]"){
                    queue[m] = [];
                }
                queue[m].push(out[i]);
            }
        }
        //升序,收集
        var collectUp = function(out, queue){
            out.splice(0,out.length);
            for(let i=0;i<10;i++){
                while(queue[i]!==undefined && queue[i].length>0){
                    out.push(queue[i].shift());
                }
            }
        }
        //降序,分配
        var distributeDown = function(out, queue, pow){
            queue.splice(0,queue.length);
            for(let i=len-1;i>=0;i--){
                let m = parseInt(out[i]/pow)%10;
                if(Object.prototype.toString.call(queue[m]) !== "[object Array]"){
                    queue[m] = [];
                }
                queue[m].push(out[i]);
            }
        }
        //降序,收集
        var collectDown = function(out, queue){
            out.splice(0,out.length);
            for(let i=9;i>=0;i--){
                while(queue[i]!==undefined && queue[i].length>0){
                    out.push(queue[i].pop());
                }
            }
        }

        var queue = [];
        if(type!==true){
            //升序
            for(let i=0;i<max_pow;i++){
                distributeUp(out, queue, Math.pow(10,i));
                collectUp(out, queue);
            }
        }else{
            //降序
            for(let i=0;i<max_pow+1;i++){
                distributeDown(out, queue, Math.pow(10,i));
                collectDown(out, queue);
            }
        }
        if(typeof showSort === "function"){
            showSort(out,null,null);
        }
        
        return out;
    }
    var obj = {
        bubbleSort: bubbleSort,
        insertSort: insertSort,
        shellSort: shellSort,
        quickSort: quickSort,
        selectSort: selectSort,
        heapSort: heapSort,
        mergeSort: mergeSort,
        bucketSort: bucketSort,
        radixSort: radixSort
    }
    return obj;
}
//初始化函数
var init = function(){
    //实例化排序对象
    var sort = new Sort();
    var arr = [];
    var number = Number(numberDom.value.trim());
    if(number!==NaN&&number>0){
        arr = random(number,number,1);
        showArr(arr);
    }else{
        arr = random(10,100,1);//随机生成待排序的数组
        //刷新显示初始数组
        showArr(arr);
    }
    arr = [2,8,7,4,1,5];
    console.log("待排序的数组",arr);
    showArr(arr);

    //随机生成指定长度的数组
    getArrDom.addEventListener("click",function(){
        if(animation_flag===true){
            return;
        }
        var number = Number(numberDom.value.trim());
        if(number!==NaN&&number>0){
            arr = random(number,number,1);
            console.log("随机生成的数组:",arr);
            showArr(arr);
        }else{
            alert("请输入正确的数组长度");
        }
    });
    //点击开始排序
    button.addEventListener("click",function(){
        //在动画中点击无效
        if(animation_flag===true){
            return;
        }
        deley_space = Number(deleyDom.value.trim());//输入的动画时间间隔,控制动画快慢(单位ms)
        var order = orderSelect.selectedIndex===0?false:true;//选择的排序顺序
        var index = select.selectedIndex;//选择的排序方法
        var traversalArr = [];
        //按选择的排序方式和顺序排序
        switch(index){
            case 0: traversalArr = sort.bubbleSort(arr,order,showSort);
                    console.log("冒泡排序:",traversalArr);
                    break;
            case 1: traversalArr = sort.insertSort(arr,order,showSort);
                    console.log("插入排序:",traversalArr);
                    break;
            case 2: traversalArr = sort.shellSort(arr,order,showSort);
                    console.log("希尔排序:",traversalArr);
                    break;
            case 3: traversalArr = sort.quickSort(arr,order,showSort);
                    console.log("快速排序:",traversalArr);
                    break;
            case 4: traversalArr = sort.selectSort(arr,order,showSort);
                    console.log("选择排序:",traversalArr);
                    break;
            case 5: traversalArr = sort.heapSort(arr,order,showSort);
                    console.log("堆排序:",traversalArr);
                    break;
            case 6: traversalArr = sort.mergeSort(arr,order,showSort);
                    console.log("归并排序:",traversalArr);
                    break;
            case 7: traversalArr = sort.bucketSort(arr,order,showSort);
                    console.log("桶排序:",traversalArr);
                    break;
            case 8: traversalArr = sort.radixSort(arr,order,showSort);
                    console.log("基数排序:",traversalArr);
                    break;
            default: //alert("选择遍历方式出错");
                    break;
        }   
    });
}
//初始化
init();

})();