Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Работа со StringGrid'ом и не только


Автор: MacTep 16.6.2004, 08:58
Помогите, кто может! У меня есть несколько вопросов:

1) На форме есть компонент StringGrid. Его свойство Options.goRowCount установлено в True. Как мне узнать в любой момент времени, сколько именно и какие строки выделены? Вообще, есть ли какое-нибудь свойство у StringGrid'а, которое отвечает за выделение ячеек или целых строк?

2) Еще вопрос по StringGrid'у. Как сделать так, чтобы вывести на печать содержимое StringGrid'a в виде таблицы?

3) Как вывести на печать содержимое формы без заголовка, т.е. только то, что лежит внутри окна формы?

4) Вопрос не в тему, но все-таки вопрос: какая из сортировок массивов эффективнее всего сортирует массивы длинной где-то 500-1500 элементов. Очень надо сортировать, но простыми сортировками получается очень медленно!

Заранее всем спасибо! Буду рад, если получу исчерпывающие ответы на свои вопросы! cool.gif

Автор: Serggggg 16.6.2004, 09:56
(2). Я так решаю проблему с DBGrid'ом - Через EXCEL! Поднять ядро Excel, создать/открыть документ (готовый шаблон ведомости, например).
А потом 2 цикла - по кол-ву строк и по кол-ву столбцов - выводят в нужные ячейки ячейки Grid'а - Excel.Cells.Item[i,j].Value:=...

Автор: Serggggg 16.6.2004, 10:07
(4). В принципе, нас ещё со времен 1-го курса учили сортировать так (по-крайней мере, преподы утверждали, что это лучший способ):
Код

var
i,j :integer;
k :real;
X :array[1..N] of real;
begin
j:=0;
LABEL1:
for i:=1 to N-1 do begin
     if X[i]>X[i+1] then begin
        k:=X[i];
        X[i]:=X[i+1];
        X[i+1]:=k;
        j:=j+1;
        end;
if j>0 then goto LABEL1
end;


Т.е. признак того, была ли произведена в ходе проверки хотя бы одна перестановка элементов (в данном примере - j). Когда в последний раз массив будет просмотрен и будет обнаружено, что он упорядочен, то j будет равняться 0 - задача решена.
Добавлено @ 10:11
Прошу прощения - в строке
Код
for i:=1 to N-1 do begin

слово BEGIN нужно убрать или перед строкой
Код
if j>0 then goto LABEL1

вставить ещё 1 end;

Автор: Pakshin A. S. 16.6.2004, 10:23
Уважайте правила форума!!! Один топик - один вопрос!

№3: Это как?

Автор: MacTep 16.6.2004, 17:30
1) Помогите именно со StringGrid'ом, так как проблема именно в нем. Не надо сюда Excel приплетать.
2) За правила форума простите, я обязательно исправлюсь!
3) Про печать StringGrid'а обязательно подскажите, я же знаю, что здесь полно умных людей.
4) Сортировку "пузырьком" (а именно такая и была предложена) не предлагать. Это все лажа. Есть более эффективные сортировки. Ок?

По поводу третьего вопроса: есть форма. Там кнопки, все такое прочее. Так вот как это все такое прочее и кнопки вывести, но только так, чтобы не было видно, что вывелось целое окно? Доходчиво?

Автор: Serggggg 16.6.2004, 17:40
Пожалуйста.
Ищи более эффективные методы. Только грубить не надо - я всего лишь предложил свой вариант. Я же не ручался за то, что это самое рациональное решение.

Автор: Albinos_x 16.6.2004, 18:42
По сортировке:
// сортировка массива целых чисел с вабором минимального элемента

procedure MinSortInt( var M : array of integer );
var i, j, n, Imin, Rtmp: integer;
begin
N:=High(M);
for i:=0 to N-1 do
begin
Imin:=N;
for j:=n-1 downto i do
if (M[j] <= M[Imin]) then
Imin:=j;
if (i<Imin) then
begin
// Перестановка элементов
Rtmp:=M[Imin];
M[Imin]:=M[i];
M[i]:=Rtmp;
end;
end;
end;

Немного усовершенствованная пузырьковая сортировка.
Совершенствование заключается в том, что в пузырьковой сортировке
количество сравнений n*(n-1)/2
количество перестановок n*(n-1)/2
в предложенной
количество сравнений n*(n-1)/2
количество перестановок n-1
недостаток метода:
Если массив отсортирован, то все равно будут сделаны все сравнения, а метод пузырьковой сорировки ограничится n-1 сравнением

Автор: MacTep 17.6.2004, 14:19
Парни, никто не кому не грубил!!! А про быструю сортировку никто не слышал что ли?

Про StringGrid подскажитете, как там с выделением быть! Очень надо. И как содержимое StringGrid'а распечатать? Это самое главное!!! sad.gif

