Insertion Sort - TStringList Delphi - sorting

i'm trying to sort TStringList of integers from a text file with Insertion and Selection Sort .Selection Sort works ok , but the Insertion Sort doesnt work with my code . Can someone tell me where i'm wrong ? My 'numbers.txt' has 5000 lines of numbers. Thanks in advance
UPDATE : I have edited my code a bit , it works now with Insertion-Sort but it sorts just 4 indexes of integer as on the image
var
i, Position, n: integer;
Value: string;
begin
n := Items.Count;
for i := 1 to n - 1 do
begin
Value := Items[i];
Position := i-1;
while (Position >0) and (Items[Position]>Value) do
begin
Items[Position+1]:= Items[Position] ;
Position := Position -1 ;
end;
Items[Position+1] := Value;
end;
end;

Your data in the image is sorting exactly as it should, because you're sorting on string values, and based on the comparison you're making the order is perfect. "1143" falls exactly between the string values "11413" and "11443", because the comparison is made character by character out to the length of the shortest of the values. "1141" < "1143" < "1144", based on the first four characters of each string.
If you want an actual integer sort, then you need to convert the two values to integer before comparing them. Something like this should work (note I did not test your overall sort logic - I just used values that demonstrate the concept):
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Classes;
var
i, Position, n: integer;
Value: integer;
Items: TStringList;
begin
Items := TStringList.Create;
try
Items.DelimitedText := '1116,11170,11178,11206,1122,11221,11228';
n := Items.Count;
for i := 1 to n - 1 do
begin
Value := StrToInt(Items[i]);
Position := i - 1;
while (Position > 0) and (StrToInt(Items[Position]) > Value) do
begin
Items[Position + 1]:= Items[Position];
Position := Position - 1 ;
end;
Items[Position+1] := IntToStr(Value);
end;
for i := 0 to Items.Count - 1 do
WriteLn(Items[i]);
finally
Items.Free;
end;
ReadLn;
end.
The output I got from the code above in a console window:
1116
1122
11170
11178
11206
11221
11228

Related

Read integers from a string

I'm learning algorithms and I'm trying to make an algorithm that extracts numbers lets say n in [1..100] from a string. Hopefully I get an easier algorithm.
I tried the following :
procedure ReadQuery(var t : tab); // t is an array of Integer.
var
x,v,e : Integer;
inputs : String;
begin
//readln(inputs);
inputs:='1 2 3';
j:= 1;
// make sure that there is one space between two integers
repeat
x:= pos(' ', inputs); // position of the space
delete(inputs, x, 1)
until (x = 0);
x:= pos(' ', inputs); // position of the space
while x <> 0 do
begin
x:= pos(' ', inputs); //(1) '1_2_3' (2) '2_3'
val(copy(inputs, 1, x-1), v, e); // v = value | e = error pos
t[j]:=v;
delete(inputs, 1, x); //(1) '2_3' (2) '3'
j:=j+1; //(1) j = 2 (2) j = 3
//writeln(v);
end;
//j:=j+1; // <--- The mistake were simply here.
val(inputs, v, e);
t[j]:=v;
//writeln(v);
end;
I get this result ( resolved ) :
1
2
0
3
expected :
1
2
3
PS : I'm not very advanced, so excuse me for reducing you to basics.
Thanks for everyone who is trying to share knowledge.
Your code is rather inefficient and it also doesn't work for strings containing numbers in general.
A standard and performant approach would be like this:
type
TIntArr = array of Integer;
function GetNumbers(const S: string): TIntArr;
const
AllocStep = 1024;
Digits = ['0'..'9'];
var
i: Integer;
InNumber: Boolean;
NumStartPos: Integer;
NumCount: Integer;
procedure Add(Value: Integer);
begin
if NumCount = Length(Result) then
SetLength(Result, Length(Result) + AllocStep);
Result[NumCount] := Value;
Inc(NumCount);
end;
begin
InNumber := False;
NumCount := 0;
for i := 1 to S.Length do
if not InNumber then
begin
if S[i] in Digits then
begin
NumStartPos := i;
InNumber := True;
end;
end
else
begin
if not (S[i] in Digits) then
begin
Add(StrToInt(Copy(S, NumStartPos, i - NumStartPos)));
InNumber := False;
end;
end;
if InNumber then
Add(StrToInt(Copy(S, NumStartPos)));
SetLength(Result, NumCount);
end;
This code is intentionally written in a somewhat old-fashioned Pascal way. If you are using a modern version of Delphi, you wouldn't write it like this. (Instead, you'd use a TList<Integer> and make a few other adjustments.)
Try with the following inputs:
521 cats, 432 dogs, and 1487 rabbits
1 2 3 4 5000 star 6000
alpha1beta2gamma3delta
a1024b2048cdef32
a1b2c3
32h50s
5020
012 123!
horses
(empty string)
Make sure you fully understand the algorithm! Run it on paper a few times, line by line.

