排序

排序

1.经典快排

经典快排与上一篇中荷兰国旗问题的解决方法是一致的。在荷兰国旗问题中,把小于 num的数放在数组左边,等于num的数放在数组中间,大于num的数放在数组右边;在经典快排中,这个num设置为数组中的最后一个元素,之后的操作与荷兰国旗问题的操作一致即定义小于区域,等于区域和大于区域,以及相应的三个指针(具体细节见上一篇荷兰国旗问题)。在划分完小于区域,和大于区域后,接着分别在小于区域和大于区域中取最后一个位置的数为num,对其继续使用荷兰问题解决方法(递归),直到划分区域只剩一个元素。

在这里插入图片描述

在这里插入代码片
import java.util.Arrays;

public class QuickSort {
	//首先解决荷兰国旗问题
	public static int [] partition(int []arr,int L,int R) {
		int less=L-1;
		int more=R;
		//当L==R结束
		//注意这是小于more 因为more在递减,R为最后一个数
		while(L<more) {
			if(arr[L]<arr[R]) {
				swap(arr,++less,L++);
			}else if(arr[L]>arr[R]) {
				swap(arr,--more,L);
			}else {
				L++;
			}
		}
		swap(arr,more,R);
		return new int[] {less+1,more};
		
	}
	public static void swap(int [] arr,int a, int b) {
		int temp=arr[a];
		arr[a]=arr[b];
		arr[b]=temp;
	}
	public static void quickSort(int []arr,int L,int R) {
		//递归终止条件 这个L<R是因为分割数组 直至不能分割
		if(L<R) {  
			int []p=partition(arr,L,R);
			quickSort(arr,L,p[0]-1);
			quickSort(arr,p[1]+1,R);
		}
	}
	public static void main(String []args) {
		int []arr= {2,4,5,3,8,3,0,5};
		quickSort(arr,0,arr.length-1);
		System.out.println("排序后数组为"+Arrays.toString(arr));
	}

}

2.随机快排

在经典快排中,每次都是与数组的最后一个元素进行比较。特例:当数组arr=[1,2,3,4,5]时,num=5,此时只有小于区域和等于区域没有大于区域,遍历了一次,只搞定了一个数字,其时间复杂度为:

				T(N)=2T(N/2)+O(N)=O(N*logN)

,空间复杂度为:log(N).相比较于经典快排,随机快排每次都是与数组中随机的元素的比较。代码如下:

import java.util.Arrays;
//随机快排  主要是比较的数字是随机选的
//具体来说就是将最后一个数和前面的数进行交换
public class RandomQuickSort {
	//首先还是解决荷兰国旗问题
	public static int[] paratition(int []arr, int L,int R) {
		int less=L-1;
		int more=R;
		while(L<more) {
			if(arr[L]<arr[R]) {
				swap(arr,++less,L++);
			}else if(arr[L]>arr[R]) {
				swap(arr,--more,L++);
			}else {
				L++;
			}
		}
		swap(arr,R,more);
		return new int[] {less+1,more};
	}
	public static void swap(int []arr,int L,int R) {
		int temp=arr[L];
		arr[L]=arr[R];
		arr[R]=temp;
	}
	public static void randomQuickSort(int []arr,int L, int R) {
		if(L<R) {
			//Math.random()产生0到1之间的数
			swap(arr,R,L+(int)(Math.random())*(R-L+1));//注意起始索引为L;
			int []p=paratition(arr,L,R);
			randomQuickSort(arr,L,p[0]-1);
			randomQuickSort(arr,p[1]+1,R);
		}
	}
	public static void main(String []args) {
		int []arr= {1,2,8,3,2,5,7};
		randomQuickSort(arr,0,arr.length-1);
		System.out.println(Arrays.toString(arr));
	}

}

3.堆排

3.1基础知识

堆是一完全二叉树。
满二叉树:一棵深度为k且有 2k-1个结点的二叉树称为满二叉树(左右节点都存在).
完全二叉树:如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。对于一个节点i,其左叶节点为 2*i+1,其右叶节点为 2*i+2;其父节点(i-1)/2.
大根堆:父节点的值大于或等于子节点的值
小根堆:父节点的值小于或等于子节点的值

在这里插入图片描述

3.2数组形成大根堆

数组中每个索引为i的值都与其父节点的值(索引为(i-1)/2)进行比较,如果小于,则交换两节点值,继续向上比较,如果大于则满足大根堆要求。具体见下图
在这里插入图片描述