Автор: x77 18.6.2004, 04:37
MacTep, руками его печатать, как ещё... через TPrinter.Canvas. говоришь ему BeginDoc, рисуешь в цикле построчно, говоришь EndDoc.

Автор: Albinos_x 18.6.2004, 07:04
активную ячейку можно получить:

StringGrid1.Selection.Left - столбец

StringGrid1.Selection.Top - строка

Автор: x77 18.6.2004, 07:31
... а всё выделение, соответственно, через Selection.Right / Selection.Bottom

Автор: Albinos_x 18.6.2004, 07:43
Цитата
... а всё выделение, соответственно, через Selection.Right / Selection.Bottom


совершенно точно, забыл что нужно получить несколько выделенных ячеек, поэтому не указал

Автор: MacTep 18.6.2004, 23:02
x77, можно поподробнее с Canvas. Я с этим делом мало работал (почти не работал совсем), поэтому плохо пока в этом разбираюсь. Можно что-то типа исходного текста.

Про Selection тоже чуть подробнее. Ведь это всего лишь свойство, а как посмотреть, выставлено оно или нет?

Про форму: если я напишу Form1.Print, то что я получу на бумаге? Ответьте, пожалуйста.

Автор: U 19.6.2004, 10:21
Код

procedure TForm1.Button1Click(Sender: TObject);
var K: Double;
begin
Printer.BeginDoc;
K :=  Printer.Canvas.Font.PixelsPerInch / Canvas.Font.PixelsPerInch*1.2;

PrintStringGrid(StrGrid,
  K,   // Коэффициент
  200, // отступ от края листа в пихелах по Х
  200, // --"-- по Y
  200  // отступ снизу
  );

Printer.EndDoc;
end;


{----------------------------------------------------------}

unit GrdPrn3;

interface

uses
Windows, Classes, Graphics, Grids, Printers, SysUtils;

const
OrdinaryLineWidth: Integer = 2;
BoldLineWidth: Integer = 4;

procedure PrintStringGrid(Grid: TStringGrid; Scale: Double; LeftMargin, TopMargin, BottomMargin: Integer);

function DrawStringGridEx(Grid: TStringGrid; Scale: Double; FromRow,  LeftMargin, TopMargin,
Yfloor: Integer; DestCanvas: TCanvas): Integer;
// возвращает номер строки, которая не поместилась до Y = Yfloor

// не проверяет, вылезает ли общая длина таблицы за пределы страницы
// Слишком длинное слово обрежется

implementation

procedure PrintStringGrid(Grid: TStringGrid; Scale: Double; LeftMargin, TopMargin, BottomMargin: Integer);
var NextRow: Integer;
begin
//Printer.BeginDoc;

if not Printer.Printing then raise Exception.Create('function PrintStringGrid must be called between Printer.BeginDoc and Printer.EndDoc');

NextRow := 0;
repeat
  NextRow := DrawStringGridEx(Grid, Scale, NextRow, LeftMargin, TopMargin,
    Printer.PageHeight - BottomMargin, Printer.Canvas);
  if NextRow <> -1 then Printer.NewPage;
until NextRow = -1;

//Printer.EndDoc;
end;

function DrawStringGridEx(Grid: TStringGrid; Scale: Double; FromRow,  LeftMargin, TopMargin,
Yfloor: Integer; DestCanvas: TCanvas): Integer;
// возвращает номер строки, которая не поместилась до Y = Yfloor
var
i, j, d, TotalPrevH, TotalPrevW, CellH, CellW, LineWidth: Integer;
R: TRect;
s: string;


  procedure CorrectCellHeight(ARow: Integer);
  // вычисление правильной высоты ячейки с учетом многострочного текста
  // Текст рабивается только по словам слишком длинное слово обрубается
  var
    i, H: Integer;
    R: TRect;
    s: string;
  begin
    R := Rect(0, 0, CellH*2, CellH);
    s := ':)'; // Одинарная высота строки
    CellH := DrawText(DestCanvas.Handle, PChar(s), Length(s), R,
        DT_LEFT or DT_TOP or DT_WORDBREAK or DT_SINGLELINE or DT_NOPREFIX or DT_CALCRECT) + 3*d;
    for i := 0 to Grid.ColCount-1 do
    begin
      CellW := Round(Grid.ColWidths[i]*Scale);
      R := Rect(0, 0, CellW, CellH);
      //InflateRect(R, -d, -d);
      R.Left := R.Left+d;
      R.Top := R.Top + d;


      s := Grid.Cells[i, ARow];
      H := DrawText(DestCanvas.Handle, PChar(s), Length(s), R,
        DT_LEFT or DT_TOP or DT_WORDBREAK or DT_NOPREFIX or DT_CALCRECT); // Вычисление ширины и высоты текста
      if CellH < H + 2*d then CellH := H + 2*d;
      // if CellW < R.Right - R.Left then Слишком длинное слово - не помещается в одну строку;
      // Перенос слов не поддерживается
    end;
  end;

