稳定性
它只表示两个值相同的元素在排序前后是否有位置变化。如果前后位置变化,则排序算法是不稳定的,否则是稳定的
快排、堆排是不稳定的 合并、基数是稳定的
快排
var sortArray = function(nums) { function quickSort(array, start = 0, end = array.length - 1) { if (start < end) { let pivot = divide(array, start, end); quickSort(array, start, pivot - 1); quickSort(array, pivot + 1, end); } return array; }
function divide(array, start, end) { let mid = (start + end) >> 1; [array[start], array[mid]] = [array[mid], array[start]]; let pivot = start; let low = start + 1; for (let high = low; high <= end; high++) { if (array[high] < array[pivot]) { [array[low], array[high]] = [array[high], array[low]]; low++; } } [array[pivot], array[low - 1]] = [array[low - 1], array[pivot]]; return low - 1; } return quickSort(nums); };
|
归并
function sort(arr, startIndex = 0, endIndex = arr.length - 1) { if (startIndex >= endIndex) { return; }
let midIndex = parseInt((startIndex + endIndex) / 2); sort(arr, startIndex, midIndex); sort(arr, midIndex + 1, endIndex); merge(arr, startIndex, midIndex, endIndex); }
function merge(arr, startIndex, midIndex, endIndex) { let tempArr = []; let p1 = startIndex; let p2 = midIndex + 1; let p = 0;
while (p1 <= midIndex && p2 <= endIndex) { if (arr[p1] <= arr[p2]) { tempArr[p++] = arr[p1++]; } else { tempArr[p++] = arr[p2++]; } }
while (p1 <= midIndex) { tempArr[p++] = arr[p1++]; } while (p2 <= endIndex) { tempArr[p++] = arr[p2++]; }
for (let i = 0; i < tempArr.length; i++) { arr[i + startIndex] = tempArr[i]; } }
|
堆排序
建立堆顺序 -从下往上
根据大顶堆的性质,每个节点的值都大于或者等于它的左右子节点的值。所以我们需要找到所有包含子节点的节点,也就是非叶子节点,然后调整他们的父子关系,非叶子节点遍历的顺序应该是从下往上,这比从上往下的顺序遍历次数少很多,因为,大顶堆的性质要求父节点的值要大于或者等于子节点的值,如果从上往下遍历,当某个节点即是父节点又是子节点并且它的子节点仍然有子节点的时候,因为子节点还没有遍历到,所以子节点不符合大顶堆性质,当子节点调整后,必然会影响其父节点需要二次调整。但是从下往上的方式不需要考虑父节点,因为当前节点调整完之后,当前节点必然比它的所有子节点都大,所以,只会影响到子节点二次调整。相比之下,从下往上的遍历方式比从上往下的方式少了父节点的二次调整。
(类似自己去堆一堆沙子,肯定是从下面往上更便捷,否则你需要架子架住)
(21条消息) 十大经典排序算法-堆排序算法详解_小小学编程的博客-CSDN博客
最后一个非叶子节点的位置-arr.length / 2 -1
对于一个完全二叉树,在填满的情况下(非叶子节点都有两个子节点),每一层的元素个数是上一层的二倍,根节点数量是1,所以最后一层的节点数量,一定是之前所有层节点总数+1,所以,我们能找到最后一层的第一个节点的索引,即节点总数/2(根节点索引为0),这也就是第一个叶子节点,所以第一个非叶子节点的索引就是第一个叶子结点的索引-1。那么对于填不满的二叉树呢?这个计算方式仍然适用,当我们从上往下,从左往右填充二叉树的过程中,第一个叶子节点,一定是序列长度/2,所以第一个非叶子节点的索引就是arr.length / 2 -1。
(21条消息) 十大经典排序算法-堆排序算法详解_小小学编程的博客-CSDN博客
左孩子节点 -2*i+1
function downAdjust(arr, parentIndex, length) { let temp = arr[parentIndex]; let childIndex = 2 * parentIndex + 1;
while (childIndex < length) { if (childIndex + 1 < length && arr[childIndex + 1] > arr[childIndex]) { childIndex++; }
if (temp >= arr[childIndex]) { break; }
arr[parentIndex] = arr[childIndex]; parentIndex = childIndex; childIndex = 2 * parentIndex + 1; } arr[parentIndex] = temp; }
function sort(arr) { for (let i = parseInt(arr.length / 2) - 1; i >= 0; i--) { downAdjust(arr, i, arr.length); } console.log(arr);
for (let i = arr.length - 1; i > 0; i--) { [arr[0], arr[i]] = [arr[i], arr[0]]; downAdjust(arr, 0, i); } }
|