快排+堆排序

快排+堆排序

  1. 荷兰国旗问题
  2. 经典快排、改进快排、随机快排
  3. 堆结构
    (from左神算法初级班第二节)

1.荷兰国旗问题

问题一:
给定一个数组arr,和一个数num,请把小于等于num的数放在数 组的左边,大于num的数放在数组的右边。

要求额外空间复杂度O(1),时间复杂度O(N)

问题二(荷兰国旗问题) 给定一个数组arr,和一个数num,请把小于num的数放在数组的 左边,等于num的数放在数组的中间,大于num的数放在数组的 右边。

要求额外空间复杂度O(1),时间复杂度O(N)

荷兰国旗问题代码:

public static int[] partition(int[] arr, int l, int r, int p) {
		int less = l - 1;//小于区域
		int more = r + 1;//大于区域的左边界
		while (l < more) {
			if (arr[l] < p) {
				swap(arr, ++less, l++);//less位置和l位置交换,并且less扩充一个位置
			} else if (arr[l] > p) {
				swap(arr, --more, l);//当前数与大于区域边界交换,相当于大于区域扩充了一个位置
			} else {
				l++;
			}
		}
		return new int[] { less + 1, more - 1 };
	}
	// for test
	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

2.经典快排、改进快排(荷兰国旗问题)

优点:常数项很低、常数项操作很少

时间复杂度O(N*logN)
额外空间复杂度O(logN)(需要记录划分点,也就是树结构)

数据状况糟糕的话,时间复杂度会变成O(N2)

1)原理:以最后一个数x为界,小于x放左边,大于放x右边。x左边再以最后一个数y为界,小于y放在y左边,大于y放在y右边,一直递归下去。

2)代码:

public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}
public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			//swap(arr, l + (int) (Math.random() * (r - l + 1)), r);//随机快排
			int[] p = partition(arr, l, r);
			quickSort(arr, l, p[0] - 1);
			quickSort(arr, p[1] + 1, r);
		}
	}
public static int[] partition(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, more, r);
		return new int[] { less + 1, more };
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

3)随机快排
随机在数组上选一个数,然后和最后一个数交换,再进行快排。
长期期望时间复杂度为:O(N*logN)

swap(arr, l + (int) (Math.random() * (r - l + 1)), r);//加一行就是随机快排

3.堆结构(可以转换成数组)

1)什么是完全二叉树?
任何一个非叶子节点是完全二叉树
二叉树区别
二叉树求下标关系

二叉树下标
父节点树下标(i-1)/2
左孩子树下标2*i+1
右孩子树下标2*i+2

例如:该数组找到其父节点和左右孩子树
原数组
其树结构为:
在这里插入图片描述
2)大根堆、小根堆
大根堆:树的最值在头结点(整棵树)
小根堆:树的最值在头结点(整棵树)

建立大根堆

	public static void heapInsert(int[] arr, int index) {
		while (arr[index] > arr[(index - 1) / 2]) {//比父节点大。来到0位置会停
			swap(arr, index, (index - 1) / 2);//交换
			index = (index - 1) / 2;//来到父位置
		}
	}

建立大根堆的时间复杂度为:O(logN),因为是完全二叉树形成的高度

3)堆上最大值发生变化,需要调整大根堆时:

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;
			largest = arr[largest] > arr[index] ? largest : index;
			if (largest == index) {
				break;
			}
			swap(arr, largest, index);
			index = largest;
			left = index * 2 + 1;
		}
	}

4)堆结构完整代码:

public static void heapSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			heapInsert(arr, i);//0~i形成大根堆
		}
	}

	public static void heapInsert(int[] arr, int index) {
		while (arr[index] > arr[(index - 1) / 2]) {//比父节点大。来到0位置会停
			swap(arr, index, (index - 1) / 2);//交换
			index = (index - 1) / 2;//来到父位置
		}
	}

	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;//右孩子不越界,而且和左孩子比较大小,谁大就是largest
			largest = arr[largest] > arr[index] ? largest : index;//largest和我比较,谁大谁做largest
			if (largest == index) {
				break;//最大是我,就不用调整了,不往下沉了,并且跳出循环
			}
			swap(arr, largest, index);//largest!=index
			index = largest;
			left = index * 2 + 1;
		}
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

5)用堆结构求中位数问题(时间复杂度为:O(logN)):

  • 建立一个大根堆、一个小根堆,两个堆数目差值大于1,就将多的堆,拿出一个最上的数值,扔到另一个堆中(减堆操作、加堆操作)
  • 一开始先往小根堆里加一个数,然后下个数比这个数大就进大根堆,小就进入小根堆排序,并进行一次换堆操作(因为差值大于1)
  • 减堆操作:先将堆的最后一个数和堆顶交换,然后进行堆调整,最大堆还是最大堆,最小堆还是最小堆,只是size-1;size位置上就是要加到另一个堆上的数。
  • 加堆操作:
    最大堆–>最小堆:size上的数直接做堆顶。
    最小堆–>最大堆:size上的数直接做最后一个数。

代码:

public static void heapSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			heapInsert(arr, i);//0~i形成大根堆
		}
		int size = arr.length;
		swap(arr, 0, --size);
		while (size > 0) {
			heapify(arr, 0, size);
			swap(arr, 0, --size);
		}
	}
public static void heapInsert(int[] arr, int index) {
		while (arr[index] > arr[(index - 1) / 2]) {//比父节点大。来到0位置会停
			swap(arr, index, (index - 1) / 2);//交换
			index = (index - 1) / 2;//来到父位置
		}
	}
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;//右孩子不越界,而且和左孩子比较大小,谁大就是largest
			largest = arr[largest] > arr[index] ? largest : index;//largest和我比较,谁大谁做largest
			if (largest == index) {
				break;//最大是我,就不用调整了,不往下沉了,并且跳出循环
			}
			swap(arr, largest, index);//largest!=index
			index = largest;
			left = index * 2 + 1;
		}
	}
public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值