Pascal, reading unknown number of integers

My question is how can I read some number of integers that user enters on standard input, and place them in array.However I don't how many numbers user will enter and i can't ask him that? User enters numbers in one line.
Okay i have just one more answer i would like to add.Thanks all for your help this is code written based on suggestions.I added a line for writting array backwards just for you can see that it has readed well.
program backo;
var niz:array [1..100] of integer;
n, i:integer;
begin
i:=1;
writeln('enter elements of array');
read(niz[i]);
while not eoln do
begin
i:=i+1;
read(niz[i]);
end;
for n:=i downto 1 do
writeln(niz[n]);
end.
Ok, based on comments there is three ways demonstrated:
program readmultiint;
{$mode objfpc}{$H+}
uses
StrUtils;
const
CMaxValues = 3;
var
s: string;
darr: array of Integer;
sarr: array [0..CMaxValues-1] of Integer;
i, cnt: Integer;
begin
// Dynamic array using WordCount
Writeln('Enter values:');
Readln(s);
cnt := WordCount(s, StdWordDelims);
SetLength(darr, cnt); // Allocate room for values
for i := 0 to cnt - 1 do
Val(ExtractWord(i + 1, s, StdWordDelims), darr[i]);
for i in darr do
Writeln(i);
// Dynamic array usin EOLN
SetLength(darr, 0);
Writeln('Enter values:');
while not eoln do
begin
SetLength(darr, Length(darr) + 1); // Expand array for next value
Read(darr[High(darr)]);
end;
Readln; // Read <Enter> itself
for i in darr do
Writeln(i);
// Static array
cnt := 0;
Writeln('Enter values:');
while (not eoln) and (cnt < CMaxValues) do // Reads not more then CMaxValues values
begin
Read(sarr[cnt]);
Inc(cnt);
end;
Readln; // Read <Enter> itself
for i := 0 to cnt-1 do
Writeln(sarr[i]);
end.
Feel free to use one of them or provide your own :)
PS: Some readings:
Dynamic arrays
Val procedure
for-in loop

Pascal: Updating Recent Scores Array

