阿里P8必备Java 知识点:算法、设计模式、语法,看看你缺了哪些!

排序算法 9

P1:排序算法的分类

排序算法可以分为内部排序和外部排序,在内存中进行的排序称为内部排序,当要排序的数据量很大时无法全部拷贝到内存,需要使用外存进行排序,这种排序称为外部排序。

内部排序包括比较排序和非比较排序,比较排序包括插入排序、选择排序、交换排序和归并排序,非比较排序包括计数排序、基数排序和桶排序。其中插入排序又包括直接插入排序和希尔排序,选择排序包括直接选择排序和堆排序,交换排序包括冒泡排序和快速排序。


P2:直接插入排序

直接插入排序属于插入排序,是一种稳定的排序,平均时间复杂度和最差时间复杂度均为 O(n²),当元素基本有序时的最好时间复杂度为O(n),空间复杂度为 O(1)。

基本原理是每一趟将一个待排序的记录,按其关键字的大小插入到已经排好序的一组记录的适当位置上,直到所有待排序记录全部插入为止。适用于待排序记录较少或基本有序的情况。


`public` `void` `insertionSort(``int``[] nums) {`

`for` `(``int` `i =` `1``; i < nums.length; i++) {`

`int` `insertNum = nums[i];`

`int` `insertIndex;`

`for` `(insertIndex = i -` `1``; insertIndex >=` `0` `&& nums[insertIndex] > insertNum; insertIndex--) {`

`nums[insertIndex +` `1``] = nums[insertIndex];`

`}`

`nums[insertIndex +` `1``] = insertNum;`

`}`

`}`

**优化:**直接插入并没有利用到要插入的序列已有序的特点,插入第 i 个元素时可以通过二分查找找到要插入的位置,再把第 i 个元素前 1位与插入位置之间的所有元素后移,把第 i 个元素放在目标位置上。


`public` `void` `binaryInsertionSort(``int``[] nums) {`

`for` `(``int` `index =` `1``; index < nums.length; index++) {`

`int` `insertNum = nums[index];`

`int` `insertIndex = -``1``;`

`int` `start =` `0``;`

`int` `end = index -` `1``;`

`while` `(start <= end) {`

`int` `mid = start + (end - start) /` `2``;`

`if` `(insertNum > nums[mid])`

`start = mid +` `1``;`

`else` `if` `(insertNum < nums[mid])`

`end = mid -` `1``;`

`else` `{`

`insertIndex = mid +` `1``;`

`break``;`

`}`

`}`

`if` `(insertIndex == -``1``)`

`insertIndex = start;`

`if` `(index - insertIndex >=` `0``)`

`System.arraycopy(nums, insertIndex, nums, insertIndex +` `1``, index - insertIndex);`

`nums[insertIndex] = insertNum;`

`}`

`}`


P3:希尔排序

希尔排序属于插入排序,又称缩小增量排序,是对直接插入排序的一种改进,并且是一种不稳定的排序,平均时间复杂度为O(n1.3),最差时间复杂度为 O(n²),最好时间复杂度为 O(n),空间复杂度为 O(1)。

基本原理是把记录按下标的一定增量分组,对每组进行直接插入排序,每次排序后减小增量,当增量减至 1 时,排序完毕。适用于中等规模的数据量,对规模非常大的数据量不是最佳选择。


`public` `void` `shellSort(``int``[] nums) {`

`for` `(``int` `d = nums.length /` `2``; d >` `0` `; d /=` `2``) {`

`for` `(``int` `i = d; i < nums.length; i++) {`

`int` `insertNum = nums[i];`

`int` `insertIndex;`

`for` `(insertIndex = i - d; insertIndex >=` `0` `&& nums[insertIndex] > insertNum; insertIndex -= d) {`

`nums[insertIndex + d] = nums[insertIndex];`

`}`

`nums[insertIndex + d] = insertNum;`

`}`

`}`

`}`


P4:直接选择排序

直接选择排序属于选择排序,是一种不稳定的排序,任何情况下时间复杂度都是 O(n²),空间复杂度为 O(1)。基本原理是每次在未排序序列中找到最小元素,和未排序序列的第一个元素交换位置,再在剩余的未排序序列重复该操作直到所有元素排序完毕。适用于数据量较小的情况,比直接插入排序稍快。

`public` `void` `selectSort(``int``[] nums) {`

`int` `minIndex;`

`for` `(``int` `index =` `0``; index < nums.length -` `1``; index++){`

`minIndex = index;`