begin
Result := -1; // все строки уместились между TopMargin и Yfloor
if (FromRow < 0)or(FromRow >= Grid.RowCount) then Exit;

DestCanvas.Brush.Style := bsClear;
DestCanvas.Font := Grid.Font;
//  DestCanvas.Font.Height := Round(Grid.Font.Height*Scale);
DestCanvas.Font.Size := 10;

Grid.Canvas.Font := Grid.Font;
Scale := DestCanvas.TextWidth('test')/Grid.Canvas.TextWidth('test');

d := Round(2*Scale);
TotalPrevH := 0;

for j := 0 to Grid.RowCount-1 do
begin
  if (j >= Grid.FixedRows) and (j < FromRow) then Continue;
  // Fixed Rows рисуются на каждой странице

  TotalPrevW := 0;
  CellH := Round(Grid.RowHeights[j]*Scale);
  CorrectCellHeight(j);

  if TopMargin + TotalPrevH + CellH > YFloor then
  begin
    Result := j; // j-я строка не помещается в заданный диапазон
    if Result < Grid.FixedRows then Result := -1;
    // если фиксированные строки не влезают на страницу - это тяжёлый случай...
    Exit;
  end;

  for i := 0 to Grid.ColCount-1 do
  begin
    CellW := Round(Grid.ColWidths[i]*Scale);

    R := Rect(TotalPrevW, TotalPrevH, TotalPrevW + CellW, TotalPrevH + CellH);
    OffSetRect(R, LeftMargin, TopMargin);

    if (i < Grid.FixedCols)or(j < Grid.FixedRows) then LineWidth := BoldLineWidth
    else LineWidth := OrdinaryLineWidth;

    DestCanvas.Pen.Width := LineWidth;
    if LineWidth > 0 then
      DestCanvas.Rectangle(R.Left, R.Top, R.Right+1, R.Bottom+1);

    //InflateRect(R, -d, -d);
    R.Left := R.Left+d;
    R.Top := R.Top + d;

    s := Grid.Cells[i, j];
    DrawText(DestCanvas.Handle, PChar(s), Length(s), R,
      DT_LEFT or DT_TOP or DT_WORDBREAK or DT_NOPREFIX);

    TotalPrevW := TotalPrevW + CellW; // Общая ширина всех предыдущих колонок
  end;

  TotalPrevH := TotalPrevH + CellH;   // Общая высота всех предыдущих строк
end;
end;

end.

Автор: MacTep 19.6.2004, 13:48
Благодарю автора предыдущего сообщения. А никто так и не ответил на вопрос: что будет если вызвать такую команду Form1.Print?

Автор: _hunter 19.6.2004, 16:17
справка ответит smile.gif :
Цитата
Call Print to print the form. Print uses the GetFormImage method to obtain a bitmap of the form and draws that to the printer
’s HDC.

Автор: MacTep 19.6.2004, 23:14
Так, в тексте я понял только перво предложение! Переведите второе, плиз!

Автор: Medved 21.6.2004, 08:09
Цитата(MacTep @ 19.6.2004, 16:48)
Благодарю автора предыдущего сообщения. А никто так и не ответил на вопрос: что будет если вызвать такую команду Form1.Print?

Хм... а самому попробовать сложно?

Автор: MacTep 23.6.2004, 23:50
Дело в том, что принтера дома нет! Вот и мучаюсь! А сделать надо так, чтобы работало именно на принтере реальном!

Автор: Albinos_x 24.6.2004, 01:56
Цитата
что будет если вызвать такую команду Form1.Print?


на принтер выведится изображение формы

Автор: MacTep 24.6.2004, 14:13
Благодарю всех, кто принимал участие в обсуждении этой темы rolleyes.gif !

Автор: _hunter 24.6.2004, 19:24
а насчет принтера -- есть такая программа Адоб Дистилятор -- можеш на ней практиковаться smile.gif

Автор: MacTep 28.6.2004, 07:15
_hunter, где такую прогу, как Адоб Дистилятор можно найти. Подскажи, хотябы ссылочку, ок?

Автор: Akella 30.6.2004, 08:50
Adobe Acrobat 4.0 - версия для редактирования создает в "принтерах" такую фиговину. Новерсия для редактирование не бесплатна и инсталяха занимает почти 100 метров

Автор: Akella 30.6.2004, 14:26
2)вариантов может быть много
отправляй все в Excel
или
печать через файл
или
печать через TMemo
или
Canvas.TextOut, затем затем печатаешь
последние три способа нужно использовать со знаком табуляции(можно с двумя), т.е. ячейка1+#9+ячейка2 и т.д., для того, чтобы все столбцы были ровными
111 222 333 444 555 666 777
1111 22 333 4444 5 666 77777
ну ты понял
/////////////////////////////////////////////////////


