已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。
给定一个int数组A,同时给定A的大小n和题意中的k,请返回排序后的数组。
测试样例:
[2,1,4,3,6,5,8,7,10,9],10,2
返回:[1,2,3,4,5,6,7,8,9,10]
根据题意,最小值一定在A的前k(0~k-1)个元素内、次小值一定在A的第二组k(1~k)个元素内,以此类推,每次取原数组A的k个元素进行堆排序,然后把堆顶元素放入A的已排序序列中,(0~k-1)排序的堆顶赋值给A[0],(1~k)排序的堆顶赋值给A[1],以此类推。最后剩下A中最后的k个元素(k-n~n-1),进行标准的堆排序,第一次是K个元素的堆,排序后将堆顶赋值给A[n-k],紧接着把堆尾元素赋值给堆顶,再排序,此时只是k-1个元素的堆,依次类推,每次把堆顶元素放入A中已排序序列后,都把堆尾元素赋值到堆顶,同时堆的元素个数减1,直到堆的元素个数为1,整个排序结束。
#include <iostream> #include <algorithm> #include "string.h" #include "stdio.h" #include <vector> #include <deque> #include<stack> #include <assert.h> using namespace std; class Sort { public: int *data; int count;//堆中存在的节点数 int capacity; int* heapSort(int* A, int n,int k) { data = new int[k+1]; capacity = n; for( int i = 0 ; i < k ; i ++ ) { data[i+1] = A[i];//使堆的结点存入data数组中时下标是从1开始 } count = k; //构造堆 for( int i = count/2 ; i >= 1 ; i -- )//从树中第一个不是叶子结点的索引开始 shiftDown(i); for (int i = 0; i <n - k; i++){ A[i] = data[1];//取堆顶元素,即最小值放入原数组中 data[1] = A[i + k];//放入未处理的结点 shiftDown(1);//调整最小堆 } //剩下A中最后的k个元素(k-n~n-1),进行标准的堆排序 for( int i = 0 ; i <k ; i++ ) A[i+n-k] = extractMax(); return A; } int extractMax() { assert(count>0); int ret = data[1];//堆中最大的结点 swap( data[1] , data[count] );//令最大节点与最后一个节点交换 count --;//删除最大节点 shiftDown(1);//对刚刚交换上去的节点进行调整,使其符合最大堆的形式 return ret; } void shiftUp(int k){ while( k > 1 && data[k/2] < data[k] ){ swap( data[k/2], data[k] ); k /= 2; } } void shiftDown(int k){ while( 2*k <= count )//如果该节点有左孩子 { int j = 2*k;//左孩子的索引值 if( j+1 <= count && data[j+1] < data[j])//如果该节点有右孩子,并且右孩子的值小于左孩子 j ++;//更新为右孩子的索引值 if( data[k] <= data[j])//如果该结点小于他的孩子结点 break; swap(data[k],data[j]);//否则使该节点与孩子中最小的结点交换 k = j; } } }; int main() { int array[]={3,2,1,6,5,4,9,8,7,11,10}; Sort sort; int len = sizeof(array)/sizeof(array[0]); int* arr = sort.heapSort(array,len,3); for(int i=0;i<len;i++) { cout<<arr[i]<<" "; } cout<<endl; return 0; }