快排&归并&堆排

1.煞笔快排
假设我们对数组{7, 1, 3, 5, 13, 9, 3, 6, 11}进行快速排序。

首先在这个序列中找一个数作为基准数,为了方便可以取第一个数。

遍历数组,将小于基准数的放置于基准数左边,大于基准数的放置于基准数右边。

此时得到类似于这种排序的数组{3, 1, 3, 5, 6, 7, 9, 13, 11}。

在初始状态下7是第一个位置,现在需要把7挪到中间的某个位置k,也即k位置是两边数的分界点。

那如何做到把小于和大于基准数7的值分别放置于两边呢,我们采用双指针法,从数组的两端分别进行比对。

先从最右位置往左开始找直到找到一个小于基准数的值,记录下该值的位置(记作 i)。

再从最左位置往右找直到找到一个大于基准数的值,记录下该值的位置(记作 j)。

如果位置i<j,则交换i和j两个位置上的值,然后继续从(j-1)的位置往前和(i+1)的位置往后重复上面比对基准数然后交换的步骤。

如果执行到i==j,表示本次比对已经结束,将最后i的位置的值与基准数做交换,此时基准数就找到了临界点的位置k,位置k两边的数组都比当前位置k上的基准值或都更小或都更大。

上一次的基准值7已经把数组分为了两半,基准值7算是已归位(找到排序后的位置)。

通过相同的排序思想,分别对7两边的数组进行快速排序,左边对[left, k-1]子数组排序,右边则是[k+1, right]子数组排序。
利用递归算法,对分治后的子数组进行排序。

	static void sort(int[] nums, int start, int end) {
    
    
		if (start >= end) return;
		int tmp = nums[start];
		int i = start, j = end;
		while (i < j) {
    
     // while是i < j的话,i要从start开始 而不是start+1
			while (i < j && nums[j] >= tmp) --j;
			while (i < j && nums[i] <= tmp) ++i;
			swap(nums, i, j);
		}
		swap(nums, start, i);

		for (int num : nums) System.out.print(num + ", ");
		System.out.println();
		sort(nums, start, i - 1);
		sort(nums, i + 1, end);
		
	}
	static void swap(int[] nums, int i, int j) {
    
    
		int tmp = nums[i];
		nums[i] = nums[j];
		nums[j] = tmp;
	}

归并

	static void mergeSort(int[] nums, int start, int end) {
    
    
		if (start >= end) return;
		int mid = ((end - start) >> 1) + start;
		mergeSort(nums, start, mid);
		mergeSort(nums, mid + 1, end);
		merge(nums, start, mid, end);
	}
	 static void merge(int[] nums, int start, int mid, int end) {
    
    
		int[] copy = new int[end - start + 1];
		int i = start, j = mid + 1;// 注意i是从start开始,不是0!!!
		int tail = 0;//临时数组copy的索引
		while (i <= mid && j <= end) {
    
    
		     // 这里if包括 = 的话 保持算法稳定
			if (nums[i] <= nums[j]) copy[tail++] = nums[i++]; 
			else copy[tail++] = nums[j++];
		}
		while (i <= mid) copy[tail++] = nums[i++];
		while (j <= end) copy[tail++] = nums[j++];
		
		for (int k = 0; k < tail; ++k) nums[start + k] = copy[k];
	}

堆排

参考:这里

	// 调整start为根节点的子树
	static void adjectHeap(int[] nums, int start, int length) {
    
    
		int tmp = nums[start];// temp保存当前父节点
		// 获得左孩子索引:child
		for (int child = 2 * start + 1; child < length; child = 2 * child +1) {
    
    
			
			// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
			if (child + 1 < length && nums[child + 1] > nums[child]) {
    
    
				child++;
			}
			if (tmp < nums[child]) {
    
    
				nums[start] = nums[child]; // 把孩子结点的值赋给父结点
				start = child; // 指针记录当前父节点引用的是谁的值
			} else {
    
    // 如果父结点的值已经>=孩子结点的最大值,则直接结束
				break;
			}
		}
		nums[start] = tmp;//在调整结束后,把父节点原来值,赋给路径上的最大孩子
	}
	
	static void heapSort(int[] nums) {
    
    
		int length = nums.length;
		if (length == 0) return;
		
		// 循环建立初始最大堆
		for (int i = length / 2; i >= 0; --i) {
    
    
			adjectHeap(nums, i, length);
		}
		
		// // 进行n-1次循环,完成排序,依次将堆顶元素和堆尾互换,调整堆
		for (int i = length - 1; i > 0; --i) {
    
    
			swap(nums, 0, i);// 最后一个元素和第一元素进行交换
			// 筛选 nums[0] 结点,得到i-1个结点的堆
			adjectHeap(nums, 0, i);// 注意,这里是i,调整的长度截止到倒数第二个
		}
		
	}

猜你喜欢

转载自blog.csdn.net/qq_43778308/article/details/111474662