3) я таким еще не занимался, может показаться сложно, но всё таки...
нажимаешь програмно Alt+PrintScreen (т.е. копируешь в буфер обмена)
потом из буфера в Bitmap
берёш ClientWidth и ClientHeight формы
отмеряешь от правого нижнего края примоугольник
и копируешь этот прямоуголник в основной Bitmap
/////////////////////////////////////////////////////////

4)здесь информации побольше
Алгоритмы сортировки
Previous Top Next


Алгоритм 1. Сортировка вставками.

Это изящный и простой для понимания метод. Вот в чем его суть: создается новый ма ссив, в который мы последовательно вставляем элементы из исходного массива так, чтобы новый массив был упорядоченным. Вставка происходит следующим образом: в конце нового массива выделяется свободная ячейка, далее анализируется элемент, стоящий перед пустой ячейкой (если, конечно, пустая ячейка не стоит на первом месте), и если этот элемент больше вставляемого, то подвигаем элемент в свободную ячейку (при этом на том месте, где он стоял, образуется пустая ячейка) и сравниваем следующий элемент. Так мы прейдем к ситуации, когда элемент перед пустой ячейкой меньше вставляемого, или пустая ячейка стоит в начале массива. Помещаем вставляемый элемент в пустую ячейку . Таким образом, по очереди вставляем все элементы исходного массива. Очевидно, что если до вставки элемента массив был упорядочен, то после вставки перед вставленным элементом расположены все элементы, меньшие его, а после — большие. Так как порядок элементов в новом массиве не меняется, то сформированный массив будет упорядоченным после каждой вставки. А значит, после последней вставки мы получим упорядоченный исходный массив. Вот как такой алгоритм можно реализовать на языке программирования Pascal:

Program InsertionSort;
Var A,B : array[1..1000] of integer;
N,i,j : integer;
Begin
{Определение размера массива A (N) и его заполнение}
…
{сортировка данных}
for i:=1 to N do
begin
j:=i;
while (j>1) and (B[j-1]>A[i]) do
begin
B[j]:=B[j-1];
j:=j-1;
end;
B[j]:=A[i];
end;
{Вывод массива B}
…
End.

В принципе, данную сортировку можно реализовать и без дополнительного массива B, если сортировать массив A сразу при считывании, т. е. осуществлять вставку нового элемента в массив A.

Алгоритм 2. Пузырьковая сортировка.

Реализация данного метода не требует дополнительной памяти. Метод очень прост и состоит в следующем: берется пара рядом стоящих элементов, и если элемент с меньшим индексом оказывается больше элемента с большим индексом, то мы меняем их местами. Эти действия продолжаем, пока есть такие пары. Легко понять, что когда таких пар не останется, то данные будут отсортированными. Для упрощения поиска таких пар данные просматриваются по порядку от начала до конца. Из этого следует, что за такой просмотр находится максимум, который помещается в конец массива, а потому следующий раз достаточно просматривать уже меньшее количество элементов. Максимальный элемент как бы всплывает вверх, отсюда и название алгоритма Так как каждый раз на свое место становится по крайней мере один элемент, то не потребуется более N проходов, где N — количество элементов. Вот как это можно реализовать:

Program BubbleSort;
Var A : array[1..1000] of integer;
N,i,j,p : integer;
Begin
{Определение размера массива A (N) и его заполнение}
…
{сортировка данных}
for i:=1 to n do
for j:=1 to n-i do
if A[j]>A[j+1] then
begin {Обмен элементов}
p:=A[j];
A[j]:=A[j+1];
A[j+1]:=P;
end;
{Вывод отсортированного массива A}
…
End.

Алгоритм 3. Сортировка Шейкером.

Когда данные сортируются не в оперативной памяти, а на жестком диске, особенно если ключ связан с большим объемом дополнительной информации, то количество перемещений элементов существенно влияет на время работы. Этот алгоритм уменьшает количество таких перемещений, действуя следующим образом: за один проход из всех элементов выбирается минимальный и максимальный. Потом минимальный элемент помещается в начало массива, а максимальный, соответственно, в конец. Далее алгоритм выполняется для остальных данных. Таким образом, за каждый проход два элемента помещаются на свои места, а значит, понадобится N/2 проходов, где N — количество элементов. Реализация данного алгоритма выглядит так:

Program ShakerSort;
Var A : array[1..1000] of integer;
N,i,j,p : integer;
Min, Max : integer;
Begin
{Определение размера массива A — N) и его заполнение}
…
{сортировка данных}
for i:=1 to n div 2 do
begin
if A[i]>A[i+1] then
begin
Min:=i+1;
Max:=i;
end
else
begin
Min:=i;
Max:=i+1;
end;
for j:=i+2 to n-i+1 do
if A[j]>A[Max] then
Max:=j
else
if A[j]<A[Min] then
Min:=j;
{Обмен элементов}
P:=A[i];
A[i]:=A[min];
A[min]:=P;
if max=i then
max:=min;
P:=A[N-i+1];
A[N-i+1]:=A[max];
A[max]:=P;
end;
{Вывод отсортированного массива A}
…
End.