`for` `(``int` `i = index +` `1``;i < nums.length; i++){`

`if``(nums[i] < nums[minIndex])`

`minIndex = i;`

`}`

`if` `(index != minIndex){`

`swap(nums, index, minIndex);`

`}`

`}`

`}`


P5:堆排序

堆排序属于选择排序,是对直接选择排序的改进,并且是一种不稳定的排序,任何情况时间复杂度都为 O(nlogn),空间复杂度为 O(1)。

基本原理是将待排序记录看作完全二叉树,可以建立大根堆或小根堆,大根堆中每个节点的值都不小于它的子节点值,小根堆中每个节点的值都不大于它的子节点值。适用于数据量较大的情况。

以大根堆为例,在建堆时首先将最后一个节点作为当前节点,如果当前结点存在父节点且值大于父节点,就将当前节点和父节点交换。在移除时首先暂存根节点的值,然后用最后一个节点代替根节点并作为当前节点,如果当前节点存在子节点且值小于子节点,就将其与值较大的子节点进行交换,调整完堆后返回暂存的值。


`public` `void` `add(``int``[] nums,` `int` `i,` `int` `num){`

`nums[i] = num;`

`int` `curIndex = i;`

`while` `(curIndex >` `0``) {`

`int` `parentIndex = (curIndex -` `1``) /` `2``;`

`if` `(nums[parentIndex] < nums[curIndex])`

`swap(nums, parentIndex, curIndex);`

`else` `break``;`

`curIndex =parentIndex;`

`}`

`}`

`public` `int` `remove(``int``[] nums,` `int` `size){`

`int` `result = nums[``0``];`

`nums[``0``] = nums[size -` `1``];`

`int` `curIndex =` `0``;`

`while` `(``true``) {`

`int` `leftIndex = curIndex *` `2` `+` `1``;`

`int` `rightIndex = curIndex *` `2` `+` `2``;`

`if` `(leftIndex >= size)` `break``;`

`int` `maxIndex = leftIndex;`

`if` `(rightIndex < size && nums[maxIndex] < nums[rightIndex])`

`maxIndex = rightIndex;`

`if` `(nums[curIndex] < nums[maxIndex])`

`swap(nums, curIndex, maxIndex);`

`else` `break``;`

`curIndex = maxIndex;`

`}`

`return` `result;`

`}`


P6:冒泡排序

冒泡排序属于交换排序,是一种稳定的排序,平均时间复杂度和最坏时间复杂度均为 O(n²),当元素基本有序时的最好时间复杂度为O(n),空间复杂度为 O(1)。

基本原理是比较相邻的元素,如果第一个比第二个大就进行交换,对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对,每一轮排序后末尾元素都是有序的,针对 n 个元素重复以上步骤 n -1 次排序完毕。


`public` `void` `bubbleSort(``int``[] nums) {`

`for` `(``int` `i =` `0``; i < nums.length -` `1``; i++) {`

`for` `(``int` `index =` `0``; index < nums.length -` `1` `- i; index++) {`

`if` `(nums[index] > nums[index +` `1``])`

`swap(nums, index, index +` `1``)`

`}`

`}`

`}`

**优化:**当序列已经有序时仍会进行不必要的比较,可以设置一个标志位记录是否有元素交换,如果没有直接结束比较。


`public` `void` `betterBubbleSort(``int``[] nums) {`

`boolean` `swap;`

`for` `(``int` `i =` `0``; i < nums.length -` `1``; i++) {`

`swap =` `true``;`

`for` `(``int` `index =` `0``; index < nums.length -` `1` `- i; index++) {`

`if` `(nums[index] > nums[index +` `1``]) {`

`swap(nums, index ,index +` `1``);`

`swap =` `false``;`

`}`

`}`

`if` `(swap)` `break``;`

`}`

`}`


P7:快速排序

快速排序属于交换排序,是对冒泡排序的一种改进,并且是一种不稳定的排序,平均时间复杂度和最好时间复杂度均为 O(nlogn),当元素基本有序时的最坏时间复杂度为O(n²),空间复杂度为 O(logn)。

基本原理是首先选择一个基准元素,然后通过一趟排序将要排序的数据分割成独立的两部分,一部分全部小于等于基准元素,一部分全部大于等于基准元素,然后再按此方法递归对这两部分数据分别进行快速排序。适用于数据量较大且元素基本无序的情况。

快速排序的一次划分从两头交替搜索,直到 low 和 high 指针重合,因此时间复杂度是

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值