I have a program that stores the last three recent scores of a game. However, I would like to store only the best scores. Eg. Recent Scores: Tom - 12, Sam - 14, Sue - 16. If i played the game and got a new score of 20, i would like it to store the new score of 20 (with name) and the other two scores of Sam and Sue ...thereby losing Tom. (I'm not worried about order).
Const NoOfRecentScores = 3;
TRecentScore = Record
Name : String;
Score : Integer;
End;
TRecentScores = Array[1..NoOfRecentScores] of TRecentScore;
Var
RecentScores : TRecentScores;
When i play the game, i call a procedure called UpdateRecentScores. Here it is:
Procedure UpdateRecentScores(Var RecentScores : TRecentScores; Score : Integer);
Var
PlayerName : String;
Count,count2 : Integer;
FoundSpace : Boolean;
MinMark,position: Integer;
ScorePresent:boolean = false;
Begin
PlayerName := GetPlayerName;
FoundSpace := False;
Count :=1;
While Not FoundSpace And (Count <= NoOfRecentScores)
Do If RecentScores[Count].Name = ''
Then FoundSpace := True
Else Count := Count + 1;
Here is the part i am struggling with. If no score is previously entered, then i have to accept that the first entered score is going to be the minimum:
If ScorePresent = False then
begin
MinMark:=Score;
ScorePresent:=True;
RecentScores[Count].Name := PlayerName;
RecentScores[Count].Score := Score;
writeln('Minimum Mark is: ',MinMark);
end
...the problem with the above, however, is that if the first score is a very high score, that becomes my minimum score!
Below, i am simply saying that if the Score achieved is greater than the MinMark (i.e. the minimum score) then the score should be stored in the array.
else if Score> MinMark then
begin
For count:= 1 to NoOfRecentScores do
begin
if RecentScores[count].score<Score then
position:=count;
RecentScores[position].Name := PlayerName;
RecentScores[position].Score := Score;
end;
End;
end;
As you can see, i am trying to check what the MinMark is first of all. Then, compare the score that i've just got with the MinMarker to see if it should be stored.
To clarify therefore, I want to save the best 3 scores and not recent scores.
To store n best scores, it is convenient to order them from high to low.
Let us go with an example first. Suppose you have the following four records: Tim - 14, Susan - 7, Don - 5, and Derek - 12. Then the array will look like [('Tim', 14), ('Derek', 12), ('Susan', 7), ('Don', 5)].
Now, what happens when Bert achieves a score of 9? Turns out we just want to insert a pair in a sorted array, so that it becomes [('Tim', 14), ('Derek', 12), ('Bert', 9), ('Susan', 7), ('Don', 5)]. After that, we drop the last element.
If we have to modify the array in place, we find the position pos where to insert Bert (it is position 3), then move everything in positions pos..(n-1) into positions (pos+1)..n, and after that, write Ben and his score to position pos.
Since you're using such a small array (3 records), it's relatively simple. (If your array was considerably larger, you'd want to keep it sorted and use a faster means of locating the right position for it, but your array is extremely small.)
Let's say you now have three RecentScore records, containing Tim - 14, Susan - 7, Derek - 12.
You need to find out if there's a score lower than the one the user just attained by Gemma (9) which is in the CurrentScore record (type TRecentScore), and if so replace it with Gemma's name and score.
Here's a working console project (compiled and run in XE5):
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TRecentScore = record
Name: string;
Score: Integer;
end;
TRecentScores = array of TRecentScore;
// Function to find lower score than the one we're trying to add.
// Returns the index of the next lower item if there is one, or
// -1 if there isn't one.
function FindLowerScore(Scores: TRecentScores; CheckScore: Integer): Integer;
var
i: Integer;
MinScore: Integer; // Lowest score found
begin
Result := -1; // No index found
MinScore := CheckScore; // Lowest score so far
for i := Low(Scores) to High(Scores) do
if Scores[i].Score < MinScore then // Lower than CheckScore?
begin
MinScore := Scores[i].Score; // Yep. Store it (new lowest)
Result := i; // and where it was found
end;
end;
// Utility procedure to display list of scores
procedure PrintScores(const Prelude: string; Scores: TRecentScores);
var
Score: TRecentScore;
begin
WriteLn(Prelude);
for Score in Scores do
Writeln(' ' + Score.Name + ' = ' + IntToStr(Score.Score));
end;
var
RecentScores: TRecentScores;
CurrentScore: TRecentScore;
i: Integer;
begin
SetLength(RecentScores, 3);
RecentScores[0].Name := 'Tim';
RecentScores[0].Score := 14;
RecentScores[1].Name := 'Susan';
RecentScores[1].Score := 7;
RecentScores[2].Name := 'Derek';
RecentScores[2].Score := 12;
// Show scores where we begin
PrintScores('Before', RecentScores);
CurrentScore.Name := 'Gemma'; CurrentScore.Score := 9;
// Check for lower score than Gemma's
i := FindLowerScore(RecentScores, CurrentScore.Score);
if i = -1 then
WriteLn('No lower score found!')
else
begin
// We have a lower score in the array. Update that one
// with our new score.
RecentScores[i].Name := CurrentScore.Name;
RecentScores[i].Score := CurrentScore.Score;
PrintScores('After', RecentScores);
end;
ReadLn;
end.
Here is what i used, following your advice...
Procedure UpdateRecentScores(Var RecentScores : TRecentScores; Score : Integer);
Var
PlayerName : String;
Count : Integer;
FoundSpace : Boolean;
Begin
PlayerName := GetPlayerName;
FoundSpace := False;
Count := 1;
While Not FoundSpace And (Count <= NoOfRecentScores)
Do If RecentScores[Count].Name = ''
Then
begin
FoundSpace := True;
RecentScores[Count].Name := PlayerName;
RecentScores[Count].Score := Score;
end
Else Count := Count + 1;
If Not FoundSpace
Then
Begin
SortRecentScores(RecentScores); // sort them into order of score
if score > RecentScores[NoOfRecentScores].Score then
begin
RecentScores[NoOfRecentScores].Name:= PlayerName;
RecentScores[NoOfRecentScores].Score:= Score;
end;
End;
End;

How many times does one number divide into another, and how much is left over?

I need an algorithm in Delphi to generate partitions for a specified integer value.
Example: for 13 if 5 is specified as the max value for partition it will give 5,5,3; if 4 is specified as max partition value the result should be 4,4,4,1, and so on.
It's simple enough to solve the problem using div and mod. Here's an example program that I don't think needs any further explanation:
program IntegerPartitions;
{$APPTYPE CONSOLE}
function Partitions(const Total, Part: Integer): TArray<Integer>;
var
Count: Integer;
Rem: Integer;
i: Integer;
begin
Assert(Total>0);
Assert(Part>0);
Count := Total div Part;
Rem := Total mod Part;
if Rem=0 then
SetLength(Result, Count)
else
SetLength(Result, Count+1);
for i := 0 to Count-1 do
Result[i] := Part;
if Rem<>0 then
Result[Count] := Rem;
end;
var
Value: Integer;
begin
for Value in Partitions(13, 5) do
Writeln(Value);
Readln;
end.

Pascal Bubble Sort

I have a project where the program must accept 10 words and display the words in descending order (alphabetical order from Z-A)
using bubble sorting.
Here's what I know so far:
Program sample;
uses crt;
TYPE
no._list=ARRAY(1...10)OF REAL;
CONST
no.:no._list=(20.00,50.50.35.70....);
VAR
x:INTEGER;
small:REAL;
BEGIN clrscr:
small:=no.(1);
FOR x:=2 TO 10 DO
IF small>number(x);
writeln('Smallest value in the array of no.s is',small:7:2);
END
I really don't know how to do this though and could use some help.
Here's a video by Alister Christie on Bubble sort describing the principle :
http://codegearguru.com/index.php?option=com_content&task=view&id=64&Itemid=1
The algorithm in Pascal can be found # http://delphi.wikia.com/wiki/Bubble_sort
function BubbleSort( list: TStringList ): TStringList;
var
i, j: Integer;
temp: string;
begin
// bubble sort
for i := 0 to list.Count - 1 do begin
for j := 0 to ( list.Count - 1 ) - i do begin
// Condition to handle i=0 & j = 9. j+1 tries to access x[10] which
// is not there in zero based array
if ( j + 1 = list.Count ) then
continue;
if ( list.Strings[j] > list.Strings[j+1] ) then begin
temp := list.Strings[j];
list.Strings[j] := list.Strings[j+1];
list.Strings[j+1] := temp;
end; // endif
end; // endwhile
end; // endwhile
Result := list;
end;

Resources