題意:給定一串數字,一個定長的視窗,視窗每次平移一個位置,輸出視窗中的最大值。
題解:
堆與映射
平常,我們用堆,最常見的就是隨機地加入元素,隨機地取最大值或最小值。這些基本的操作C++中的priority_queue和set都能很好的完成,而且C++中還有一個make_heap,效率較前面2個會更高。而且前面提到的STL都是採用紅/黑樹狀結構實現的,很具有穩定性。上面的堆雖然使用簡單,但功能上還是有些局限。比如前面提到的堆都只能實現刪除最值並沒有辦法刪除指定的值。而且一般STL中的堆都是採用資料存放區的,但父親節點和兒子節點需要交換時,我們需要拷貝所儲存的整個資料單元,但資料單元較大時,拷貝的效率就低了。因此,這裡介紹一中堆變種,在堆中引入了映射。定義 具有映射功能的堆稱為雙向映射堆。又其常常是在二分堆的基礎上實現的,所以也常常叫映射二分堆。特點 映射二分堆其與普通堆不同的地方是它的節點並不真正儲存資料單元本身,而是儲存指向資料單元的指標。因此當需要交換父子節點的資料時,我們可以避免拷貝大量資料所消耗的時間。同時,映射二分堆還有一個功能可以根據具體的資料單元的索引來刪除該單元,即使這個單元不是堆中的最值。實現方法 在實現是,我們可以用數字H[]儲存資料單元,然後採用數組ind[i]=j表示H[i]存放的是索引號為j的資料,mp[i]=j表示索引索引為i的元素儲存在H[j]中,這樣就可以實現雙向映射了。插入堆,我們只需要將插入的元素放在堆尾,然後逐級調整,這個跟普通的二元堆積一樣樣。刪除最值,也跟普通的二元堆積一樣,先把堆中最後一個元素與最值對調,然後在調整堆。增加一個的是刪除固定索引的資料單元,我們可以先通過mp[i],找到其在H[]中存放的位置,然後該位置的值跟堆最後一個元素對調,接著從該節點向上調整堆,此時得到的還不是一個正規的堆,還需要重新堆整個堆進行從上到下調整。上面的3中操作中都提到了堆的調整,還是上面說的,調整時我們不需要拷貝資料單元,我們只需交換ind[]和mp[]2個數組的對應值即可。
#include <cstdio>#define M 1000005int n, k;struct min_Heap{int array[M], pos[M], id[M]; // id[i]表示堆內第i個元素對應的數組下標,pos[i]表示數組第i個元素在堆內的位置。void siftDown ( int start ) //下滑調整,若子女的值小於父節點的值,則父節點上浮,之後繼續向下層比較{int i = start, j = i * 2 + 1;int temp = array[start];while ( j < k ){if ( j < k-1 && array[j] > array[j+1] ) ++j;if ( temp <= array[j] ) break;else { exch( i, j ); i = j; j = j * 2 + 1; }}}void siftUp ( int start ) //上滑調整,若子女的值小於父節點的值則互相交換{int j = start, i = ( j - 1 ) / 2;int temp = array[j];while ( j > 0 ){if ( temp >= array[i] ) break;else { exch ( j, i ); j = i; i = ( i - 1 ) / 2; }}} void build_minHeap () //建堆{int current = ( k - 2 ) / 2;while ( current >= 0 ){siftDown ( current );current--;}}void exch ( int x, int y ){int temp;pos[id[x]] = y; pos[id[y]] = x;temp = id[x]; id[x] = id[y]; id[y] = temp;temp = array[x]; array[x] = array[y]; array[y] = temp;}} minHeap;struct max_Heap{int array[M], pos[M], id[M];void siftDown ( int start ){int i = start, j = i * 2 + 1;int temp = array[start];while ( j < k ){if ( j < k-1 && array[j] < array[j+1] ) ++j;if ( temp >= array[j] ) break;else { exch( i, j ); i = j; j = j * 2 + 1; }}}void siftUp ( int start ){int j = start, i = ( j - 1 ) / 2;int temp =array[j];while ( j > 0 ){if ( temp <= array[i] ) break;else { exch( j, i ); j = i; i = ( i - 1 ) / 2; }}} void build_maxHeap (){int current = ( k - 2 ) / 2;while ( current >= 0 ){siftDown ( current );current--;}}void exch ( int x, int y ){int temp;pos[id[x]] = y; pos[id[y]] = x;temp = id[x]; id[x] = id[y]; id[y] = temp;temp = array[x]; array[x] = array[y]; array[y] = temp;}} maxHeap;int main(){int i, t;scanf("%d%d",&n,&k);for ( i = 0; i < n; ++i ){scanf("%d", &minHeap.array[i]);maxHeap.array[i] = minHeap.array[i];minHeap.pos[i] = minHeap.id[i] = i;maxHeap.pos[i] = maxHeap.id[i] =i;}minHeap.build_minHeap (); printf("%d ", minHeap.array[0] );for ( i = k; i < n; ++i ){t = minHeap.pos[i-k];minHeap.exch( i, t );minHeap.siftUp ( t );minHeap.siftDown ( t );printf("%d ", minHeap.array[0] );}putchar('\n');maxHeap.build_maxHeap();printf("%d ", maxHeap.array[0] );for ( i = k; i < n; ++i ){t = maxHeap.pos[i-k];maxHeap.exch( i, t );maxHeap.siftUp ( t );maxHeap.siftDown ( t );printf("%d ", maxHeap.array[0] );}putchar('\n'); return 0;}
下面是單調隊列的解法。 基本仿照http://blog.chinaunix.net/space.php?uid=22753395&do=blog&cuid=2208489的寫法
#include <iostream>using namespace std;const int M = 1000005;int a[M], que[M], Min[M], Max[M], id[M];int n, k;void get_min(){int i, head = 1, tail = 0;for ( i = 1; i < k; ++i ){while ( head <= tail && que[tail] > a[i] )--tail; ++tail; que[tail] = a[i]; id[tail] = i;}for ( i = k; i <= n; ++i ){while ( head <= tail && que[tail] > a[i] )--tail;++tail;que[tail] = a[i];id[tail] = i;while ( id[head] <= i-k )++head;Min[i-k] = que[head];}}void get_max (){int i, head = 1, tail = 0;for ( i = 1; i < k; ++i ){while ( head <= tail && que[tail] < a[i] )--tail;++tail;que[tail] = a[i];id[tail] = i;}for ( i = k; i <= n; ++i ){while ( head <= tail && que[tail] < a[i] )--tail;++tail;que[tail] = a[i];id[tail] = i;while ( id[head] <= i-k )++head;Max[i-k] = que[head];}}int main(){int i;scanf("%d%d", &n, &k );for ( i = 1; i <= n; ++i )scanf("%d",a+i);get_min();get_max();for ( i = 0; i < n-k+1; ++i )printf("%d ", Min[i] );putchar('\n');for ( i = 0; i < n-k+1; ++i )printf("%d ", Max[i] );putchar('\n');return 0;}