0

I found several mergesort algorithms in the net and pasted together this one. it carries the indices along to keep the information where the element was before. but it turns out, that when i feed in an array which has the same value at each element the indices are shuffled. do i need to use another merge method?

procedure TMSortNode.MergeSort(values, indices: TMValueArrayPtr; L, R: Integer);
var
  i, j, k, m : Integer;

begin
If (l < r) Then
begin
m:= (r+l) div 2;

MergeSort(values, indices, l, m );
MergeSort(values, indices, m+1, r );

For i:= l To m Do
begin
  FValueBuffer[i]:= values^[i];
  FIndexBuffer[i]:= indices^[i];
end;
i:= l;

For j:= m+1 To r Do
begin
  FValueBuffer[r+m+1-j]:= values^[j];
  FIndexBuffer[r+m+1-j]:= indices^[j];
end;
j:= r;

For k:= l To r Do
begin
  If (FValueBuffer[i] <= FValueBuffer[j]) Then
  begin
    values^[k]:= FValueBuffer[i];
    indices^[k]:= FIndexBuffer[i];
    inc(i);
  end
  else
  begin
    values^[k]:= FValueBuffer[j];
    indices^[k]:= FIndexBuffer[j];
    dec(j);
     end;
    end;
  end;
end;

EDIT:

this is the correct stable merge part:

length := r-l+1;
For i:= 0 To length-1 Do
begin
  FValueBuffer[i]:= values^[l+i];
  FIndexBuffer[i]:= indices^[l+i];
end;

j := 0;
k := m-l+1;

for i := 0 to length-1 do
if k < length then
  begin
  if j <= m-l then
  begin
    if FValueBuffer[j] > FValueBuffer[k] then begin
      values^[l+i] := FValueBuffer[k];
      indices^[l+i] := FIndexBuffer[k];
      inc(k)
    end else begin
      values^[l+i] := FValueBuffer[j];
      indices^[l+i] := FIndexBuffer[j];
      inc(j);
    end;
  end
  else
  begin //only upper Entrys left
    values^[i+l] := FValueBuffer[k];
    indices^[i+l] := FIndexBuffer[k];
    inc(k);
  end;
end else begin //only superior Entrys left
  values^[i+l] := FValueBuffer[j];
  indices^[i+l] := FIndexBuffer[j];
  inc(j);
end;
thalm
  • 2,738
  • 2
  • 35
  • 49

1 Answers1

1

try

If (FValueBuffer[i] <= FValueBuffer[j]) Then ...

(to keep stability, we have not to exchange equal items)

Edit:

Another issue: standard merging looks like:

  1. while both left and right part contains untreated items, move the smallest item to the new place
  2. move the rest of untreated part (if exists)

Example:

procedure Merge(var c, d: array of Integer; l, m, r: Integer);
 var
   i, j, k: Integer;
 begin
   i := l;
   j := m;
   k := l;
   while (i < m) and (j <= r) do
     if c[i] <= c[j] then begin
       d[k] := c[i];
       Inc(k);
       Inc(i);
     end
     else begin
       d[k] := c[j];
       Inc(k);
       Inc(j);
     end;
   while i < m do begin
     d[k] := c[i];
     Inc(k);
     Inc(i);
   end;
   while k <= r do begin
     d[k] := c[j];
     Inc(k);
     Inc(j);
   end;
  end;

But your first variant goes beyond the left-right part border (to avoid this, one could use sentinel item(s) with extra value - it takes additional efforts and place).

It seems that this problem is taken into account in the second version

MBo
  • 77,366
  • 5
  • 53
  • 86
  • good hint! thats much better, but it still has a few indices which are moved around... any idea? – thalm Sep 13 '12 at 17:39