排序算法记录
前言:看过好大佬写的排序,终究是写了忘忘了写,发现过一段时间还是会忘记,记录在这里,忘了可以常看看~~有大哥发现是自己的代码,留言call我,加引用
.js文件
const arr = [91, 60, 96, 7, 35, 65, 10, 65, 9, 30, 20, 31, 77, 81, 24];
// 常用函数,数组元素换位置
function swap(arr, q, w) {
return [arr[q], arr[w]] = [arr[w], arr[q]]
}
1.冒泡排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>冒泡排序</title>
</head>
<body>
</body>
</html>
<script>
// 循环两两比较,交换位置
// 冒泡排序
const arr = [91, 60, 96, 7, 35, 65, 10, 65, 9, 30, 20, 31, 77, 81, 24]
// 常用函数,数组元素换位置
function swap(arr, q, w) {
return [arr[q], arr[w]] = [arr[w], arr[q]]
}
// 基本版本
function bubbleSort(arr) {
const len = arr.length - 1
for (let i = len; i > 0; i--) {
for (let j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1)
}
}
}
return arr
}
// 设置缓存
// 设置一标志性变量 pos,用于记录每趟排序中最后一次进行交换的位置。 由于 pos 位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到 pos 位置即可。
function bubbleSort2(arr) {
let i = arr.length - 1
while (i > 0) {
let pos = 0
for (let j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
pos = j
swap(arr, j, j + 1)
}
}
i = pos
}
return arr
}
// 双向遍历
function bubbleSort3(arr) {
let start = 0
let end = arr.length - 1
while (start < end) {
for (let i = start; i < end; i++) {
if (arr[i] > arr[i+1]) {
swap(arr, i, i+1)
}
}
end -= 1
for (let i = end; i > start ; i--) {
if (arr[i] < arr[i - 1]) {
swap(arr, i-1, i)
}
}
start += 1
}
return arr
}
// 缓存加双向
function bubbleSort4(arr) {
let start = 0
let end = arr.length - 1
while (start < end) {
let posStart = 0
let posEnd = 0
for (let i = start; i < end; i++) {
if (arr[i] > arr[i+1]) {
swap(arr, i, i+1)
posEnd = i
}
}
end = posEnd
for (let i = end; i > start ; i--) {
if (arr[i] < arr[i - 1]) {
swap(arr, i-1, i)
posStart = i
}
}
start = posStart
}
return arr
}
console.log(bubbleSort4(arr))
</script>
2.选择排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>选择排序</title>
</head>
<body>
</body>
</html>
<script src="./js"></script>
<script>
// 选择一个元素挨个与数组中元素比较,每次循环选出最小的放到前面
// 最小值
function selectSort(arr) {
let len = arr.length
let minIndex = 0
if (len <= 1) {
return arr
}
for (let i = 0; i < len - 1; i++) {
minIndex = i
for (let j = i+1; j < len; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
swap(arr, i, minIndex)
}
return arr
}
// 最大值
function selectSort2(arr) {
const len = arr.length
let maxIndex = 0
if (len <= 1) {
return arr
}
for (let i = len-1; i > 0; i--) {
maxIndex = i
for (let j = i-1; j >= 0; j--) {
if (arr[j] > arr[maxIndex]) {
maxIndex = j
}
}
swap(arr, i, maxIndex)
}
return arr
}
console.log(selectSort(arr))
console.log(selectSort2(arr))
</script>
3.插入排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>插入排序</title>
</head>
<body>
</body>
<script src="./js"></script>
<script>
// 默认 a[0] 为已排序数组中的元素,从 arr[1] 开始逐渐往已排序数组中插入元素,从后往前一个个比较,如果待插入元素小于已排序元素,则已排序元素往后移动一位,
// 直到待插入元素找到合适的位置并插入已排序数组。经过 n - 1 次这样的循环插入后排序完毕。
function insertSort(arr) {
const len = arr.length
if (len <= 1) {
return arr
}
for (let i = 1; i < len; i++) {
for (let j = i; j > 0; j--) {
if (arr[j] < arr[j-1]) {
swap(arr, j-1, j)
}
}
}
return [...arr]
}
function insert_sort(arr) {
let count = 0
for (let j = arr.length; j > 0; j--) {
for (let i = j; i > 0; i--) {
if (arr[i] < arr[i - 1]) {
[arr[i], arr[i - 1]] = [arr[i - 1], arr[i]]
count++
}
}
if (count === 0) {
return
}
}
return arr
}
let arr1 = [7, 4, 3, 67, 34, 1, 8]
let arr2 = [ 1, 3, 4, 7, 8, 34, 67 ]
insert_sort(arr1) // [ 1, 3, 4, 7, 8, 34, 67 ] 时间复杂度:O(n^2)
insert_sort(arr2) // [ 1, 3, 4, 7, 8, 34, 67 ] 时间复杂度:O(n)
console.log(insertSort(arr))
</script>
</html>
4.希尔排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>希尔排序</title>
</head>
<body>
</body>
<script src="./js"></script>
<script>
// 插入排序升级版
function shellSort(arr) {
const len = arr.length
let temp = 0
//增量(一般第一趟取长度的一半向下取整)
let gap = Math.floor(len / 2)
let j
//每一趟过后增量会变成原来的一半并向下取整,可知最后一趟增量肯定是1
//增量为1即实现普通的插入排序,执行完毕整个排序肯定就完成了
//而最后一趟增量为1过完时,增量变成0
//gap > 0是为了控制当增量变成0时,整个排序已完成,停止排序
for (gap; gap > 0; gap = Math.floor(gap / 2)) {
//以下开始即普通的插入排序(按分好的组进行插入)
//初始i=gap是为了让第gap+1个数和第一个数相比
//i<len是从第gap+1个数开始之后的每一个数都和之前差n个增量的数相比
for (let i = gap; i < len; i++) {
//保存当前要拿来对比插入的数(比较数)
temp = arr[i]
//j=i-gap是将temp和之前差一个增量的数相比,arr[j]即被比较数
//j>=0是按增量向前比,一直比到向前减一个增量没有数 即下标j<0
//arr[j] > temp是判断被比较数是否大于比较数,
//如果大于,将被比较数向后移一个增量的位置,
//即比较数的位置 i或者j+gap
//j -= gap 每次在上次被比较数的基础上向前比差一个增量的数
for (j = i-gap; j>=0 && arr[j]>temp; j-=gap) {
//将被比较数向后移一个增量的位置,
//即比较数的位置 i或者j+gap
arr[j + gap] = arr[j]
}
arr[j + gap] = temp//插入比较数
}
}
return arr
}
console.log(shellSort(arr))
</script>
</html>
5.快速排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>快速排序</title>
</head>
<body>
</body>
</html>
<script>
/*
* 选择一个基准,根据基准分开
* 进行迭代
* */
const arr = [91, 60, 96, 7, 35, 65, 10, 65, 9, 30, 20, 31, 77, 81, 24]
const quickSort = function (arr) {
if (arr.length <= 1) {
return arr
}
const pivotIndex = Math.floor(arr.length / 2)
const pivot = arr.splice(pivotIndex, 1)[0]
const left = []
const right = []
for (let i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return [...quickSort(left), ...[pivot], ...quickSort(right)]
}
function quickSort2(arr) {
if (arr.length <= 1) {
return arr
}
const pivotIndex = Math.floor(arr.length / 2)
const pivot = arr.splice(pivotIndex, 1)[0]
const left = arr.filter(q => q < pivot)
const right = arr.filter(q => q >= pivot)
return [...quickSort2(left), ...[pivot], ...quickSort2(right)]
}
console.log(quickSort2(arr))
</script>
6.归并排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>归并排序</title>
</head>
<body>
</body>
<script src="./js"></script>
<script>
// 归并排序使用分而治之的思想,以折半的方式来递归/迭代排序元素,利用空间来换时间,做到了时间复杂度 O(n·log(n)) 的同时保持了排序
// 元素的 stable,这让它在一些更考虑排序效率和稳定性,次考虑存储空间的场合非常适用(如数据库内排序,和堆排序相比,归并
// 排序的 stable 是优点)。并且归并排序非常适合于列表排序。
function mergeSort(arr) { // recursion
const len = arr.length
const mid = Math.floor(len / 2)
let left = arr.slice(0, mid)
let right = arr.slice(mid)
if (len < 2) {
return arr
}
return merge(mergeSort(left), mergeSort(right))
}
function merge(left, right) {
let result = []
while (left.length > 0 && right.length > 0) { // sorting: small to big
result.push(left[0] <= right[0] ? left.shift() : right.shift()) // keep stable
}
return result.concat(left, right)
}
// test
let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
console.log(mergeSort(arr))
// [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
</script>
</html>
7.堆排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>堆排序</title>
</head>
<body style="width: 1200px;margin: 0 auto">
<p>
一,堆的概念
堆是一棵顺序存储的二叉树
其中每个节点的关键字都不大于其子节点的关键字,这样的堆称为小根堆
其中每个节点的关键字都不小于其自己点的关键字,这样的堆称为大根堆
下面是一个典型的小根堆:
</p>
<img src="./img/典型小根堆.jpg" alt="">
<p>
二,堆的调整
堆的调整是为了保持堆的特性而做的一个操作,对以某一个节点为根的子树做堆调整,其实就是将该根节点进行”下沉操作”(具体是通过和子节点的交换完成的),一直下沉到合适位置,使得刚才的子树满足堆的性质为止。
对大根堆的调整步骤如下:
1,在对应元素A[i],找到左孩子A[left(i)]和右孩子A[right(i)]中最大的那一个,将其下标存储在largest中。
2,如果A[i]已经是最大元素,则程序结束
3,否则,i的某个子节点为最大的元素,将A[largest]与A[i]交换
4,再从交换的子节点开始,重复1,2,3步骤,直至叶子节点,算完成一次堆调整。
做一次堆调整的时间复杂度为lgn,从二叉树的根节点到叶子结点,树的深度为lgn,也就是需要下沉的最大次数为lgn。
三,堆的建立
建堆是一个通过不断的堆调整,使得整个二叉树满足堆性质的操作。在数组中,我们一般从下标n/2的数开始作调整,一直到下标为0的数
疑问:为什么是从n/2的数开始?因为下标大于n/2的数都是叶子节点,其子树已经满足堆的性质,所以不用再调整了。下图为一个小根堆的建立图示:
</p>
<img src="./img/小根堆建立图.jpg" alt="">
<img src="./img/小根堆建立图2.jpg" alt="">
<p>
四,堆的排序
堆的排序是堆建立完成之后进行的,假设数组存储成大根堆的形式之后
第一次将A[0]与A[n-1]交换,然后将A[0~n-2]数组进行堆恢复操作(做堆调整)。
第二次将A[0]与A[n-2]交换,然后将A[0~n-3]数组进行堆恢复操作(做堆调整)。
重复这样的操作,直到A[0]与A[1]交换,排序结束。
由于每次都是将最大的数并入到后面的有序区间,所以整个数组是有序的,上述步骤产生的是升序排列。一个大根堆排序过程的示意图:
</p>
<img src="./img/大根堆排序过程示意图.jpg" alt="">
<img src="./img/大根堆排序过程示意图2.jpg" alt="">
<img src="./img/大根堆排序过程示意图3.jpg" alt="">
<img src="./img/大根堆排序过程示意图4.jpg" alt="">
<p>
五,堆排序性能分析
堆排序时间=建堆时间+堆恢复时间
建堆时间:建堆的过程就是堆不断调整的过程,前面分析过一次堆调整的时间复杂度为O(lgn),堆建立过程调整了n/2次,因此时间复杂度是O(nlgn).
堆恢复时间:堆恢复操作共进行了n-1次堆调整操作,因此,时间复杂度是O(nlgn)
综上得:堆排序的最差、最优、平均时间复杂度均是O(nlgn) 不稳定
“空间复杂度”指占内存大小,堆排序每次只对一个元素操作,是就地排序,所以空间复杂度是O(1)
</p>
</body>
<script src="./js"></script>
<script>
/**
*
*@Description:堆排序
*@param arr
*@author 肖芳
*/
function headSort(arr){
buildHeap(arr)//构建堆
let len = arr.length
for(let i=len-1;i>0;i--){
swap(arr,0,i)//交换堆的第一个元素和最后一个元素
heapify(arr,i)//调整堆
}
return arr
}
/**
*
*@Description:创建堆
*@param arr
*@author 肖芳
*/
function buildHeap(arr){
let len = arr.length
if(len === 0)
return
for(let i=Math.floor(len/2);i>0;i--){
heapify(arr,i)
}
}
/**
*
*@Description:调整堆
*@param arr 调整数组
*@param i 跟
*@author 肖芳
*/
function heapify(arr,i){
const left = 2 * i + 1
const right = 2 * i + 2
let largest = i
const len = arr.length
if(left <len && arr[left]>arr[largest]){//先判断左节点还否超出
largest=left
}
if(right <len && arr[right]>largest){//有节点是否超出 找出最大的子节点
largest=right
}
if(largest !== i){
swap(arr,i,largest)//交换 largrst为i
heapify(arr,largest)//递归调整
}
}
/**
*
*@Description:交换
*@param arr
*@author 肖芳
*/
function swap(arr,i,j){
let temp=arr[i]
arr[i] = arr[j]
arr[j] = temp
}
</script>
</html>
8.基数排序
参考网址:https://www.cnblogs.com/0603ljx/p/4379418.html

浙公网安备 33010602011771号