选择排序
**选择排序:**每次从无序的区间选出一个最大的(或者最小的)元素,插入到无序区间的最后(或者最前),重复选择插入,直至全部待排序的都进行插入操作,既排序完毕。
//时间复杂度O(N^2)
class Solution {
public int[] sort( int[] nums) {
for(int i=0;i<nums.length-1;i++){
//无序区间:[0,nums.length-i)
//有序区间:[nums.length-i,nums.length)
int max=0;
for(int j=1;j<nums.length-i;j++){//无序区间找最大
if(nums[j]>nums[max]){
max=j;
}
}
int t=nums[max];
nums[max]=nums[nums.length-i-1];
nums[nums.length-i-1]=t;//最大放到无序区间的最后
}
return nums;
}
}
优化版
//双向选择排序
public static void selectSortOP(int[]array){
int begin=0,end=array.length-1;
while(begin<end){
int maxPos=begin;
int minPos=begin;
int index=begin+1;
while(index<=end){
if(array[index]>array[maxPos]){
maxPos=index;
}
if(array[index]<array[minPos]){
minPos=index;
}
++index;
}
//将最大元素放在区间最后位置
if(maxPos!=end){
swap(array,maxPos,end);
}
//如果最小元素刚好在区间最后一个位置,则必须要更新minPos
if(minPos==end){
minPos=maxPos;
}
if(minPos!=begin){
swap(array,minPos,begin);
}
begin++;
end--;
}
}
希尔排序
希尔排序(缩小增量法): 希尔排序是对直接插入排序的优化,先选定一个整数作为分组的间隔,把待排序文件中的所有记录分成组,对每一组内的记录进行排序,然后取,重复上述分组和排序工作。当分组的gap==1时,所有记录在统一组内排好序。
public static void shellSort(int[]array){
int gap=3;
while(gap>0){
for(int i=gap;i<array.length;++i){
//1.找待插入元素在前面已排好序部分的位置
int key=array[i];
int end=i-gap;
//带插入元素为最小元素的情况
while(end>=0&&key<array[end]){
array[end+gap]=array[end];
end-=gap;
}
//2.插入元素
array[end+gap]=key;
}
gap--;
}
}
快速排序
选出基准值,按照基准值将区间划分为两个部分:左测部分<基准值<右侧部分
int div=partion(array,left,right);
递归排基准值的左侧quickSort(array,left,div)
递归排基准值的右侧quickSort(array,div+1,right)
递归式:
//递归过深,可能发生栈溢出
//所以没必要等到划分数组为一个个的元素
//可以在数组适当大小的时候,使用其他排序提升效率,如插入排序或者堆排序
public static void quickSort(int[]array,int left,int right){
if((right-left)>1){
int div=partition(array,left,right);
quickSort(array,left,div);
quickSort(array,div+1,right);
}
}
循环式:
//借助栈的特性:先进后出
public static void quickSort(int[]array,int left,int right){
Stack<Integer> s=new Stack<>();
s.push(array.length);//先放右区间
s.push(0);//后方左区间
while(!s.empty()){
int left=s.pop();
int right=s.pop();
if(right-left>1){
int div=partion(array,left,right);
//[left,div)
//[div+1,right)
s.push(right);
s.push(div+1);
s.push(div);
s.push(left)
}
}
}
Hoare法:
1.找一个基准值
2.定两个变量,在数组的left和right,依次向中间遍历,遇到当左边的值大于基准值,且右边的值小于基准值,两者交换。
3.基准值在它应该在的位置上,既基准值的排序完成,且基准值前皆小于基准值,之后皆大于基准值,重新定义left和right,进行下一次排序
4.反复执行2,3一直到所有元素都当过基准值,既排序完成
private static int partition(int[]array,int left,int right){
int i=left;
int j=right;
int pivot=array[left];//本次基准值
while(i<j){
while(i<j&&array[j]>=pivot){
j--;
}
while (i < j && array[i] <= pivot) {
i++;
}
swap(array, i, j);
}
swap(array, i, left);
return i;
}
取基准值的优化:
从区间最左侧、中间、最右侧取基准值,对该三个位置进行比较,返回比较结果靠中间的值
public static int getIndexOfMiddle(int[]array,int left,int right){
//(left+right)/2,容易溢出
int mid=left+((right-left)>>1);
if(array[left]<array[right-1])
{
if(array[mid]<array[left])
{
return left;
}else if(array[mid]>array[right-1])
{
return right-1;
}else{
return mid;
}
}else{
if(array[mid]>array[left])
{
return left;
}
else if(array[mid]>array[right-1])
{
return right-1;
}
else{
return mid;
}
}
}
挖坑法:
1.找到一个基准值,定两个变量,在定义一个临时变量
2.在数组的left和right,依次向中间遍历,遇到当左边的值大于基准值,两者交换,而当右边的值小于基准值,两者再次交换(与Hoare法类似,只是不再等两侧都符合条件再交换,而是直接进行赋值)
3.交换完毕之后,重新确定left和right以及当前子数组的基准值,重复以上步骤,直至完成排序
private static int partition(int[] array, int left, int right) {
int i = left;
int j = right;
int pivot = array[left];
while (i < j)
{
while (i < j && array[j] >= pivot)
{
j--;
}
array[i] = array[j];
while (i < j && array[i] <= pivot)
{
i++;
}
array[j] = array[i];
}
array[i] = pivot;
return i;
}
前后遍历法:(上图使用的就是前后遍历)
1.找基准值,然后取前后索引,都指向当前数组的最左侧
2.前索引开始遍历,如果前索引大于基准值,直接往前走,而当基准值大于前索引时,后索引往前一步走(此时后索引所指元素也大于基准值)
3.此时若后索引不与前索引重合,则两个索引之间的元素均大于基准值,且前索引当前小于基准值,交换前索引和后索引。
4.重复2,3,直至本次遍历结束,开始重新定义新left,right和基准值,开始下一次子数组的排序。
private static int partition(int[] array, int left, int right) {
int cur=left;
int prev=cur-1;
int key=array[left];
while(cur<right){
if(array[cur]<key&&++prev!=cur)
swap(array,cur,prev);
++cur;
}
if(++prev!=right-1){
swap(array,prev,right-1);
}
return prev;
}
堆排序()
1.建堆:升序——大堆,降序——小堆
- 找堆的倒数一个非叶子节点,最后一个叶子为size-1;它的双亲:((size-1)-1)>>1==(size-2)>>1;
- 从倒数第一个非叶子节点的位置开始向下调整,一直向前调整至根节点的位置为止。
2.利用堆删除的思想来进行排序:用堆顶元素与堆中最后一个元素进行交换,将堆中元素减少一个,再将堆顶元素向下调整。
public static void HeapSort(int[]array){
//1.建堆
//找到倒数第一个非叶子节点
int lastLeaf=(array.length-2)>>1;
//从lastLeaf到root的位置不断进行向下调整
for(int root=lastLeaf;root>=0;root--)
{
shiftDown(array,root,array.length);//向下调整
}
//2.利用堆删除思想
int end=array.length-1;
while(end>=0){
swap(array,0;end);//堆顶元素和堆中最后元素进行交换
shiftDown(array,0,end);
end--;
}
}
public static void shiftDown(int[]array,int parent,int size){
int child=parent*2+1;
while(chile<size){
//找左右孩子中较大的孩子
if(child+1<size&&array[child+1]>array[child])
{
child+=1;
}
//检测双亲 是否比 较大的孩子 小
if(array[child]>array[parent]){
swap(array.child,parent);
parent =child;
child=parent*2+1;
}else{
break;
}
}
}
排序方法 | 最好 | 平均 | 最坏 | 空间复杂 | 稳定 | 排序方式 |
---|---|---|---|---|---|---|
冒泡 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 | 内排 |
插入 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 | 内排 |
选择 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 | 内排 |
希尔 | O(n) | O(n^1.3) | O(n^2) | O(1) | 不稳定 | 内排 |
堆 | O(n log n) | O(n log n) | O(n log n) | O(1) | 不稳定 | 内排 |
快速 | O(n log n) | O(n log n) | O(n^2) | O(log n)~O(n) | 不稳定 | 内排 |
归并 | O(n log n) | O(n log n) | O(n log n) | O(n) | 稳定 | 外排 |
计数 | O(n+k) | O(n+k) | O(n+k) | O(k) | 稳定 | 外排 |
桶 | O(n+k) | O(n+k) | O(n^2) | O(n+k) | 稳定 | 外排 |
基数 | O(n*k) | O(n*k) | O(n*k) | O(n+k) | 稳定 | 外排 |