排序漏掉的知识(JAVA)

原地排序算法

原地排序是针对排序的空间复杂度而言的。从前边的若干排序算法中可以看出,排序过程中需要借助于一些局部变量暂存数据,或用于比较,或用于交换,或用于存储当前排序结果集。

  • 比如选择排序,每轮需要一个局部变量暂存最大/小值,一个局部变量暂存最大/小值的索引;
  • 比如冒泡排序,每轮需要一个局部变量用于交换(当然针对int的交换也可以用比较骚气的操作不使用额外变量);
  • 比如插入排序,每轮需要一个局部变量暂存固定元素的值;
  • 比如归并排序,每次合并不仅需要固定数量局部变量,还需要新开与当前合并任务相关的临时数组;
  • 比如快速排序,每次分区也是需要几个局部变量;
  • 比如桶排序,每个桶的设定都是要开辟新的内存空间;
  • 比如计数排序,存储统计个数的数组需要开辟新空间;
  • 比如基数排序,需要用到桶排序或计数排序的一种,相应就需要开辟新的内存空间。

可以看出,有些算法需要用到的额外内存有限,但有些用到的内存和待排序数据量有关。其中使用有限内存空间复杂度为O(1)的排序称为原地排序
不难看出,之前使用的排序中,大部分都是原地排序,只有少数几个不是
原地排序列表:
选择排序、冒泡排序、插入排序、快速排序

稳定排序算法

稳定排序是针对待排数据集中的重复元素而言的。
比如一个人员待排数据集[张三,李四,王五,马六],其中张三7岁,李四、王五、马六都是5岁,现在按年龄升序排列,得到的结果可以是[李四,王五,马六,张三],也可以是[王五,马六,李四,张三]甚至其他,但对比发现,第一种排序结果中,年龄相同的三人先后顺序没变,而其他的都改变了。
针对这种值相等的情况,经过排序后,其先后顺序不改变,相应的排序算法称为稳定排序算法。
大部分基于数值比较的都是稳定的排序算法,当然一不小心可能就将代码写成了不稳定排序。

  • 比如选择排序,之前我以为选择排序是稳定排序,但其实想一个反例就可证明其不是稳定排序。例如[2,2,1],第一轮找最小值找到了1,那么1就要和index=0的元素做交换了,这一交换就打破了原本两个2元素的顺序。所以选择排序不是稳定排序;
  • 比如冒泡排序,在前后元素比较时要格外注意,array[j]array[j+1]比较,如果相等,不做交换,就是稳定排序;如果不小心做了交换,就不稳定了。所以冒泡排序是稳定排序;
  • 比如插入排序,和冒泡排序一样,temparray[j]比较,因为temp所在索引原本就比j大,在相等情况下,就不要做交换了,可以保证稳定。所以插入排序是稳定排序;
  • 比如归并排序,在合并两个有序数组时,前半部分子数组天然的在前边,所以如果两边有相同元素,保持前半部分的元素优先就能保证稳定。所以归并排序是稳定排序;
  • 比如快速排序,之前我也以为这是个稳定排序(其实之前以为有比较的都是稳定的),找个反例,例如某个子数组[1,4,4,2],选取了2为分区点,执行到最后时,要把子数组相对位置为index=1的元素42做交换,这就破坏了两个4的顺序。所以快速排序不是稳定排序;
  • 比如桶排序,单个桶要用到归并排序或者快速排序,天然的一致。使用了归并排序就是稳定的,使用了快速排序就不是稳定的;
  • 比如计数排序(这里曾给自己挖了个坑,坑的地址在时间复杂度为O(n)的排序(JAVA)),因为计数排序独特而巧妙的赋值方式是从后往前的,所以搭配着也要从后往前去遍历,只要二者方向一致,就能保证稳定。所以计数排序是稳定排序。
  • 比如基数排序,基数排序要用到稳定的桶排序(也就是单个桶中使用归并排序),或者使用稳定的计数排序,所以天然就是稳定的。
    稳定排序列表:
    冒泡排序、插入排序、归并排序、桶排序(使用归并)、计数排序、基数排序

总结

是否原地排序是否稳定排序
选择排序
冒泡排序
插入排序
归并排序
快速排序
桶排序(归并)
计数排序
基数排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值