【算法基础(3)】快速排序和堆排序

  1. 快速排序算法通过多次比较和交换来实现排序,其排序流程如下:

首先设定一个分界值,通过该分界值将数组分成左右两部分。

将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都 小于分界值,而右边部分中各元素都大于或等于分界值。

然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。

重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

/**
* 快速排序
* @param {array}    - arr 需要排序的数组
* @returns {array}
*/
export function quickSort(arr: number[]) {
    
    
  quick(arr, 0, arr.length - 1) // 数组从0 - 结束 有序
  return arr
}
function quick(arr: number[],left:number, right:number) {
    
    
  // 数组长度大于1才排序,1个数字不需要排序
  if (arr.length > 1) {
    
    
    let index = partition(arr, left, right)
    if (left < index - 1) {
    
     // 左边 继续 做有序操作,直到递归到最后
      quick(arr, left, index - 1)
    }
    if (index < right) {
    
     // 右边 继续 做有序操作,直到递归到最后
      quick(arr, index, right)
    }
  }
}
function partition(arr: number[],left:number,right:number) {
    
    
  // 将数组以pivot 为标准 划分为俩半 局部有序
  let pivot = arr[Math.floor(left + right)/2]
  let i = left
  let j = right
  while(i <= j) {
    
    
    while (arr[i] < pivot) {
    
    
      i++
    }
    while (arr[j] > pivot){
    
    
      j--
    }
    if (i <= j) {
    
    
      swap(arr, i, j)
      i++
      j--
    }
  }
  return i // 到最后 i == j 就是pivot的下标
}
/***
这种实现思路 是利用 i j 两个指针 , 将 数组 以 pivot(枢纽) 分割成两半

左边 小于 pivot 右边大于pivot ,然后递归 分而治之 .

例如 数组 [ 3 , 6 , 5 , 7 , 4 , 1 , 8 , 2 , 9]

选取pivot : 4 , left : 3 right : 9

//left 会一直找到 >= 4 时停住

//right 会一直找到<= 4 停住

=> left =>6 , right => 2 这个时候 swap

[3,2,5,7,4,1,8,6,9] 继续循环

=>left =>5 , right =>1 调用 swap

[ 3,2,1,7,4,5,8,6,9]

=>left => 7 , right =>4 调用 swap

[3,2,1,4,7,5,8,6,9] 退出循环 ( i > j )

你会发现 在 4 的左边 都比 4 小

在 4 的右边都比 4 大 ,这个时候以4 为枢纽分开 递归即可
***/
  1. 堆排序

    1. 堆的概念

      堆是一颗顺序存储的二叉树

      大根堆:其中每个节点的值都不小于其子节点

      小根堆:其中每个节点的值都不大于其子节点

    2. 堆排序的过程

      创建堆 => 调整堆 => 选取最值 => 再次调整堆

    3. 代码实现

      function swap(arr, i, j){
              
              
          let temp = arr[i]
          arr[i] = arr[j]
          arr[j] = temp
      }
      function BuildHeap(arr){
              
              
          let len = arr.length
          // 因为二叉树的特点, 父节点 i   左子节点 2i+1  右子节点 2i+2
          let start = Math.floor(len/2) - 1
          for(let i = start; i >= 0; i--){
              
              
      		heapify(arr, i, len)
          }
      }
      function heapify(arr, i, end){
              
              
          let current = i
          // 父节点 i   左子节点 2i+1  右子节点 2i+2
          //        0             
          //    1       2  
          //  3   4   5   6
          let left = 2*i + 1
          let right = 2*i + 2
          if(left < end && arr[current] < arr[left]){
              
              
              current = left
          }
          if(right < end && arr[current] < arr[right]){
              
              
              current = right
          }
          if(current != i){
              
              
              swap(arr, i, current)
              heapify(arr, current, end)
          }
      }
      function HeapSort(arr){
              
              
          let len = arr.length - 1
          // 1.创建堆
          BuildHeap(arr)
          for(let i = len; i > 0; i--){
              
              
              swap(arr, 0, i)
              heapify(arr, 0, i)
          }
          return arr
      }
      

猜你喜欢

转载自blog.csdn.net/sinat_29843547/article/details/128788606