算法学习-排序部分

排序算法平均时间复杂度最坏时间复杂度空间复杂度是否稳定排序
冒泡排序O(n2)O(n2)O(1)稳定
鸡尾酒排序O(n2)O(n2)O(1)稳定
桶排序O(n)O(nlogn)O(n)稳定
快速排序O(nlogn)O(n2)O(logn)不稳定

桶排序

简单的桶排序

  • 栗子
    一场考试有5名同学,满分10分,5名同学得到的分数分别为5分,3分,5分,2分,8分,请给同学进行排序
  • JS简易版桶排序算法实现
var bucketSort = function(arr, len, rule = 'desc') {
  if (!arr || arr.length < 1 || !len) return

  let tempArr = new Array(len)
  let returnArr = []
  for (let i = 0; i < len; i++) tempArr[i] = 0
  for (let j = 0; j < arr.length; j++) {
    tempArr[arr[j]] += 1 
  }
  if (rule === 'desc') {
    for (let i = 0; i < len; i++) {
      for (let j = 1; j <= tempArr[i]; j++) {
        returnArr.push(i)
      }
    }
  } else {
    for (let i = len; i > 0; i--) {
      for (let j = 1; j <= tempArr[i]; j++) {
        returnArr.push(i)
      }
    }
  }
  return returnArr
};

console.log(bucketSort([5,3,5,2,8], 11, 'asc'))
  • 学习讲解
    上述代码中新建大小为11的数组,数组下表代表的是成绩分布情况(0到10分)
    简易版桶排序排序思路:将同学的成绩按照各自所对应的数组下标“倒进去”,多一个人,桶内数字加一;最后按照桶内数字的大小进行每个下标的循环输出

  • 算法说明
    时间复杂度:O(M+N)
    时间复杂度说明:
    第一个循环–桶的数量—循环M次
    待排序的列表循环–同学的分数—循环N次
    循环输出–循环M+N次
    整个算法循环M+N+M+N次 = O(2*(M+N)) = O(M+N)

冒泡排序

  • JS 实现
var bubbleSort = function(arr) {
  if (!arr || arr.length < 1) return
  let returnArr = arr

  for (let i = 0; i < returnArr.length; i++) {
    for (let j = 0; j < returnArr.length; j++) {
      if (returnArr[j] > returnArr[j+1]) {
        let temp = returnArr[j+1]
        returnArr[j+1] = returnArr[j]
        returnArr[j] = temp
      }
    }
  }
  return returnArr
};

console.log(bubbleSort([8,100,50,22,15,6,1,1000,99,0]))
  • 学习讲解
    冒泡排序的基本思想是:每次比较相邻的两个元素,顺序错误就交换

  • 算法说明
    时间复杂度:O(N2)


  • 冒泡排序优化实现-1
var sortArray = function(nums) {
    if(!nums || nums.length < 1) return nums

    for (let i = 0; i < nums.length; i++) {
        let isSorted = true
        for (let j = 0; j < nums.length - 1; j++) {
            if (nums[j] > nums[j+1]) {
                let temp = nums[j]
                nums[j] = nums[j+1]
                nums[j+1] = temp
                isSorted = false
            }
        }
        if (isSorted) break
    }

    return nums
};
  • 学习讲解
    添加了一个标志isSorted 用来标志当前数组是否已经是有序的了,如果是有序的了,就不再继续循环

  • 冒泡排序优化实现-2
var sortArray = function(nums) {
    if(!nums || nums.length < 1) return nums
    let lastExchangeIndex = 0 // 记录最后一次交换位置
    let sortBorder = nums.length - 1  // 无序数组的边界

    for (let i = 0; i < nums.length - 1; i++) {
        let isSorted = true
        for (let j = 0; j < sortBorder; j++) {
            if (nums[j] > nums[j+1]) {
                let temp = nums[j]
                nums[j] = nums[j+1]
                nums[j+1] = temp
                isSorted = false
                lastExchangeIndex = j
            }
        }
        sortBorder = lastExchangeIndex
        if (isSorted) break
    }

    return nums
};
  • 学习讲解
    添加一个无序数组循环的边界值,这样每次循环的时候只循环在边界值就好了

  • 冒泡排序升级版–鸡尾酒排序
  • JS实现
var sortArray = function(nums) {
    if(!nums || nums.length < 1) return nums
    for (let i = 0; i < nums.length/2; i++) {
        let isSorted = true
        // 从左向右比较和交换
        for (let j = i; j < nums.length - i - 1; j++) {
            if (nums[j] > nums[j+1]) {
                let temp = nums[j]
                nums[j] = nums[j+1]
                nums[j+1] = temp
                isSorted = false
            }
        }
        if (isSorted) break

        // 从右向左比较和交换
        isSorted = true
        for (let j = nums.length - i - 1; j > i; j--) {
            if (nums[j] < nums[j-1]) {
                let temp = nums[j]
                nums[j] = nums[j-1]
                nums[j-1] = temp
                isSorted = false
            }
        }
        if (isSorted) break
    }

    return nums
};
  • 学习讲解
    鸡尾酒排序的比较和交换过程是双向的,排序过程就像钟摆一样。算法的优点是能够在特定的条件下减少排序的回合数;最好是能在大部分元素已经有序的情况下使用哦