3.2heapify 过程

在数组按照3.1过程形成大根堆后,其根节点的值为最大值,将根节点与最底部的最右节点值进行交换,然后移除最右节点值,从根到底部,通过比较根节点索引为i 与左子节点i*2+1和右子节点i*2+2的值,再次形成大根堆。具体见下图
在这里插入图片描述

3.3代码如下
import java.util.Arrays;

public class StackSort {
	//首先遍历整个数组 把这个数组的元素变为大根堆
	public static void main(String[] args) {
		int[] arr=new int[] {8,3,5,6,5,11,23,13};
		for(int i=0;i<arr.length;i++) {
			heapInsert(arr,i);	
		}
		//经过了大根堆只是把最大值给求了出来,将根节点和最后一个节点交换然后进行构建大根堆
		int heapSize=arr.length;
		//将根节点元素和最后一个节点元素进行交换
		swap(arr,0,--heapSize);
		//这个是为了逐次构建大根堆
		//由于最后一个节点元素与根节点元素进行了交换,因此每次heapify 都从0开始。
		while(heapSize>0) {
			heapify(arr,0,heapSize);
			swap(arr,0,--heapSize);	
		}
		System.out.println(Arrays.toString(arr));
		
	}
	//比较子节点 和父节点的大小
	public static void heapInsert(int[] arr,int index) {
		//已知子节点的索引为i,对应父节点 的索引为(i-1)/2;
		while(arr[index]>arr[(index-1)/2]) {
			//交换两者的位置
			swap(arr,index,(index-1)/2);
			//此时 向上继续比较
			index=(index-1)/2;
		}
	}
	//交换两者 
	public static void swap(int []arr,int a,int b) {
		int temp=arr[a];
		arr[a]=arr[b];
		arr[b]=temp;
	}
	//重新构建大根堆
	public static void heapify(int []arr,int index,int heapSize) {
		//这里index 为根节点  其左节点为index*2+1;
		int left=index*2+1;//计算下左节点
		while(left<heapSize) {
			//比较左右节点 的值的大小
			int largest=(left+1<heapSize)&&(arr[left+1]>arr[left])?(left+1):left;
			//比较 largest 与 根节点的值
			largest=arr[largest]>arr[index]?largest:index;
			if(largest==index)
				break;
			swap(arr,largest,index);
			index=largest;//继续下沉比较
			//注意此时 left 的值也需要变换
			left=2*index+1;
		}
	}
}

总结:堆排的时间复杂度为O(N*logN),空间复杂度为O(1).

4.归并排序

在归并排序中,会先找到一个数组的中间下标mid,然后以这个mid为中心,对两边分别进行排序,之后我们再根据两边已排好序的子数组,重新进行合并。(对一个数组不断以中间位置划分,直到只有一个数字划分结束,然后排序,合并划分的数组)
在这里插入图片描述

import java.util.Arrays;

public class MergeSort {
    public  static void main(String[]args){
        int arr[]={8,3,4,2,6,9};
        mergeSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void mergeSort(int[] arr){
        if(arr.length<2){
            return;
        }
        sortProcess(arr,0,arr.length-1);
    }
    public static void  sortProcess(int[] arr,int L,int R){
        //当划分后只有一个数时停止
        if(L==R){
            return;
        }
        //划分为两部分 R和L中间位置的索引
        int mid=L+((R-L)>>1);
        sortProcess(arr,L,mid);
        sortProcess(arr,mid+1,R);
        //用来合并被分掉的数组
        merge(arr,L,mid,R);
    }
    public  static void merge(int[]arr,int L,int mid,int R){
        int p1=L; //左指针
        int p2=mid+1;//右边的指针
        int[] help=new int[R-L+1];//定义一个数组用来存储合并后的数组
        int i=0;
        while(p1<=mid&&p2<=R){
            help[i++]=arr[p1]>arr[p2]?arr[p2++]:arr[p1++];
        }
        //当从第一个while循环出来后必然是有一个>R 或mid
        while(p1<=mid){
            help[i++]=arr[p1++];//限制性help[i]=arr[p1]在分别执行++
        }
        while(p2<=R){
            help[i++]=arr[p2++];
        }
        //再把help数组赋值给原数组:
        for (int j=0;j<i;j++) {
            arr[L+j] = help[j];
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值