Рассмотрев эти методы, сделаем определенные выводы. Их объединяет не только то, что они сортируют данные, но также и время их работы. В каждом из алгоритмов присутствуют вложенные циклы, время выполнения которых зависит от размера входных данных. Значит, общее время выполнения программ есть O(n2) (константа, умноженная на n2). Следует отметить, что первые два алгоритма используют также O(n2) перестановок, в то время как третий использует их O(n). Отсюда следует, что метод Шейкера является более выгодным для сортировки данных на внешних носителях информации.

Если вы думаете, что бравые «алгоритмщики» остановились на достигнутом, то вы ошибаетесь. Видите ли, временная оценка O(n2) показалась им слишком громоздкой, и они, жадины такие, решили еще потратить свое время, чтобы впоследствии сэкономить наше. Итак, давайте теперь рассмотрим более быстрые алгоритмы.

Алгоритм 4. Сортировка слиянием.

Эта сортировка использует следующую подзадачу: есть два отсортированных массива, нужно сделать (слить) из них один отсортированный. Алгоритм сортировки работает по такому принципу: разбить массив на две части, отсортировать каждую из них, а потом слить обе части в одну отсортированную. Корректность данного метода практически очевидна, поэтому перейдем к реализации.

Program SlivSort;
Var A,B : array[1..1000] of integer;
N : integer;
Procedure Sliv(p,q : integer); {процедура сливающая массивы}
Var r,i,j,k : integer;
Begin
r:=(p+q) div 2;
i:=p;
j:=r+1;
for k:=p to q do
if (i<=r) and ((j>q) or (a[i]<a[j])) then
begin
b[k]:=a[i];
i:=i+1;
end
else
begin
b[k]:=a[j];
j:=j+1;
end ;
for k:=p to q do
a[k]:=b[k];
End;
Procedure Sort(p,q : integer); {p,q — индексы начала и конца сортируемой части массива}
Begin
if p<q then {массив из одного элемента тривиально упорядочен}
begin
Sort(p,(p+q) div 2);
Sort((p+q) div 2 + 1,q);
Sliv(p,q);
end;
End;
Begin
{Определение размера массива A — N) и его заполнение}
…
{запуск сортирующей процедуры}
Sort(1,N);
{Вывод отсортированного массива A}
…
End.

Чтобы оценить время работы этого алгоритма, составим рекуррентное соотношение. Пускай T(n) — время сортировки массива длины n, тогда для сортировки слиянием справедливо T(n)=2T(n/2)+O(n) (O(n) — это время, необходимое на то, чтобы слить два массива). Распишем это соотношение:

T(n)=2T(n/2)+O(n)=4T(n/4)+2O(n/2)+O(n)=4T(n/4)+2O(n)= … = 2kT(1)+kO(n)

Осталось оценить k. Мы знаем, что 2k=n, а значит k=log2n. Уравнение примет вид T(n)=nT(1)+ log2nO(n). Так как T(1) — константа, то T(n)=O(n)+log2nO(n)=O(nlog2n). То есть, оценка времени работы сортировки слиянием меньше, чем у первых трех алгоритмов (я прошу прощения у тех, кто не понял мои рассуждения или не согласен с ними, — просто поверьте мне на слово). Перед тем как объяснить, чем этот метод лучше, рассмотрим еще один алгоритм.

Алгоритм 5. Сортировка двоичной кучей

Проблема первых трех алгоритмов, описанных в прошлой части статьи, состояла в том, что после того как элемент занимал свое место, информация об уже произведенных сравнениях никак не использовалась. Структура двоичного дерева позволяет сохранить эту информацию. Итак, представим массив в виде дерева (Рис. 1). Корень дерева — элемент с индексом 1; элемент с индексом i является «родителем» для элементов с индексами 2*i и 2*i+1, а те, в свою очередь, являются его «детьми». Каждый элемент кроме первого имеет «родителя» и может иметь до двух «детей» — речь ведь идет именно о ДВОИЧНОМ дереве. Очевидно, что корнем дерева является наименьший элемент, а наибольший не имеет детей. Тут возникают два вопроса: как нам такую кучу наплодить? И зачем нам это вообще нужно? Пренебрегая порядком, отвечу сразу на второй вопрос: мы хотим извлечь из кучи минимальный элемент, а потом как-то преобразовать и восстановить кучу. Таким образом, по очереди извлечь все элементы и получить отсортированный массив. И вот как мы собираемся это сделать: пусть поддеревья с корнями 2*i и 2*i+1 уже имеют свойство кучи, мы же хотим, чтобы такое свойство имело и поддерево с корнем i. Для этого, если корень больше наименьшего своего «ребенка», мы меняем корень дерева (элемент с индексом i) с этим «ребенком», после повторяем алгоритм для поддерева, куда перешел бывший корень. Выполняя этот алгоритм «снизу вверх» (сначала для маленьких поддеревьев, потом для больших), мы добьемся того, что свойство кучи будет выполняться для всего дерева. Извлечение элемента происходит очень простым способом: мы ставим последний элемент на первое место и запускаем алгоритм исправления кучи от корня дерева… Я тут много наговорил, но на самом деле, реализация совсем несложная:

