[Fortran] 纯文本查看 复制代码
module heap
! this module implements basic functions to realize a heap structure.
implicit none
save
private
integer, parameter :: sp = selected_real_kind(6,37)
integer, parameter :: i4b = selected_int_kind(9)
public :: heapify
public :: heapify_alt
! public :: nsmallest
public :: nlargest
public :: sp
public :: i4b
contains
subroutine sift_down(pos,end_pos,arr)
! element array(pos) = temp is out of position, the function moves the temp element
! down "promotes" the descendent elements larger than array(pos), till array(pos)
! hits a leaf. The loop stops as well when array(pos) is larger than both daughters.
! this requires a supplementary comparison.
implicit none
integer(i4b), intent(in) :: pos, end_pos
real(sp), dimension(:), intent(inout) :: arr
integer(i4b) :: j, jold
real(sp) :: temp
temp = arr(pos)
jold = pos
j = pos+pos ! leftmost daughter of arr(pos)
do
if (j>end_pos) exit ! hitting the leaf
if (j<end_pos) then ! compare with the larger element between two daughters
if (arr(j) < arr(j+1)) j = j + 1
end if
if (temp >= arr(j)) exit
arr(jold) = arr(j)
jold = j
j = j + j
end do
arr(jold) = temp
end subroutine sift_down
subroutine sift_up(pos,start_pos,arr)
! element arr(pos) is out of position, the heap is heap invariant at all
! positions <=start_pos. pos > startpos moves arr(pos) up and moves parent
! element down if smaller.
implicit none
real(sp), dimension(:), intent(inout) :: arr
integer(i4b), intent(in) :: start_pos,pos
integer(i4b) :: parent_pos, old_pos
real(sp) :: temp
old_pos = pos
temp = arr(pos)
do
if (old_pos <= start_pos) exit
parent_pos = old_pos/2
if (arr(parent_pos) < temp) then
arr(old_pos) = arr(parent_pos)
old_pos = parent_pos
else
exit
end if
end do
arr(old_pos) = temp
end subroutine sift_up
subroutine sift_down_alt(pos,end_pos,arr)
! alternative way of move the arr(pos) = temp down till to the leaf. the comparison
! between temp and the bigger daughter element is not made. As a result, the temp
! will reach the leaf no matter what. a sift down is performed afterwards to put
! the temp in proper position.
implicit none
integer(i4b), intent(in) :: pos, end_pos
real(sp), dimension(:), intent(inout) :: arr
real(sp) :: temp
integer(i4b) :: daughter_pos, old_pos, start_pos
temp = arr(pos)
start_pos = pos
old_pos = pos
daughter_pos = pos+pos
do
if (daughter_pos > end_pos) exit ! hitting the leaf
if (daughter_pos < end_pos) then ! if the element has got two daughters
if (arr(daughter_pos) < arr(daughter_pos+1)) daughter_pos = daughter_pos + 1
end if
arr(old_pos) = arr(daughter_pos)
old_pos = daughter_pos
daughter_pos = daughter_pos + daughter_pos
end do
arr(old_pos) = temp
call sift_up(old_pos,start_pos,arr)
end subroutine sift_down_alt
subroutine heapify(arr)
implicit none
real(sp), dimension(:), intent(inout) :: arr
integer(i4b) :: i, n
n = size(arr)
do i = n/2, 1, -1
call sift_down(i,n,arr)
end do
end subroutine heapify
subroutine heapify_alt(arr)
implicit none
real(sp), dimension(:), intent(inout) :: arr
integer(i4b) :: i, n
n = size(arr)
do i = n/2, 1, -1
call sift_down_alt(i,n,arr)
end do
end subroutine heapify_alt
function nlargest(arr, n) result(nlarge_arr) ! return the first nth largest elements
implicit none
real(sp), dimension(:), intent(inout) :: arr
integer(i4b), intent(in) :: n
real(sp), dimension(n) :: nlarge_arr
integer(i4b) :: i, j
call heapify(arr)
j = size(arr)
do i = 1, n
nlarge_arr(i) = arr(1)
arr(1) = arr(j)
arr(j) = nlarge_arr(i)
j = j-1
call sift_down(1,j,arr)
end do
end function nlargest
end module heap