理解算法(1): 最大值,最小值,和堆。

最近總想,算法好像沒有數學那樣直觀,例如方程可以解決一大類問題,我們遇到許多數學問題,只要將其轉成方程問題,剩下的就是解方程。算法好像不是那麼直觀,順着這個思路開始重新看算法問題。今天有一個收穫,也可能其他人早就知道。

int max=INT_MIN;
for(size_t i=0;i<v.size();i++){
   if(v[i]>max){
       max=v[i];
   }
}

int min=INT_MAX;
for(size_t i=0;i<v.size();i++){
   if(v[i]<min){
       min=v[i];
   }
}

我今天才意識到,這裏的
a)int max=INT_MIN; 和 if(v[i]>max)
b)int min=INT_MAX; 和 if(v[i]<min)

居然,分別就是隱含的堆。其中,
a) int max=INT_MIN; 是元素只有一個的最大堆。
b) int min=INT_MAX; 是元素只有一個的最小堆。

從這個角度來說,很多利用堆來解決的算法,本質上就和上面這兩個for循環一樣。

例如,求k個最小值:

// 步驟1,等價於 int min = INT_MAX;
MinHeap heap;
for(size_t i=0;i<k;i++){
  heap.push(v[i]);
}

// 步驟2,等價於 if(v[i]<min) 然後交換
for(size_t i=k;i<v.size();i++){
  if(v[i]<heap.top()){
      heap.pop();
      heap.push(v[i]);
  }
}

例如,求k個最大值

// 步驟1,等價於 int max = INT_MIN;
MaxHeap heap;
for(size_t i=0;i<k;i++){
  heap.push(v[i]);
}

// 步驟2,等價於 if(v[i]>max) 然後交換
for(size_t i=k;i<v.size();i++){
  if(v[i]>heap.top()){
      heap.pop();
      heap.push(v[i]);
  }
}

從這個角度來說,似乎:求最大值和最小值,就是堆問題。只是堆的元素可能是1個,也可能是k個,進一步堆每個元素可能是一個向量,就像方程和方程組的關係。

下一個例子是,求多個數組裏,覆蓋範圍最小的區間,使得區間包含了每個數組至少一個元素。例如

輸入數據:

[ 3, 6, 8, 10, 15 ]
[ 1, 5, 12 ]
[ 4, 8, 15, 16 ]
[ 2, 6 ]

輸出:

4–6

這仍然是一個求「最小值」問題,最小值變成了一個最小區間,基本思路還是

  • 使用一個最小堆存區間
  • 循環往前迭代,比較新區間是否比當前區間更小,如果是就替換。

如果把lists看成矩陣,

  • 初始化:首先取每個數組裏最小的元素,一共4個數構成了一個數組
  • 更新:刪除4個元素裏最小的數,同時從這個數所在的原list裏取下一個數填充

那麼,就會得到一種新的組合數構成的數組

[ {3,1,4,2} {3,5,4,2} {3,5,4,6}, ... ]

對於這個數組,用一個for循環就可以遍歷出最小區間,這個區間包含每個list至少一個元素:

MinHeap heap;
int high=0;
pair<int,int> min_range={0,INT_MAX};
for(size_t i=0;i<lists.size(); i++){
  auto e = {value=lists[i][0], row=i, column=0};
  heap.push(e);
  high = max(high, e.value);
}

while(true){
  auto e = heap.top();
  heap.pop();

  low = e.value;
  if(high-low<min_range.second-min_range.first){
     min_range.first = low;
     min_range.second = high; 
  }
  
  if(e.column+1<lists[e.row].size()){
    next_value = lists[e.row][e.column+1];
    next_row = e.row;
    next_column = e.column+1;
    heap.push({value:next_value, row: next_row, column: next_column});
  }else{
     break;
  }
}

從這個角度來說,如何看待「數組」,就是需要重新審視的,正確的角度看“數組”,就可以獲得更簡潔的算法。

--end--

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章