Program HeapSort;
Var A,B : array[1..1000] of integer;
N,i,P : integer;
Procedure Heapi(ind : integer); {процедура, формирующая и исправляющяя кучу}
Var k : integer;
Begin
k:=ind*2;
If k<=N then
begin
if (k+1<=N) and (A[k]>A[k+1]) then
k:=k+1;
if A[ind]>A[k] then
begin
P:=A[ind];
A[ind]:=A[k];
A[k]:=P;
Heapi(k);
end;
end;
End;
Begin
{Определение размера массива A — N) и его заполнение}
…
{формирование кучи}
for i:=N div 2 downto 1 do
Heapi(i);
{формирование массива B}
for i:=1 to N do
begin
B[i]:=A[1];
A[1]:=A[N];
N:=N-1;
Heapi(1);
end;
{Вывод отсортированного массива B}
…
End.

А теперь главное, т. е. оценка сложности. Время работы процедуры исправляющей кучу зависит от высоты дерева. Высота всего дерева равна log2n, значит, время работы процедуры есть O(log2n). Программа состоит из двух частей: формирование кучи и создание отсортированного массива B. Время исполнения каждой из частей не больше O(n log2n) (в каждой части исправляющая процедура вызывается не более n раз). Значит, время работы то же, что и в сортировке слиянием.

Теперь лирическое отступление насчет времени работы. Может, читатель думает, что быстрые алгоритмы сложны в исполнении и проще написать что-то вроде сортировки вставками. Что ж, рассмотрим простой пример: допустим, вы написали сортировку вставками, тщательно, с помощью ассемблера, и время работы получилось 2n2, а какой-нибудь раздолбай написал сортировку слиянием со временем работы 50nlog2n. И тут появилась необходимость отсортировать 1000000 элементов (что в наше время не редкость). Вы использовали крутой компьютер, который делает 108 операций сравнения и перестановки в секунду, а у него компьютер похуже — всего 106 операций в секунду. И вы будете ждать 2*(106)2/108 = 20 000 секунд (приблизительно 5.56 часов), а ваш конкурент — 50*(106)*log2(106)/106 = 1000 секунд (приблизительно 17 минут). Надеюсь, вы проведете это время (5 часов) с пользой для себя и поймете, что хороший алгоритм — быстрый алгоритм :-). Хотя, если вы будете сортировать маленький массив или много маленьких массивов, то 2n2 для вас будет лучше, чем 50nlog2n. Эту закономерность использует один из способов оптимизации сортировки слиянием: сортировать маленькие части массива вставками.

Теперь переходим к самому интересному, а именно к одной из самых быстрых и эффективных из известных сортировок, которая так и называется — «быстрая сортировка».

Алгоритм 6. Быстрая сортировка.

Как и в сортировке слиянием, массив разбивается на две части, с условием, что все элементы первой части меньше любого элемента второй. Потом каждая часть сортируется отдельно. Разбиение на части достигается упорядочиванием относительно некоторого элемента массива, т. е. в первой части все числа меньше либо равны этому элементу, а во второй, соответственно, больше либо равны. Два индекса проходят по массиву с разных сторон и ищут элементы, которые попали не в свою группу. Найдя такие элементы, их меняют местами. Тот элемент, на котором индексы пересекутся, и определяет разбиение на группы. Классическая реализация алгоритма выглядит так:

Program QuickSort;
Var A : array[1..1000] of integer;
N,T : integer;
Procedure Sort(p,q : integer); {p,q — индексы начала и конца сортируемой части массива}
Var i,j,r : integer;
Begin
if p<q then {массив из одного элемента тривиально упорядочен}
begin
r:=A[p];
i:=p-1;
j:=q+1;
while i<j do
begin
repeat
i:=i+1;
until A[i]>=r;
repeat
j:=j-1;
until A[j]<=r;
if i<j then
begin
T:=A[i];
A[i]:=A[j];
A[j]:=T;
end;
end;
Sort(p,j);
Sort(j+1,q);
end;
End;
Begin
{Определение размера массива A — N) и его заполнение}
…
{запуск сортирующей процедуры}
Sort(1,N);
{Вывод отсортированного массива A}
…
End.

