
使用ArkTS完成经典排序算法(插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序.) 原创
在万物互联的智能时代,华为HarmonyOS作为面向全场景的分布式操作系统,正在重塑应用开发的范式。ArkTS是华为基于TypeScript扩展的声明式开发语言,它继承了TypeScript的所有特性,同时针对HarmonyOS进行了深度优化。作为JavaScript的超集,ArkTS不仅保留了动态语言的灵活性,还通过静态类型系统大大提升了代码的可靠性和可维护性。
而算法是程序开发中不可或缺的一部分。排序算法作为最基本、最常用的算法之一,在程序开发中起到了至关重要的作用。本文将使用ArkTS实现十大经典排序算法,帮助开发者在HarmonyOS应用中高效处理数据。
一、冒泡排序
冒泡排序是一种基本的排序算法,其思路是比较相邻元素的大小,如果逆序则交换,一轮结束后将最大元素“冒泡”到数列的末尾。这个过程像冒泡一样,因此被称为冒泡排序。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
// 冒泡排序ArkTS实现
function bubbleSort(arr: number[]): number[] {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // ES6解构交换
}
}
}
return arr;
}
// 可视化组件
@Component
struct BubbleSortDemo {
@State data: number[] = [5, 3, 8, 4, 2]
@State sortedData: number[] = []
build() {
Column() {
Text('原始数组: ' + this.data.toString())
Button('执行冒泡排序')
.onClick(() => {
this.sortedData = bubbleSort([...this.data])
})
Text('排序结果: ' + this.sortedData.toString())
}
}
}
时间复杂度:
- 最好情况:已经有序,只需要进行一次比较,时间复杂度为O(n)。
- 最坏情况:完全逆序,需要进行n轮比较,时间复杂度为O(n^2)。
- 平均情况:时间复杂度也为O(n^2)。
二、选择排序
选择排序是一种简单直观的排序算法,其思想是每次从待排序的数列中选择最小(或最大)的元素,放到已排序数列的末尾(或开头),直到整个数列有序。
function insertionSort(arr: number[]): number[] {
const array = arr.slice();
for (let i = 1; i < array.length; i++) {
const current = array[i];
let j = i - 1;
while (j >= 0 && array[j] > current) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = current;
}
return array;
}
时间复杂度:
- 最好情况:O(n^2)。
- 最坏情况:O(n^2)。
- 平均情况:O(n^2)。
三、插入排序
插入排序是一种简单直观的排序算法,其基本思想是将待排序序列分为两部分,一部分已经排好序,另一部分待排序,然后将待排序部分的元素依次插入到已排序部分的适当位置,使得插入后仍然有序。
function selectionSort(arr: number[]): number[] {
const array = arr.slice();
for (let i = 0; i < array.length - 1; i++) {
let minIndex = i;
for (let j = i + 1; j < array.length; j++) {
if (array[j] < array[minIndex]) {
minIndex = j;
}
}
[array[i], array[minIndex]] = [array[minIndex], array[i]];
}
return array;
}
四、归并排序
归并排序是一种经典的分治算法,它的基本思想是将待排序的数组划分为两个子数组,分别对这两个子数组进行排序,然后将排序后的子数组合并成一个有序数组。具体来说,归并排序的步骤如下:
- 分解:将待排序的数组不断地二分,直到每个子数组只包含一个元素,即认为这些子数组已经有序。
- 合并:将已经有序的子数组两两合并,形成新的有序子数组。
- 重复步骤2,直到只剩下一个有序数组,即为最终排序结果。
简易写法:
function mergeSort(arr: number[]): number[] {
if (arr.length <= 1) return arr.slice();
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left: number[], right: number[]): number[] {
let i = 0, j = 0;
const result: number[] = [];
while (i < left.length && j < right.length) {
result.push(left[i] < right[j] ? left[i++] : right[j++]);
}
return result.concat(left.slice(i)).concat(right.slice(j));
}
归并排序的时间复杂度是O(nlogn),其中n是待排序数组的长度。虽然归并排序的时间复杂度比较稳定,但需要额外的空间来存储辅助数组,因此其空间复杂度是O(n)。
时间复杂度:假设数组长度为 n,需要进行 logn 次归并操作;每次归并操作需要 O(n) 的时间复杂度;
- 最好情况:O(n)。
- 最坏情况:O(nlogn)。
- 平均情况:O(nlogn)。
五、快速排序
快速排序采用分治策略,首先选取一个基准元素,将数组划分为左右两部分:左边元素均小于基准,右边元素均大于基准,然后递归地对左右子数组重复此过程。通过双指针从两端扫描并交换不符合条件的元素,最终确保基准处于正确位置,实现整体有序。
function quickSort(arr: number[]): number[] {
const array = arr.slice();
quickSortHelper(array, 0, array.length - 1);
return array;
}
// 快速排序辅助函数
function quickSortHelper(arr: number[], low: number, high: number): void {
if (low < high) {
const pi = partition(arr, low, high);
quickSortHelper(arr, low, pi - 1);
quickSortHelper(arr, pi + 1, high);
}
}
六、希尔排序
希尔排序是一种插入排序的变体,也称为缩小增量排序。其基本思想是将待排序的数组分成若干个子序列,对每个子序列进行插入排序,然后逐步缩小增量,直到增量为1。
具体来说,希尔排序的步骤如下:
- 选择一个增量序列:将待排序数组按照一定的间隔划分成若干个子序列,对每个子序列进行插入排序。这里的增量序列可以使用Hibbard增量序列,它的公式为$2^k-1$,其中k为增量的大小,从n/2、n/4、n/8等不断除以2,直到增量等于1为止。
- 对子序列进行插入排序:对每个子序列进行插入排序,将子序列中的数依次插入到前面已经排好序的序列中,使得插入后仍然有序。
- 逐步缩小增量:对于增量序列,逐步缩小增量,重复步骤2,直到增量为1。
function shellSort(arr: number[]): number[] {
const array = arr.slice();
let gap = Math.floor(array.length / 2);
while (gap > 0) {
for (let i = gap; i < array.length; i++) {
const temp = array[i];
let j = i;
while (j >= gap && array[j - gap] > temp) {
array[j] = array[j - gap];
j -= gap;
}
array[j] = temp;
}
gap = Math.floor(gap / 2);
}
return array;
}
七、堆排序
堆排序的实现过程主要分为两个步骤:
- 构建最大堆:将待排序的数组看作是完全二叉树的形式,从最后一个非叶子节点开始,依次对每个非叶子节点进行堆调整,使其成为最大堆的形式。具体做法是比较节点和它的两个子节点之间的大小,将较大的子节点调整到父节点的位置上,然后递归地对调整过后的子节点进行堆调整。
- 堆排序:从最大堆中取出根节点(即数组的第一个元素),将其与最后一个元素交换位置,然后对剩余的节点重新进行堆调整,以确保剩余节点仍然构成最大堆。重复这个过程,直到所有元素都被取出并排序。
堆排序相较于其他排序算法的优势在于,它是一种原地排序算法,只需要一个额外的空间来存放根节点的值,没有浪费的空间。同时,堆排序还具有稳定的时间复杂度和稳定的排序结果。不过,需要注意的是,堆排序对于小规模的数据排序并不是特别高效,适用于大规模数据的排序。
// 堆排序
function heapSort(arr: number[]): number[] {
const array = arr.slice();
let n = array.length;
// 构建最大堆
for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
heapify(array, n, i);
}
// 逐个提取元素
for (let i = n - 1; i > 0; i--) {
[array[0], array[i]] = [array[i], array[0]];
heapify(array, i, 0);
}
return array;
}
// 堆化函数
function heapify(arr: number[], n: number, i: number): void {
let largest = i;
const left = 2 * i + 1;
const right = 2 * i + 2;
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
if (largest !== i) {
[arr[i], arr[largest]] = [arr[largest], arr[i]];
heapify(arr, n, largest);
}
}
通过ArkTS实现这些经典排序算法,开发者可以在HarmonyOS应用中高效处理各种数据排序需求。ArkTS的类型系统和现代语法使得算法实现既安全又简洁,欢迎大家点赞评论!
