排序3——不稳定的排序

选择排序

**选择排序:**每次从无序的区间选出一个最大的(或者最小的)元素,插入到无序区间的最后(或者最前),重复选择插入,直至全部待排序的都进行插入操作,既排序完毕。
在这里插入图片描述

//时间复杂度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)稳定外排
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值