2021年12月13日|14日|15日|16日|17日

2021年12月第三周周记

B站左神Leetcode视频

归并排序

归并排序介绍

package class02;
import java.util.Arrays;

public class Code01_MergeSort {

	public static void mergeSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		mergeSort(arr, 0, arr.length - 1);
	}

	public static void mergeSort(int[] arr, int l, int r) {
		if (l == r) {
			return;
		}
		int mid = l + ((r - l) >> 1);
		mergeSort(arr, l, mid);
		mergeSort(arr, mid + 1, r);
		merge(arr, l, mid, r);
	}

	public static void merge(int[] arr, int l, int m, int r) {
		int[] help = new int[r - l + 1];
		int i = 0;
		int p1 = l;
		int p2 = m + 1;
		while (p1 <= m && p2 <= r) {
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= m) {
			help[i++] = arr[p1++];
		}
		while (p2 <= r) {
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[l + i] = help[i];
		}
	}

	// for test
	public static void comparator(int[] arr) {
		Arrays.sort(arr);
	}

	// for test
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
		}
		return arr;
	}

	// for test
	public static int[] copyArray(int[] arr) {
		if (arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for (int i = 0; i < arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}

	// for test
	public static boolean isEqual(int[] arr1, int[] arr2) {
		if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
			return false;
		}
		if (arr1 == null && arr2 == null) {
			return true;
		}
		if (arr1.length != arr2.length) {
			return false;
		}
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	// for test
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	// for test
	public static void main(String[] args) {
		int testTime = 500000;
		int maxSize = 100;
		int maxValue = 100;
		boolean succeed = true;
		for (int i = 0; i < testTime; i++) {
			int[] arr1 = generateRandomArray(maxSize, maxValue);
			int[] arr2 = copyArray(arr1);
			mergeSort(arr1);
			comparator(arr2);
			if (!isEqual(arr1, arr2)) {
				succeed = false;
				printArray(arr1);
				printArray(arr2);
				break;
			}
		}
		System.out.println(succeed ? "Nice!" : "Fucking fucked!");

		int[] arr = generateRandomArray(maxSize, maxValue);
		printArray(arr);
		mergeSort(arr);
		printArray(arr);

	}
}

归并排序的扩展-小和问题和逆序对问题

小和问题

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。
eg:求数组[1,3,4,2,5]的小和
1左边比1小的数 null;
3左边比3小的数 1;
4左边比4小的数 1、3;
2左边比2小的数 1;
5左边比5小的数 1、3、4、2;
小和= 1+1+3+2+1+3+4+2 = 16


public class Code02_SmallSum {

	public static int smallSum(int[] arr) {
		if (arr == null || arr.length < 2) {
			return 0;
		}
		return mergeSort(arr, 0, arr.length - 1);
	}

	// arr[L..R]既要排好序,也要求小和
	
	public static int mergeSort(int[] arr, int l, int r) {
		if (l == r) {
			return 0;
		}
		int mid = l + ((r - l) >> 1);
		return mergeSort(arr, l, mid) 
				+ mergeSort(arr, mid + 1, r) 
				+ merge(arr, l, mid, r);
	}

	public static int merge(int[] arr, int l, int m, int r) {
		int[] help = new int[r - l + 1];
		int i = 0;
		int p1 = l;
		int p2 = m + 1;
		int res = 0;
		while (p1 <= m && p2 <= r) {
			res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0;
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= m) {
			help[i++] = arr[p1++];
		}
		while (p2 <= r) {
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[l + i] = help[i];
		}
		return res;
	}

	// for test
	public static int comparator(int[] arr) {
		if (arr == null || arr.length < 2) {
			return 0;
		}
		int res = 0;
		for (int i = 1; i < arr.length; i++) {
			for (int j = 0; j < i; j++) {
				res += arr[j] < arr[i] ? arr[j] : 0;
			}
		}
		return res;
	}

	// for test
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
		}
		return arr;
	}

	// for test
	public static int[] copyArray(int[] arr) {
		if (arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for (int i = 0; i < arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}

	// for test
	public static boolean isEqual(int[] arr1, int[] arr2) {
		if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
			return false;
		}
		if (arr1 == null && arr2 == null) {
			return true;
		}
		if (arr1.length != arr2.length) {
			return false;
		}
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	// for test
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	// for test
	public static void main(String[] args) {
		int testTime = 500000;
		int maxSize = 100;
		int maxValue = 100;
		boolean succeed = true;
		for (int i = 0; i < testTime; i++) {
			int[] arr1 = generateRandomArray(maxSize, maxValue);
			int[] arr2 = copyArray(arr1);
			if (smallSum(arr1) != comparator(arr2)) {
				succeed = false;
				printArray(arr1);
				printArray(arr2);
				break;
			}
		}
		System.out.println(succeed ? "Nice!" : "Fucking fucked!");
	}

}

逆序对问题

定义:在一个数组中,如果左边的数比右边的数大,则这两个数构成一个逆序对;
排序的过程就是消除逆序对的过程。逆序对越多,相对来说排序所需要的时间就越多。

朴素算法 (O(n^2))

枚举数组元素的所有组合, 对于 长度为 n 的数组, 一共有​n*(n-1)/2种组合。

int countInversions(int a[], int length)
{
	int count = 0;
	for (int i = 0; i < length - 1; i++) {
		for (int j = i + 1; j < length; j++) {
			if (a[i] > a[j])
				count++;
		}
	}
	return count;
}
借助冒泡排序 O(n^2)

冒泡排序的每一次交换就会消除一个逆序对, 交换一个相邻的逆序对,不会影响到其它的逆序对,所以可以计算冒泡排序在排序过程一共进行了多少次交换,由此得出数组的逆序对数。最普通的冒泡排序和枚举的循环次数一样多。但是枚举的过程没有元素交换,会比使用冒泡排序计算逆序对数快很多。

  • 借助冒泡排序只需要在交换两个元素的同时逆序对计数加1即可。普通的冒泡算法会试遍所有的组合,而改进的冒泡算法则可以减少这些检查,逆序对数少时就比枚举法快很多,但是逆序对数多的话就不行,因为排序有很大一部分时间花在元素交换上。
  • 缺点是计算的过程会消除逆序对,计算完成后,逆序对也被消除了,如果想保留数列,则需要复制数组,使用副本计算。
int countInversions_bubbleSort(int a[], int length)
	{
		int count = 0;
		int left = 0, right = length - 1;	//遍历的范围
		int leftBound, rightBound;			//记录乱序的边界

		while (left < right) {
			leftBound = right, rightBound = left;
			for (int i = left; i < right; i++) {
				if (a[i] > a[i + 1]) {
					count++;
					int temp = a[i];
					a[i] = a[i + 1];
					a[i + 1] = temp;

					rightBound = i;				//修改右边界
					
				}
			}

			right = rightBound;			//修改范围

			for (int i = right; i > left; i--) {
				if (a[i - 1] > a[i]) {
					count++;
					int temp = a[i - 1];
					a[i - 1] = a[i];
					a[i] = temp;

					leftBound = i;				//修改左边界
				}
			}

			left = leftBound;			//修改范围
		}
		return count;
	}

借助归并排序 (O(nlogn))

一个升序数组,逆序对为0。数组中一个连续段之间的逆序对交换,只会影响段内的逆序对数,而不会影响段外的逆序对,段外的元素与段内的元素之间的逆序对数也不受影响
  将一个数组A从中间分成左右两部分, 分别是A[Left]…A[Mid]和A[Mid+1]…A[Right]
  假设两部分分别有序,在归并过程中,i, j 分别表示左右两部分归并到的元素下标, 必有i < j。如果有A[i] > A[j], 那么左边A[i]之后的元素也都会大于A[j], 所以A[j] 与 A[i] 到A[mid]的逆序对为mid-i+1
  归并排序的两个有序数列归并,这两个有序数列在数组中是相邻的,所以交换这两个数列的元素,只会影响这两个数列元素之间的逆序对数。
  由此修改归并排序来计算逆序对, 逆序对在归并时计算。为了简便,就使用最普通的递归归并排序来说明。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
#include <set>
using namespace std;
typedef long long LL;
#define maxn 500010
LL a[maxn];
LL temp[maxn];
LL sum;
void Merge(int l,int r,int m)
{
    int i=l;
    int j=m+1;
    int k=l;
    while(i<=m&&j<=r)
    {
        if(a[i]>a[j])
        {
            sum+=m-i+1;
            temp[k++]=a[j++];
        }
        else
        {
            temp[k++]=a[i++];
        }
    }

    while(i<=m)
    {
        temp[k++]=a[i++];
    }
    while(j<=r)
    {
        temp[k++]=a[j++];
    }
    for(i=l; i<=r; i++)
    {
        a[i]=temp[i];
    }
}
void mergesort(int l,int r)
{
    if(l<r)
    {
        int mid=l+(r-l)>>2;
        mergesort(l,mid);
        mergesort(mid+1,r);
        Merge(l,r,mid);
    }
}
int main()
{
    int n,i;
    while(~scanf("%d",&n))
    {
        if(n==0)
            break;
        for(i=0; i<n; i++)
        {
            cin>>a[i];
        }
        sum=0;
        mergesort(0,n-1);
        cout<<sum<<endl;
    }
    return 0;
}

快速排序

引入问题 (V1.0)
>  给定一个数组Arr,和一个数Num,请把小于等于Num的数放在数组的左边,大于Num的放在数组右边
   要求额外空间复杂度O(1),时间复杂度O(N)
荷兰国旗问题 (V2.0 )

给定一个数组arr,和一个数num,请把小于num的数放在数组的 左边,等于num的数放在数组的 中间,大于num的数放在数组的 右边。
要求额外空间复杂度O(1),时间复杂度 O(N)

package class02;

public class Code05_NetherlandsFlag {

	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++);
			} 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;
	}

	// for test
	public static int[] generateArray() {
		int[] arr = new int[10];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) (Math.random() * 3);
		}
		return arr;
	}

	// for test
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	public static void main(String[] args) {
		int[] test = generateArray();

		printArray(test);
		int[] res = partition(test, 0, test.length - 1, 1);
		printArray(test);
		System.out.println(res[0]);
		System.out.println(res[1]);

	}
}
快排(V3.0) O(nlogn)

