这是我的第一篇技术博客,从一个小算法题写起。
小范围排序:已知一个几乎有序的数组,几乎有序指的是如果把数组排好序,每个元素移动距离不超过k,且k相对于原数组大小来说很小。比如一个数组[3,1,2,5,6,4,8,7,9]大小为9,我们可以发现每个元素移动不需要超过k=3就可以完成从小到大的排序,我们要选择一个合适的排序算法来对数据进行排序。
考虑时间复杂度,我们使用改进后的堆排序,即有一组数组A[0],A[1]...A[k-1]...A[n-1]。
首先根据A[0]...A[k-1]建立从小到大排序的小根堆B[k],将堆的第一个元素(最小)弹出,放在位置A[0]。
再根据A[1]...A[k]建立从小到大排序的小根堆B[k],将堆的第一个元素(第二小)弹出,放在位置A[1]。
.
.
.
这样依次弹出,直到弹出N-k个数,我们再对结尾的最后k个数进行一次堆排序并将其插入到原数组A中的最后几位就好了。
我们每得到一个数,时间复杂度为O(logK),总共有N个数,所以此算法的时间复杂度为O(N*logK)。
附代码:
import java.util.Scanner; public class Sort { public static int[] sortElement(int[] A, int n, int k) { // write code herep int[] B = new int[k]; int flag = 0; for (int p = 0; p <= n-k; p++) { for (int i = 0; i < k; i++) { B[i] = A[i + p];//建立小根堆 } B = buildHeap(B); for(int i = p;i < p+k; i++){ if(A[i] == B[0]){//将堆顶弹出,放在数组A中 flag = i;//对每一个堆处理,标记弹出数在A中位置 } } int tem = A[p];//每运行一轮,B[0]弹出插入到A[p]位置上,我们需要将A中弹出的数A[flag]填上原本在A[p]位置上的数 A[p] = B[0]; A[flag] = tem; } for(int i=k-1;i>0;i--){//对最后k个数进行堆排序,从大到小 int t = B[0]; B[0] = B[i]; B[i] = t; adjustHeap(B,0,i); } for(int i=0;i<k;i++) {//将最后K个数反序插入到原数组A中 A[n - k + i ] = B[k - i - 1]; } return A; } private static int[] buildHeap(int[] A) { //建立堆 for (int i = A.length - 1; i >= 0; i--) { adjustHeap(A, i, A.length); } return A; } private static void adjustHeap(int[] A, int k, int len) {//对堆的数字进行调整 int tem = A[k]; for (int i = 2 * k + 1; i < len; i = 2 * i + 1) { if (i != len - 1 && A[i] > A[i + 1]) { i++;//如果左子树大于右子树,那么取右子树 } if (tem < A[i]) {//将子树与树根比较 break; } else { A[k] = A[i]; k = i;//修改K值,以方便下调 } } A[k] = tem; } public static void main(String[] args) { Scanner read = new Scanner(System.in); int n; //元素个数 System.out.println("请输入数组元素个数"); n = read.nextInt(); int[] arr; arr = new int[n]; int i, k; for (i = 0; i < arr.length; i++) { arr[i] = read.nextInt(); } System.out.println("请输入移动距离"); k = read.nextInt(); arr = sortElement(arr,n,k); for (i = 0; i < arr.length; i++) { System.out.print(arr[i]+" "); } } }