Что же делает данный алгоритм таким быстрым? Ну во-первых, если массив каждый раз будет делится на приблизительно равные части, то для него будет верно то же соотношение, что и для сортировки слиянием, т. е. время работы будет O(nlog2n). Это уже само по себе хорошо. Кроме того, константа при nlog2n очень мала, ввиду простоты внутреннего цикла программы. В комплексе это обеспечивает огромную скорость работы. Но как всегда есть одно «но». Вы, наверное, уже задумались: а что если массив не будет делится на равные части? Классическим примером является попытка «быстро» отсортировать уже отсортированный массив. При этом данные каждый раз будут делиться в пропорции 1 к n-1, и так n раз. Общее время работы при этом будет O(n2), тогда как вставкам, для того чтобы «понять», что массив уже отсортирован, требуется всего-навсего O(n). А на кой нам сортировка, которая одно сортирует хорошо, а другое плохо? А собственно, что она сортирует хорошо? Оказывается, что лучше всего она сортирует случайные массивы (порядок элементов в массиве случаен). И поэтому нам предлагают ввести в алгоритм долю случайности. А точнее, вставить randomize и вместо r:=A[p]; написать r:=A[random(q-p)+p]; т. е. теперь мы разбиваем данные не относительно конкретного, а относительно случайного элемента. Благодаря этому алгоритм получает приставку к имени «вероятностный». Особо недоверчивым предлагаю на своем опыте убедится, что данная модификация быстрой сортировки сортирует любые массивы столь же быстро.

А теперь еще один интересный факт: время O(nlog2n) является минимальным для сортировок, которые используют только попарное сравнение элементов и не использует структуру самих элементов. Тем, кому интересно, откуда это взялось, рекомендую поискать в литературе, доказательство я здесь приводить не намерен, не Дональд Кнут, в конце концов :-). Но вы обратили внимание, что для рассмотренных алгоритмов в принципе не важно, что сортировать — такими методами можно сортировать хоть числа, хоть строки, хоть какие-то абстрактные объекты. Следующие сортировки могут сортировать только определенные типы данных, но за счет этого они имеют рекордную временную оценку O(n).

Алгоритм 7. Сортировка подсчетом.

Этот метод подходит для сортировки целых чисел из не очень большого диапазона (сравнимого с размером массива). Идея вот в чем: для каждого элемента найти, сколько элементов, меньших определенного числа, и поместить это число на соответствующие место. Делается это так: за линейный проход по массиву мы для каждого из возможных значений подсчитываем, сколько элементов имеют такое значение. Потом добавляем к каждому из найденных чисел суму всех предыдущих. Получая, таким образом, сколько есть элементов, значения которых не больше данного значения. Далее, опять-таки за линейный проход, формируем из исходного массива новый отсортированный. При этом следим, чтобы два одинаковых элемента не были записаны в одно место. Если все равно непонятно, смотрите реализацию:

Program CountingSort;
Var A,B : array[1..1000] of byte;
C : array[byte] of integer;
N,i : integer;
Begin
{Определение размера массива A (N) и его заполнение}
…
{сортировка данных}
for i:=0 to 255 do
C[i]:=0;
for i:=1 to N do
C[A[i]]:=C[A[i]]+1;
for i:=1 to 255 do
C[i]:=C[i-1]+C[i];
for i:=N downto 1 do
begin
B[C[A[i]]]:=A[i];
C[A[i]]:=C[A[i]]-1; {здесь мы избегаем возможности записи двух одинаковых чисел в одну ячейку}
end;
{Вывод массива B}
…
End.


Этот простой метод не использует вложенных циклов и, учитывая небольшой диапазон значений, время его работы есть O(n).

Рассмотрев такое количество сортировок, можно задуматься: а будет ли результат их работы одинаковым? Странный вопрос, ведь все сортировки правильно сортируют данные, так почему же результат работы может быть разным? Хорошо, объясню: меньшие элементы всегда расположены перед большими, но порядок одинаковых элементов может быть нарушен. Если мы сортируем данные, которые состоят из одного ключа, то мы, конечно, не заметим разницы. Но если к ключу прилагается дополнительная информация, то одна сортировка может вернуть нам 1977 "Иванов" и 1977 "Сидоров", а другая — 1977 "Сидоров" и 1977 "Иванов". Значит, порядок одинаковых элементов может в процессе сортировки стать другим. Правда, это бывает далеко не всегда и не в каждой сортировке. В сортировках вставками, пузырьком, подсчетом и слиянием порядок элементов с одинаковыми ключами всегда такой же, как и в изначальном массиве. Такие сортировки называются устойчивыми, и сейчас я познакомлю вас с улучшенной сортировкой подсчетом, которая позволяет сортировать числа большего диапазона, используя другую устойчивую сортировку.

Алгоритм 8. Цифровая сортировка.