因为分割数是随机选择出来的 时间复杂度从O(N^2)进化到O(nlogn)
空间复杂度为O(NlogN)

package class02;

import java.util.Arrays;

public class Code06_QuickSort {

	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]) {
			// 若当前数比给定数小 
			// 当前数和下边界的下一个数交换,下边界右扩 l++
				swap(arr, ++less, l++);
			} else if (arr[l] > arr[r]) {
			// 若当前数比给定数大
			// 当前数和上边界的前一个数交换,上边界左扩 l不变
				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;
	}

	// for test
	public static void comparator(int[] arr) {
		Arrays.sort(arr);
	}

	// for test
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
		}
		return arr;
	}

	// for test
	public static int[] copyArray(int[] arr) {
		if (arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for (int i = 0; i < arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}

	// for test
	public static boolean isEqual(int[] arr1, int[] arr2) {
		if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
			return false;
		}
		if (arr1 == null && arr2 == null) {
			return true;
		}
		if (arr1.length != arr2.length) {
			return false;
		}
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	// for test
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	// for test
	public static void main(String[] args) {
		int testTime = 500000;
		int maxSize = 100;
		int maxValue = 100;
		boolean succeed = true;
		for (int i = 0; i < testTime; i++) {
			int[] arr1 = generateRandomArray(maxSize, maxValue);
			int[] arr2 = copyArray(arr1);
			quickSort(arr1);
			comparator(arr2);
			if (!isEqual(arr1, arr2)) {
				succeed = false;
				printArray(arr1);
				printArray(arr2);
				break;
			}
		}
		System.out.println(succeed ? "Nice!" : "Fucking fucked!");

		int[] arr = generateRandomArray(maxSize, maxValue);
		printArray(arr);
		quickSort(arr);
		printArray(arr);

	}
}

1、堆结构就是用数组实现的完全二叉树结构
2、完全二叉树中如果每棵子树的最大值都在顶部就是大根堆
3、完全二叉树中如果每棵子树的最小值都在顶部就是小根堆
4、堆结构的heapInsert与heapfy操作
5、堆结构的增大和减少
6、优先级队列结构,就是堆结构

堆调整(插入新值)
  • 不知道插入值变大还是变小时,下列两种都尝试,只会命中一种情况
当插入值比原位置上的数大时
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 ;
		}
	}
