我有这段代码
var
arr: TArray<string>;
e1, e2, e3, e4: string;
begin
e1 := 'val1';
e2 := 'val2';
e3 := 'val3';
e4 := 'val4';
arr := TArray<string>.Create(e1, e2, e3, e4);
我现在需要检查上面的数组中是否存在e1到e4 两次最好的方法是什么? 我还应该跳过检查任何具有空值的元素。
还请告知我是否应该手动释放此阵列
由于
答案 0 :(得分:8)
为了玩算法的乐趣;如果原始数组足够大以使天真O(n^2)
算法不实用,并且由于OP使用的是具有泛型的Delphi版本,我建议使用TDictionary<string, integer>
来跟踪所有字符串没有排序,并识别重复。
由于多种原因,这将是有效的:
O(n)
。我们从一开始就知道数组的大小,因此我们可以使用TDictionary
而不会增长它。这使得整个算法O(n)
。代码:
type
TZeroWidthRecord = record
end;
function FindFirstDuplicate(const ArrayOfString: array of string): Integer;
var Dict: TDictionary<string, TZeroWidthRecord>;
i: Integer;
ZW: TZeroWidthRecord;
begin
Dict := TDictionary<string, TZeroWidthRecord>.Create(Length(ArrayOfString));
try
for i:=0 to High(ArrayOfString) do
try
Dict.Add(ArrayOfString[i], ZW);
except on E:Exception do
Exit(i);
end;
finally Dict.Free;
end;
// No duplicates found:
Result := -1;
end;
为了回答David的评论,我做了一个简短的测试程序,将基于TDictionary
的算法与sort-and-search
算法进行比较。我创建了随机的字符串数组,然后尝试找到第一个副本。我的数组不包含重复项,如果有重复项,那么TDictionary的运行时间将与平均首次重复的重复项成比例。例如,如果TDictionary平均在阵列中间找到重复,那么TDict算法的平均运行时间将是一半。基于排序的算法需要对整个数组进行排序,排序是占用最多时间的。
与基于排序和字典的算法一样,人们需要使用逼真的数据进行测试。例如,如果我在OP的问题中使用短字符串进行测试,则TDict和sort之间不会有竞争:即使对于琐碎的长度数组,Dict也会更快。但是当平均字符串长度增加时,基于排序的算法开始变得更好;但话说再次,这取决于字符串:例如,如果大多数字符串共享一个长前缀,那么排序算法中的“比较”阶段将花费更长的时间,使TDictionary再次看起来更好!
*==========*===========================*
| | Number of strings |
| Avg str | in the Array for |
| length | TDictionary to be faster |
*======================================*
| 7 | 33 |
| 10 | 73 |
| 13 | 73 |
| 16 | 163 |
| 19 | 163 |
| 22 | 366 |
| 25 | 366 |
| 28 | 549 |
| 37 | 2776 |
| 40 | 2776 |
| 43 | 2776 |
| 46 | 4164 |
| 49 | 9369 |
| 52 | 9369 |
| 55 | 9369 |
| 58 | 21079 |
*==========*===========================*
如果第一个副本恰好位于数组的中间,那么这将是结果。请注意平均字符串长度58的巨大差异:
*==========*===========================*
| | Number of strings |
| Avg str | in the Array for |
| length | TDictionary to be faster |
*======================================*
| 30 | 109 |
| 33 | 163 |
| 36 | 163 |
| 58 | 366 |
*==========*===========================*
如果在阵列的1/4左右找到第一个副本,就会发生这种情况:
*==========*===========================*
| | Number of strings |
| Avg str | in the Array for |
| length | TDictionary to be faster |
*======================================*
| 29 | 73 |
| 32 | 73 |
| 38 | 73 |
| 57 | 109 |
*==========*===========================*
以下是测试应用程序:http://pastebin.com/vDznwKtZ
答案 1 :(得分:6)
不需要释放动态数组。它们是托管类型,编译器确保当没有对该对象的更多引用时,该对象将被销毁。
对于检测重复项,你可以这样做:
function HasDuplicates(const arr: array of string): Boolean;
var
i, j: Integer;
begin
for i := 0 to high(arr) do
if arr[i]<>'' then
for j := i+1 to high(arr) do
if arr[i]=arr[j] then
exit(True);
Result := False;
end;
我假设当你说&#34; null&#34;时,在字符串的上下文中你的意思是字符串是空的。
这是一种复杂度为O(n ^ 2)的算法。如果阵列很大,这是个坏消息。
如果您的阵列已经订购,您可以使用O(n)算法进行测试。
function OrderedHasDuplicates(const arr: array of string): Boolean;
var
i: Integer;
begin
for i := 0 to high(arr)-1 do
if arr[i]<>'' then
if arr[i]=arr[i+1] then
exit(True);
Result := False;
end;
当然,可以轻松修改这些函数以识别哪个索引是重复的:
function IndexOfDuplicate(const arr: array of string): Integer;
var
i: Integer;
begin
for Result := 0 to high(arr) do
if arr[Result]<>'' then
for i := Result+1 to high(arr) do
if arr[Result]=arr[i] then
exit;
Result := -1;
end;
答案 2 :(得分:4)
要检查字符串是否存在两次,请使用TStringList的功能。创建一个TStringList对象,添加数组的所有元素(字符串),将Sorted属性设置为True,然后从开头到结尾循环检查当前元素是否等于最后一个元素,如果它等于你找到了重复。
使用TStringList的主要优点是提供的排序功能。
答案 3 :(得分:2)
TArray类型是一种经典的动态数组,具有泛型风格。此类型由Delphi管理,因此您无需手动释放所涉及的内存。如果您存储对象或其他动态创建的变量,您仍然有责任释放该内存,但同样不是数组本身。
特别是在TArray上,作为两种托管类型,你根本不关心内存。
要检查非有序数组中的重复项,您必须循环使用它并为每个元素进行循环,与数组的其余部分进行比较,这些内容如下:
var
arr: TArray<string>;
e1, e2, e3, e4: string;
I, J: Integer;
begin
e1 := 'val1';
e2 := 'val2';
e3 := 'val3';
e4 := 'val4';
arr := TArray<string>.Create(e1, e2, e3, e4);
//check for duplicates
for I := low(arr) to High(arr) do
for J := I + 1 to High(arr) do
if arr[I] = arr[J] then
ShowMessage('A duplicate was found');
最后,字符串不能为空,因此您不必检查空元素。
准确地说,任何空字符串(''
)实际上都是一个零指针,但那是故事的另一面。