快速排序

分治法:在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比他小的元素移动到数列另一边,从而把数列拆解成两个部分

  • 双边循环JS 实现
var sortArray = function(nums) {
    function quickSort(array, start = 0, end = array.length - 1) {
        if (start < end) {
            // 取基准值,将array的每个元素与基准值大小比对后放在基准值的左边或者右边
            let pivot = partition(array, start, end);
            // 对小于基准值的元素排序
            quickSort(array, start, pivot - 1);
            // 对大于基准值的元素排序
            quickSort(array, pivot + 1, end);
        }
        return array;
    }
    var partition = function(arr, startIndex, endIndex) {
	    // 基准元素的选取
	    let pivot = arr[startIndex]
	    let left = startIndex
	    let right = endIndex
	    while (left !== right) {
	        while (left < right && arr[right] > pivot) {
	            right--
	        }
	        while (left < right && (arr[left] < pivot || arr[left] === pivot)) {
	            left++
	        } 
	        if (left < right) {
	            let temp = arr[left]
	            arr[left] = arr[right]
	            arr[right] = temp
	        }
	    }
	
	    arr[startIndex] = arr[left]
	    arr[left] = pivot
	
	    return left
	}
	return quickSort(nums)
};
  • 学习讲解
    双边循环需要两个指针left,right,代表了每次比较的区间;双边循环,如同名字一样,从数据两边进行循环,先右边循环后左边循环,依次进行

  • 单边循环JS 实现

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
    if (!nums || nums.length < 1) return nums

    let startIndex = 0, lastIndex = nums.length - 1;
    var quickSort = function(array, start, end) {
        if (start < end) {
            // 取基准值,将array的每个元素与基准值大小比对后放在基准值的左边或者右边
            let pivot = partition(array, start, end);
            // 对小于基准值的元素排序
            quickSort(array, start, pivot - 1);
            // 对大于基准值的元素排序
            quickSort(array, pivot + 1, end);
        }
        return array;
    }

    var partition = function(arr, startIndex, endIndex) {
        // 基准元素的选取
        let pivot = arr[startIndex]
        let mark = startIndex
        for (let i = startIndex + 1; i <= endIndex; i++) {
            if (arr[i] < pivot) {
                mark++
                let temp = arr[mark];
                arr[mark] = arr[i];
                arr[i] = temp
            }
        }
    
        arr[startIndex] = arr[mark]
        arr[mark] = pivot
    
        return mark
    }

    return quickSort(nums, startIndex, lastIndex)
};
  • 学习讲解
    单边循环只需要一个指针mark
  • 非递归JS 实现
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function(nums) {
   if (!nums || nums.length < 1) return nums
   
   var quickSort = function(array, start, end) {
       let arrStack = [{startIndex: start, endIndex: end}]

       while(arrStack.length > 0) {
           let param = arrStack.pop();
           let pivot = partition(array, param.startIndex, param.endIndex)
           if (param.startIndex < pivot - 1) {
               let leftParam = {
                   startIndex: param.startIndex, 
                   endIndex: pivot - 1
               }
               arrStack.push(leftParam)
           } 
           if (pivot + 1 < param.endIndex) {
               let rightParam = {
                   startIndex: pivot + 1, 
                   endIndex: param.endIndex
               }
               arrStack.push(rightParam)
           }
       }

       return array;
   }

   var partition = function(arr, startIndex, endIndex) {
       // 基准元素的选取
       let pivot = arr[startIndex]
       let mark = startIndex
       for (let i = startIndex + 1; i <= endIndex; i++) {
           if (arr[i] < pivot) {
               mark++
               let temp = arr[mark];
               arr[mark] = arr[i];
               arr[i] = temp
           }
       }
   
       arr[startIndex] = arr[mark]
       arr[mark] = pivot
   
       return mark
   }

   let startIndex = 0, lastIndex = nums.length - 1;
   return quickSort(nums, startIndex, lastIndex)
};

堆排序

  • JS实现
var sortArray = function(nums) {
    
    let downAdjust = (array, parentIndex, length) => {
        let temp = array[parentIndex] // 父节点值,用于最后的赋值
        let childIndex = 2 * parentIndex + 1;
        while (childIndex < length) {
            if (childIndex + 1 < length && array[childIndex + 1] > array[childIndex]) {
                childIndex++;
            }
            if (temp >= array[childIndex]) {
                break;
            }
            array[parentIndex] = array[childIndex]
            parentIndex = childIndex
            childIndex = 2 * childIndex + 1
        }
        array[parentIndex] = temp
    }

    
};
  • 学习讲解

堆排序的算法步骤:

  1. 把无序数组构建成二叉堆。需要从小到大排序,则构建成最大堆;需要从大到小排序,则构建成最小堆
  2. 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值