当插入值比原位置上的数小时
  • 此操作实际为二叉树堆化
// 某个数在index位置,能否往下移动调整堆
public static void heapify(int[] arr, int index, int size) {
		// size掌管着堆的大小 比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);

			// 交换后 arr[largest]其实就是最初的arr[index]
			index = largest;
			
			//交换后 若arr[index]还有左右孩子且未越界则继续循环
			left = index * 2 + 1;
		}
	}
堆调整的时间复杂度(O(LogN))

完全二叉树的高度:LogN
不管是插入一个新数字还是移除堆的最大值并返回都只遍历一遍数的高度

Java使用优先队列实现小根堆
package class07;

import java.util.Comparator;
import java.util.PriorityQueue;

public class Code03_priorityQueueForHeap {

	public static class MinheapComparator implements Comparator<Integer> {

		@Override
		public int compare(Integer o1, Integer o2) {
			return o1 - o2; // < 0  o1 < o2  负数
		}

	}

	public static class MaxheapComparator implements Comparator<Integer> {

		@Override
		public int compare(Integer o1, Integer o2) {
			return o2 - o1; // <   o2 < o1
		}

	}

	public static void main(String[] args) {
	
		int[] arrForHeap = { 3, 5, 2, 7, 0, 1, 6, 4 };

		// min heap
		PriorityQueue<Integer> minQ1 = new PriorityQueue<>();
		for (int i = 0; i < arrForHeap.length; i++) {
			minQ1.add(arrForHeap[i]);
		}
		while (!minQ1.isEmpty()) {
			System.out.print(minQ1.poll() + " ");
		}
		System.out.println();

		// min heap use Comparator
		PriorityQueue<Integer> minQ2 = new PriorityQueue<>(new MinheapComparator());
		for (int i = 0; i < arrForHeap.length; i++) {
			minQ2.add(arrForHeap[i]);
		}
		while (!minQ2.isEmpty()) {
			System.out.print(minQ2.poll() + " ");
		}
		System.out.println();

		// max heap use Comparator
		PriorityQueue<Integer> maxQ = new PriorityQueue<>(new MaxheapComparator());
		for (int i = 0; i < arrForHeap.length; i++) {
			maxQ.add(arrForHeap[i]);
		}
		while (!maxQ.isEmpty()) {
			System.out.print(maxQ.poll() + " ");
		}
	}
}

