排序算法(1)

85 阅读2分钟

冒泡排序(Buubble sort)

原理:

1、对相邻元素进行比较,如果左边大于右边、左右交换,遍历一遍后得到的数组末尾的数为最大值 2、重复遍历数组除最后一个数以外的数,直到数组有序


var sortArray = function(nums) {
    let len=nums.length
    for(let i=0;i<len-1;i++){
        for(let j=0;j<len-i-1;j++){
            var tmp
            if(nums[j]>nums[j+1]){
                tmp=nums[j]
                nums[j]=nums[j+1]
                nums[j+1]=tmp
                //[nums[j],nums[j+1]]=[nums[j+1],nums[j]]
            }
            
            
        }
    }
    return nums
};

总结:

1、时间复杂度:O(n²) 空间复杂度:O(1) 稳定性:稳定

2、时间复杂度过高,基本不用

3、交换两个元素有许多办法,开始写的时候只想到了最笨的办法,后来发现结构赋值是最简便的

快速排序(partition-exchange sort)

原理

1、选取数组中随机一个数(一般选第一个)当做pivot,将数组中比他小的数放在它左边,比他大的数放在右边(无序即可)

2、采用递归的思路将pivot左边和右边的数组继续重复上述操作

3、我个人认为难点在于如何简洁的将第一步完成

 var sortArray = function (nums) {

      let len = nums.length
      if (len === 1) {
        return nums
      }

      quickSort(nums, 0, len - 1)

      function quickSort(nums, left, right) {
        if (left < right) {
          let pivot = partition(nums, left, right)
          console.log(pivot, nums)
          quickSort(nums, left, pivot - 1)
          quickSort(nums, pivot + 1, right)
        }
      }

      //
      function partition(nums, left, right) {
        let i = left,
          j = right + 1
        // 设置最左边元素为基准值
        let pivot = nums[left]

        while (1) {
          // 左边的元素要比pivot小,满足这个条件就继续往右遍历
          // 不满足或者越界了就break,
          while (nums[++i] < pivot) {
            if (i === right) {
              break
            }
          }
          // 左边的元素要比pivot大,满足这个条件就继续往左边遍历
          // 不满足或者越界了就break,
          while (pivot < nums[--j]) {
            if (j === left) {
              break
            }
          }
          // 标准:右边的元素要比pivot小,左边的元素要比pivot大,
          // 经过以上两次while循环之后,得到的i和j如果是一左一右,即i<j就要交换位置,不满足则中止循环
          console.log('oo', i, j)
          if (i >= j) {
            break
          }
          [nums[i], nums[j]] = [nums[j], nums[i]]
        }
        // 经过上边的while循环,得到了的nums:j位置为分割线,左边的值比pivot小,右边的值比pivot大
        // 那么此时的顺序就是[左边...pivot...右边],而pivot = nums[left]
        // 因此 nums[left]应该是j位置,此时交换nums[left]和nums[j]的位置
        [nums[left], nums[j]] = [nums[j], nums[left]]
        return j
      }

      return nums
    };

总结:

1、时间复杂度:O(nlogn) 空间复杂度:O(logn) 稳定性:不稳定

2、partition的部分不止一种解法,我看王道考研书上写的方法每次遍历需要两次交换,上述方式我的理解是: 先从左往右找到第一个比pivot大的数(max),再从右往左找到第一个比pivot小的数(min),如果是两个数正好一左一右,则交换后继续遍历,直到两个数是 右 左的形态 max左边的数必然比pivot小,所以min与max一定相邻,此时交换pivot和max就找到了pivot正确的位置

选择排序(Selection Sort)

原理:

1、找到数组中最大或最小的数,放到新数组中的尾或头

2、重复上述操作直到数组有序

 var sortArray = function (nums) {
      let len = nums.length;
      if (len === 1) {
        return nums;
      }
      for (let i = 0; i < nums.length - 1; i++) {
        let minIndex = i
        for (let j = i + 1; j < nums.length; j++) {
          if (nums[j] < nums[minIndex]) {
            minIndex = j
          }
        }
        if (minIndex !== i) {
          [nums[minIndex], nums[i]] = [nums[i], nums[minIndex]]
        }
      }

      return nums

    };

总结:

1、时间复杂度:O(n²) 空间复杂度:O(1) 稳定性:不稳定

插入排序(Insertion Sort)

原理:

1、将第一个数认为是有序数组,从数组的第二个数开始在已经排序的元素序列中从后向前扫描找到应在的位置

2、重复操作直到整个数组有序

var sortArray = function (nums) {
      let len = nums.length;
      if (len === 1) {
        return nums;
      }

      for (let i = 1; i < nums.length; i++) {
        for (let j = i; j > 0 && (nums[j] < nums[j - 1]); j--) {
          [nums[j], nums[j - 1]] = [nums[j - 1], nums[j]]
        }
      }

      return nums

    };

总结

1、时间复杂度:O(n²) 空间复杂度:O(1) 稳定性:稳定

2、第二层循环的意思是:将代排序的元素,在已有序的数列中从后往前便利,如果小于,则交换次序

希尔排序(Shell Sort)

原理:

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

希尔本人认为,首次选择步长应为数组长度的一半,这样会使算法更高效(书上看来的,不知真假)

var sortArray = function (nums) {
      let len = nums.length
      if (len == 1) {
        return nums
      }

      let d = Math.floor(len / 2)

      while (d >= 1) {
        for (let i = d; i < len; i++) {
          for (let j = i;
            (j >= d) && (nums[j] < nums[j - d]); j -= d) {
            [nums[j], nums[j - d]] = [nums[j - d], nums[j]]
          }
        }
        if (d >= 1) {
          d = Math.floor(d / 2)
        }
      }
      return nums
    };

总结:

1、时间复杂度:O(n的1.3次方) (无法准确计算) 空间复杂度:O(1) 稳定性:不稳定

2、leetcode中最快的就是他了

本文中代码均在912数组排序中跑过,写文章的目的是记录自己刷算法的经过,记录自己的心路历程,避免遗忘,也许会有错误,仅供参考