Этой сортировкой можно сортировать целые неотрицательные числа большого диапазона. Идея состоит в следующем: отсортировать числа по младшему разряду, потом устойчивой сортировкой сортируем по второму, третьему, и так до старшего разряда. В качестве устойчивой сортировки можно выбрать сортировку подсчетом, в виду малого времени работы. Реализация такова:

Program RadixSort;
Var A,B : array[1..1000] of word;
N,i : integer;
t : longint;
Procedure Sort; {сортировка подсчетом}
Var C : array[0..9] of integer;
j : integer;
Begin
For j:=0 to 9 do
C[j]:=0;
For j:=1 to N do
C[(A[j] mod (t*10)) div t]:= C[(A[j] mod (t*10)) div t]+1;
For j:=1 to 9 do
C[j]:=C[j-1]+C[j];
For j:=N downto 1 do
begin
B[C[(A[j] mod (t*10)) div t]]:=A[j];
C[(A[j] mod (t*10)) div t] := C[(A[j] mod (t*10)) div t]-1;
end;
End;
Begin
{Определение размера массива A (N) и его заполнение}
…
{сортировка данных}
t:=1;
for i:=1 to 5 do
begin
Sort;
A:=B;
t:= t*10;
end;
{Вывод массива A}
…
End.

Так как сортировка подсчетом вызывается константное число раз, то время работы всей сортировки есть O(n). Заметим, что таким способом можно сортировать не только числа, но и строки, если же использовать сортировку слиянием в качестве устойчивой, то можно сортировать объекты по нескольким полям.
Теперь вы владеете достаточным арсеналом, чтобы сортировать все что угодно и как угодно. Помните, что выбор нужной вам сортировки зависит от того, какие данные вы будете сортировать и где вы их будете сортировать.
P.S. Все программы рабочие — если, конечно, вам не лень будет заменить три точки на код ввода и вывода массивов :-).
///////////////////////////////////////////////////////////////////////////////////
Сортировка столбцов в StringGrid
Previous Top Next

Procedure GridSort(StrGrid: TStringGrid; NoColumn: Integer);
Var Line, PosActual: Integer;
Row: TStrings;
begin
Renglon := TStringList.Create;
For Line := 1 to StrGrid.RowCount-1 do
Begin
PosActual := Line;
Row.Assign(TStringlist(StrGrid.Rows[PosActual]));
While True do
Begin
If (PosActual = 0) Or (StrToInt(Row.Strings[NoColumn-1]) >=
StrToInt(StrGrid.Cells[NoColumn-1,PosActual-1])) then
Break;
StrGrid.Rows[PosActual] := StrGrid.Rows[PosActual-1];
Dec(PosActual);
End;
If StrToInt(Row.Strings[NoColumn-1]) < StrToInt(StrGrid.Cells[NoColumn-1,PosActual]) then
StrGrid.Rows[PosActual] := Row;
End;
Renglon.Free;
end;


--------------------------------------------------------------------------------


type TStringGridExSortType = (srtAlpha,srtInteger,srtDouble);

procedure GridSort(SG : TStringGrid; ByColNumber,FromRow,ToRow : integer;
SortType : TStringGridExSortType = srtAlpha);
var Temp : TStringList;

function SortStr(Line : string) : string;
var RetVar : string;
begin
case SortType of
srtAlpha : Retvar := Line;
srtInteger : Retvar := FormatFloat('000000000',StrToIntDef(trim(Line),0));
srtDouble : try
Retvar := FormatFloat('000000000.000000',StrToFloat(trim(Line)));
except
RetVar := '0.00';
end;
end;

Result := RetVar;
end;

// Рекурсивный QuickSort
procedure QuickSort(Lo,Hi : integer; CC : TStrings);

procedure Sort(l,r: integer);
var i,j : integer;
x : string;
begin
i := l; j := r;
x := SortStr(CC[(l+r) DIV 2]);
repeat
while SortStr(CC[i]) < x do inc(i);
while x < SortStr(CC[j]) do dec(j);
if i <= j then begin
Temp.Assign(SG.Rows[j]); // Меняем местами 2 строки
SG.Rows[j].Assign(SG.Rows[i]);
SG.Rows[i].Assign(Temp);
inc(i); dec(j);
end;
until i > j;
if l < j then sort(l,j);
if i < r then sort(i,r);
end;

begin {quicksort};
Sort(Lo,Hi);
end;

begin
Temp := TStringList.Create;
QuickSort(FromRow,ToRow,SG.Cols[ByColNumber]);
Temp.Free;
end;

для 4-го вопроса инфа из DRKB

Автор: Akella 30.6.2004, 14:39
3) почитай эту тему http://forum.vingrad.ru/index.php?showtopic=25097

Автор: MacTep 3.7.2004, 22:37
У меня нет слов о человеке под ником dsergey. Это просто монстр в хорошем смысле этого слова! Благодарю за столь распространненный ответ! А можно задать вопрос не по теме? Откуда вы столько знаете? А точнее, откуда столько подробной инфы?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)