堆排序

堆排序通俗的说就是:

  • 对初始无序数组建堆,得到一个堆(此时HeapSize=Arr.length)
  • 将堆顶(最值)和最后一个数交换 同时HeapSize-- 对Arr[0]→Arr[HeapSize-1] 进行建堆
  • 最终HeapSize=0 得到一个有序数组
package class02;

import java.util.Arrays;

public class Code03_HeapSort {

	public static void heapSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length; i++) { //O(N)
			heapInsert(arr, i);		//O(LogN)
		}

		// 若直接给了一颗二叉树,而不是单值插入 则上面的For循环有更简单的方法
		//	相当于对一个数从下往上进行堆调整 时间复杂度为O(N) 但也仅仅是改变这一步的速度堆排序整体还是NlogN		
		//for(int i = arr.length -1 ;i >=0;i--){
		//		heapfy(arr,i,arr.length);
		//}
		
		int size = arr.length;
		swap(arr, 0, --size);
		while (size > 0) {	// O(N)
			heapify(arr, 0, size);	// O(logN)
			swap(arr, 0, --size);	//O(1)
		}
	}

	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 ;
		}
	}

	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;
		}
	}

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

	// for test
	public static void comparator(int[] arr) {
		Arrays.sort(arr);
	}

	// for test
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
		}
		return arr;
	}

	// for test
	public static int[] copyArray(int[] arr) {
		if (arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for (int i = 0; i < arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}

	// for test
	public static boolean isEqual(int[] arr1, int[] arr2) {
		if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
			return false;
		}
		if (arr1 == null && arr2 == null) {
			return true;
		}
		if (arr1.length != arr2.length) {
			return false;
		}
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	// for test
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	// for test
	public static void main(String[] args) {
		int testTime = 500000;
		int maxSize = 100;
		int maxValue = 100;
		boolean succeed = true;
		for (int i = 0; i < testTime; i++) {
			int[] arr1 = generateRandomArray(maxSize, maxValue);
			int[] arr2 = copyArray(arr1);
			heapSort(arr1);
			comparator(arr2);
			if (!isEqual(arr1, arr2)) {
				succeed = false;
				break;
			}
		}
		System.out.println(succeed ? "Nice!" : "Fucking fucked!");

		int[] arr = generateRandomArray(maxSize, maxValue);
		printArray(arr);
		heapSort(arr);
		printArray(arr);
	}

}
堆排序扩展题目

已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小.
请选择一个合适的排序算法针对这种数据进行排序

package class02;

import java.util.PriorityQueue;

public class Code04_SortArrayDistanceLessK {

	public void sortedArrDistanceLessK(int[] arr, int k) {
	
	// java中优先级队列默认构造即为小根堆
		PriorityQueue<Integer> heap = new PriorityQueue<>();
		int index = 0;
		for (; index < Math.min(arr.length, k); index++) {
			heap.add(arr[index]);
		}
		int i = 0;
		for (; index < arr.length; i++, index++) {
			heap.add(arr[index]);
			arr[i] = heap.poll();
		}
		while (!heap.isEmpty()) {
			arr[i++] = heap.poll();
		}
	}
}

通过堆找中位数
 首先,我们思考一下中位数的性质:
 如果一个数是中位数,那么在这个数组中,大于中位数的数目和小于中位数的数目,
 要么相等,要么就相差一。
 我们自然可以考虑到利用堆维护这个数据结构,大顶堆存放前半小的数,小顶堆存放后半大的数。
package class07;

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;

public class Code06_MadianQuick {

	public static class MedianHolder {
		private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(new MaxHeapComparator());
		private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(new MinHeapComparator());

		private void modifyTwoHeapsSize() {
			if (this.maxHeap.size() == this.minHeap.size() + 2) {
				this.minHeap.add(this.maxHeap.poll());
			}
			if (this.minHeap.size() == this.maxHeap.size() + 2) {
				this.maxHeap.add(this.minHeap.poll());
			}
		}

		public void addNumber(int num) {
			if (maxHeap.isEmpty() || num <= maxHeap.peek()) {
				maxHeap.add(num);
			} else {
				minHeap.add(num);
			}
			modifyTwoHeapsSize();
		}

		public Integer getMedian() {
			int maxHeapSize = this.maxHeap.size();
			int minHeapSize = this.minHeap.size();
			if (maxHeapSize + minHeapSize == 0) {
				return null;
			}
			Integer maxHeapHead = this.maxHeap.peek();
			Integer minHeapHead = this.minHeap.peek();
			if (((maxHeapSize + minHeapSize) & 1) == 0) {
				return (maxHeapHead + minHeapHead) / 2;
			}
			return maxHeapSize > minHeapSize ? maxHeapHead : minHeapHead;
		}

	}

	public static class MaxHeapComparator implements Comparator<Integer> {
		@Override
		public int compare(Integer o1, Integer o2) {
			if (o2 > o1) {
				return 1;
			} else {
				return -1;
			}
		}
	}

	public static class MinHeapComparator implements Comparator<Integer> {
		@Override
		public int compare(Integer o1, Integer o2) {
			if (o2 < o1) {
				return 1;
			} else {
				return -1;
			}
		}
	}

	// for test
	public static int[] getRandomArray(int maxLen, int maxValue) {
		int[] res = new int[(int) (Math.random() * maxLen) + 1];
		for (int i = 0; i != res.length; i++) {
			res[i] = (int) (Math.random() * maxValue);
		}
		return res;
	}

	// for test, this method is ineffective but absolutely right
	public static int getMedianOfArray(int[] arr) {
		int[] newArr = Arrays.copyOf(arr, arr.length);
		Arrays.sort(newArr);
		int mid = (newArr.length - 1) / 2;
		if ((newArr.length & 1) == 0) {
			return (newArr[mid] + newArr[mid + 1]) / 2;
		} else {
			return newArr[mid];
		}
	}

	public static void printArray(int[] arr) {
		for (int i = 0; i != arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	public static void main(String[] args) {
		boolean err = false;
		int testTimes = 200000;
		for (int i = 0; i != testTimes; i++) {
			int len = 30;
			int maxValue = 1000;
			int[] arr = getRandomArray(len, maxValue);
			MedianHolder medianHold = new MedianHolder();
			for (int j = 0; j != arr.length; j++) {
				medianHold.addNumber(arr[j]);
			}
			if (medianHold.getMedian() != getMedianOfArray(arr)) {
				err = true;
				printArray(arr);
				break;
			}
		}
		System.out.println(err ? "Oops..what a fuck!" : "today is a beautiful day^_^");

	}
}

比较器

  • 比较器的实质就是重载比较运算符
  • 比较器可以很好的应用在特殊标准的排序上
  • 比较器可以很好的应用在根据特殊标准排序的结构上

对所有比较器而言:
返回负数的时候,第一个参数排在前面;
返回正数的时候,第二个参数排在前面;
返回0的时候,前后无所谓;

package class03;

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.TreeSet;

public class Code03_Comparator {

	public static class Student {
		public String name;
		public int id;
		public int age;

		public Student(String name, int id, int age) {
			this.name = name;
			this.id = id;
			this.age = age;
		}
	}

	public static class IdAscendingComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o1.id - o2.id;
		}

	}

	public static class IdDescendingComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o2.id - o1.id;
		}

	}

	public static class AgeAscendingComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o1.age - o2.age;
		}

	}

	public static class AgeDescendingComparator implements Comparator<Student> {

		@Override
		public int compare(Student o1, Student o2) {
			return o2.age - o1.age;
		}

	}

	public static void printStudents(Student[] students) {
		for (Student student : students) {
			System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
		}
	}

	public static void printArray(Integer[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	public static class MyComp implements Comparator<Integer> {

		@Override
		public int compare(Integer o1, Integer o2) {
			return o2 - o1;
		}

	}

	public static void main(String[] args) {
		Student student1 = new Student("A", 2, 23);
		Student student2 = new Student("B", 3, 21);
		Student student3 = new Student("C", 1, 22);

		Student[] students = new Student[] { student1, student2, student3 };

		Arrays.sort(students, new IdAscendingComparator());
		printStudents(students);
		System.out.println("===========================");

		Arrays.sort(students, new IdDescendingComparator());
		printStudents(students);
		System.out.println("===========================");

		Arrays.sort(students, new AgeAscendingComparator());
		printStudents(students);
		System.out.println("===========================");

		Arrays.sort(students, new AgeDescendingComparator());
		printStudents(students);
		System.out.println("===========================");
		System.out.println("===========================");
		System.out.println("===========================");
		System.out.println("===========================");

		PriorityQueue<Student> maxHeapBasedAge = new PriorityQueue<>(new AgeDescendingComparator());
		maxHeapBasedAge.add(student1);
		maxHeapBasedAge.add(student2);
		maxHeapBasedAge.add(student3);
		while (!maxHeapBasedAge.isEmpty()) {
			Student student = maxHeapBasedAge.poll();
			System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
		}
		System.out.println("===========================");

		PriorityQueue<Student> minHeapBasedId = new PriorityQueue<>(new IdAscendingComparator());
		minHeapBasedId.add(student1);
		minHeapBasedId.add(student2);
		minHeapBasedId.add(student3);
		while (!minHeapBasedId.isEmpty()) {
			Student student = minHeapBasedId.poll();
			System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
		}
		System.out.println("===========================");
		System.out.println("===========================");
		System.out.println("===========================");

		TreeSet<Student> treeAgeDescending = new TreeSet<>(new AgeDescendingComparator());
		treeAgeDescending.add(student1);
		treeAgeDescending.add(student2);
		treeAgeDescending.add(student3);

		Student studentFirst = treeAgeDescending.first();
		System.out.println("Name : " + studentFirst.name + ", Id : " + studentFirst.id + ", Age : " + studentFirst.age);

		Student studentLast = treeAgeDescending.last();
		System.out.println("Name : " + studentLast.name + ", Id : " + studentLast.id + ", Age : " + studentLast.age);
		System.out.println("===========================");

	}
}

基数排序

出桶入桶的次数由最大数十进制位有几位来决定的

package class03;

import java.util.Arrays;

public class Code02_RadixSort {

	// only for no-negative value
	public static void radixSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		radixSort(arr, 0, arr.length - 1, maxbits(arr));
	}

	public static int maxbits(int[] arr) {
		int max = Integer.MIN_VALUE;
		for (int i = 0; i < arr.length; i++) {
			max = Math.max(max, arr[i]);
		}
		int res = 0;
		while (max != 0) {
			res++;
			max /= 10;
		}
		return res;
	}

	public static void radixSort(int[] arr, int begin, int end, int digit) {
		final int radix = 10;
		int i = 0, j = 0;

		int[] bucket = new int[end - begin + 1];
		for (int d = 1; d <= digit; d++) { // 有多少位就进出多少次
			
			// 10个空间 count[0..9]
			// count[0] 当前位(d位)是0的数字有多少个
			// count[1] 当前位(d位)是0和1的数字有多少个
			// count[2] 当前位(d位)是0、1、2的数字有多少个
			// count[i] 当前位(d位)是0~i的数字有多少个
			
			int[] count = new int[radix];
			for (i = begin; i <= end; i++) {
				j = getDigit(arr[i], d);
				count[j]++;
			}
			for (i = 1; i < radix; i++) {
				count[i] = count[i] + count[i - 1];
			}
			for (i = end; i >= begin; i--) {
				j = getDigit(arr[i], d);
				bucket[count[j] - 1] = arr[i];
				count[j]--;
			}
			for (i = begin, j = 0; i <= end; i++, j++) {
				arr[i] = bucket[j];
			}
		}
	}

	public static int getDigit(int x, int d) {
		return ((x / ((int) Math.pow(10, d - 1))) % 10);
	}

	// for test
	public static void comparator(int[] arr) {
		Arrays.sort(arr);
	}

	// for test
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) ((maxValue + 1) * Math.random());
		}
		return arr;
	}

	// for test
	public static int[] copyArray(int[] arr) {
		if (arr == null) {
			return null;
		}
		int[] res = new int[arr.length];
		for (int i = 0; i < arr.length; i++) {
			res[i] = arr[i];
		}
		return res;
	}

	// for test
	public static boolean isEqual(int[] arr1, int[] arr2) {
		if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
			return false;
		}
		if (arr1 == null && arr2 == null) {
			return true;
		}
		if (arr1.length != arr2.length) {
			return false;
		}
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] != arr2[i]) {
				return false;
			}
		}
		return true;
	}

	// for test
	public static void printArray(int[] arr) {
		if (arr == null) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
		System.out.println();
	}

	// for test
	public static void main(String[] args) {
		int testTime = 500000;
		int maxSize = 100;
		int maxValue = 100000;
		boolean succeed = true;
		for (int i = 0; i < testTime; i++) {
			int[] arr1 = generateRandomArray(maxSize, maxValue);
			int[] arr2 = copyArray(arr1);
			radixSort(arr1);
			comparator(arr2);
			if (!isEqual(arr1, arr2)) {
				succeed = false;
				printArray(arr1);
				printArray(arr2);
				break;
			}
		}
		System.out.println(succeed ? "Nice!" : "Fucking fucked!");

		int[] arr = generateRandomArray(maxSize, maxValue);
		printArray(arr);
		radixSort(arr);
		printArray(arr);

	}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值