堆排、快排、归并

本文深入讲解了三种主要的排序算法:堆排序、快速排序和归并排序。详细介绍了每种算法的工作原理,包括堆排序的大顶堆构建和调整过程,快速排序的随机选择间隔元素和递归操作,以及归并排序的分治策略和合并过程。

堆排序

在堆中,节点和数组中的下标对应关系:
如果元素x的下标为i,那么它的左孩子节点的下标为2i+1,它的右孩子节点的下标为2i+2,它的父节点的坐标为(i-1)/2

  • 第一步对数组中所有的元素构造大顶堆
    • 默认数组中第一个元素在堆中,第二个元素和父节点比较,如果大于父节点,和父节点交换位置,即元素上移;如果小于等于父节点,元素位置不改变,继续插入下一个元素。
    • 元素移动的次数最多为logN次,即和堆的高度有关。
    • 数组中所有的元素遍历结束之后,大顶堆构建完成。
  • 第二步每次将堆顶元素和有效长度内的最后一个元素交换,
    • 假设数组的长度为len,那么第一次将堆顶元素和数组的第len个元素(此处下标从1开始)交换,然后重新对前len-1个元素调整使其成为大顶堆;第二次就将堆顶元素和数组的第len-1个元素交换,重新对前len-2个元素调整使其成为大顶堆;直到将堆顶元素和数组的第2个元素交换结束之后,排序结束。
    • 调整堆的过程是使元素下移的过程,如果堆顶元素小于孩子节点那么与其交换,继续判断交换之后与孩子节点的大小关系,直到确定一个位置,在该位置处均大于等于孩子节点的值。
//首先调整该数组构建一个大根堆,每次将堆顶元素和数组的有效范围内的最后一个位置交换
	public static void heapSort(int[]arr) {
		if(arr==null||arr.length<2)
			return;
		for(int i=1;i<arr.length;i++)
			heapInsert(arr,i);
		int size = arr.length;
		swap(arr, 0, --size);
		while (size > 1) {
			heapify(arr, 0, size);
			swap(arr, 0, --size);
		}
	}
	//建大根堆:
	//在插入的过程中使较大的元素上移,上移的次数和树的高度有关所以复杂度是logN级别。
	//当添加一个数具体和哪几个数比较,就是对应的二叉树的高度。节点数为N的话,二叉树的高度为log(N)
	public static void heapInsert(int[] arr,int index) {
		while(arr[index]>arr[(index-1)/2]) {
			swap(arr,index,(index-1)/2);
			index=(index-1)/2;
			//(0-1)/2也等于0
		}
	}
	//堆的插入,
	//调整堆的过程就是当前的堆顶元素不是最大元素,需要下移到合适的位置,使其大于左右孩子节点,移动的次数和树的高度有关。
	public static void heapify(int[] arr, int index, int size) {
		int left = index * 2 + 1;
		while (left < size) {
			int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
			if(arr[largest]<=arr[index])
				break;
			swap(arr, largest, index);
			index = largest;
			left = index * 2 + 1;
		}
	}
	private static void swap(int[] arr, int i, int j) {
		// TODO Auto-generated method stub
		int temp=arr[j];
		arr[j]=arr[i];
		arr[i]=temp;		
	}

快速排序

  • 在数组中随机选择一个元素作为间隔元素,将小于该元素的放在左侧,大于该元素的放在右侧,等于该元素的放在中间;
  • 然后递归对该元素左侧的元素和该元素右侧的元素做相同的操作;如果排序数组的左边界下标大于等于右边界的下标,那么排序结束。
  • 维护两个变量,less记录小于区域的最后边界,more记录大于区域的头边界
public static void quickSort(int[]arr) {
		if(arr==null||arr.length<2)
			return;
		quickSort(arr,0,arr.length-1);
	}
	//经典的快速排序是每次选择最后一个位置的元素作为分界元素,
	//随机快排的意思是每次随机选择一个位置作为分界元素
	private static void quickSort(int[] arr, int i, int j) {
		if(i>=j)
			return;
		int p= i+new Random().nextInt(j-i+1);
		swap(arr,p,j);
		int less=i-1,more=j,cur=i;
		while(cur<more) {
			if(arr[cur]<arr[j])
				swap(arr,cur++,++less);
			else if(arr[cur]>arr[j])
				swap(arr,cur,--more);
			else
				cur++;
		}
		swap(arr,less+1,j);//将分界元素放到合适的位置
		quickSort(arr,i,less);
		quickSort(arr,more,j);
	}

归并排序

  • 每次对数组分成两部分,先对左侧部分排序,再对右侧部分排序,直到左侧边界等于右侧边界时,证明该部分数组已经有序,然后将两部分合并。
  • 合并的过程中,定义一个辅助数组,存放合并后排好序的元素,定义两个指针分别指向各部分的头元素,哪一个小将对应的放入辅助数组中,并且下标后移,直到将两部分的元素合并结束,再将辅助数组中的元素拷贝到原数组中,合并结束。
  • 归并排序的优势是保留了之前比较的结果,不需要重复去比较,每次合并时不再是相邻之间进行判断,而是跨间隔的判断。
public static void mergeSort(int[] arr) {
		if(arr==null||arr.length<2)
			return;
		mergeSort(arr,0,arr.length-1);
	}
	private static void mergeSort(int[] arr, int i, int j) {
		if(i==j)
			return;
		int mid=i+(j-i)/2;
		mergeSort(arr,i,mid);
		mergeSort(arr,mid+1,j);
		merge(arr,i,j,mid);
	}
	private static void merge(int[] arr, int i, int j, int mid) {
		int indexi=i,indexj=mid+1;
		int []help=new int[j-i+1];
		int k=0;
		while(indexi<=mid&&indexj<=j) {
			if(arr[indexi]<arr[indexj]) {
				help[k++]=arr[indexi];
				indexi++;
			}else {
				help[k++]=arr[indexj];
				indexj++;
			}		
		}
		while(indexi<=mid)
			help[k++]=arr[indexi++];
		while(indexj<=j)
			help[k++]=arr[indexj++];
		for(int m=0;m<help.length;m++) {
			arr[i+m]=help[m];
		}
	}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值