Почему эта реализация MergeSort нестабильна?
Я нашел несколько алгоритмов слияния в сети и вставил вместе этот. это несет индексы, чтобы сохранить информацию, где элемент был прежде. но получается, что когда я добавляю массив, который имеет одинаковое значение для каждого элемента, индексы перемешиваются. мне нужно использовать другой метод слияния?
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;
РЕДАКТИРОВАТЬ:
это правильная стабильная часть слияния:
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;
1 ответ
Пытаться
If (FValueBuffer [i] <= FValueBuffer [j]) Тогда...
(чтобы сохранить стабильность, мы не должны обменивать равные предметы)
Редактировать:
Еще одна проблема: стандартное слияние выглядит так:
- в то время как левая и правая части содержат необработанные предметы, переместите самый маленький предмет в новое место
- переместить оставшуюся необработанную часть (если есть)
Пример:
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;
Но ваш первый вариант выходит за границы левой и правой частей (чтобы избежать этого, можно использовать сторожевые элементы с дополнительной ценностью - это требует дополнительных усилий и места).
Похоже, эта проблема учтена во второй версии