我正在尝试编写一个递归函数,检查两个数组是否具有相同的元素,即使它们没有排序,但是我不能更改数组而我无法复制它们或使用第三个/第四个数组,它必须是递归的,最后,我不能改变函数的签名。
所以现在我必须摆脱overwrite(A2, len, i);
,因为它正在摧毁A2
,但是我没有看到任何方法去做它仍然有一个有效的功能......我可以提示吗怎么做?也许有一种方法可以通过交换它们来保存A2
的元素,然后在递归结束时恢复它们?
简而言之,下面的算法对A2中A1的最后一个元素进行线性搜索,如果找到,覆盖它并继续,这样就完成了,因此算法不会选择相同的元素两次,达到停止条件意味着所有元素都存在,因此它将返回true,否则将返回false。
bool foo(int A1[], int A2[], int len){//both arrays are size len
int i;
bool found = false;
if (len == 0)return true;//stopping condition for recursion
else{
for (i = 0; i < len && !found; i++)//linear search
if (A1[len - 1] == A2[i]){
overwrite(A2, len, i);//this function shifts back the whole array
found = true;
}
if (found == false) return false;
else foo(A1, A2, len - 1);
}
}
示例i / o:
A1: 3 2 1
A2: 1 2 3
True
A1: 3 2 3
A2: 1 2 3
False
答案 0 :(得分:5)
解决方案可能是:
M
中的最大值A1
以及出现的次数A2
是否相同,包括计数M1
的所有值中的最大值M
以及A1中存在的次数A2
是否相同,包括计数M2
的所有值中的最大值M1
以及A1中存在的次数A2
是否相同,包括计数A1
和A2
的计数器为零或不同代码:
bool checkSame(int *A1, int *A2, int len) {
struct Same {
static bool check(int *A1, int *A2, int len, int limit) {
int index1=-1, count1=0;
for (int i=0; i<len; i++) {
if (A1[i] <= limit) {
if (index1==-1 || A1[i] > A1[index1]) {
index1 = i;
count1 = 1;
} else if (A1[i] == A1[index1]) {
count1++;
}
}
}
int index2=-1, count2=0;
for (int i=0; i<len; i++) {
if (A2[i] <= limit) {
if (index2==-1 || A2[i] > A2[index2]) {
index2 = i;
count2 = 1;
} else if (A2[i] == A2[index2]) {
count2++;
}
}
}
if (index1 == -1 && index2 == -1) return true;
if (count1 != count2 || count1 == 0 ||
A1[index1] != A2[index2]) return false;
return check(A1, A2, len, A1[index1]-1);
}
};
return Same::check(A1, A2, len, INT_MAX);
}
该算法及时为O(n ^ 2)(最坏情况:数组相同且所有值都是唯一的),如果编译器支持尾调用优化,则需要恒定空间。
以下是我的电脑上0到3000个元素所需时间的图表。
请注意,然而这一切并不是解决问题的理想方案,而只是徒劳无功。一个真正的解决方案当然需要更多的上下文,因为有不同的最优性标准,但我可能会选择一个封闭的哈希表...在处理A1时添加元素并删除处理A2的元素(删除会在某些情况下失败)当且仅当数组不同时才指出):
bool checkSame2(int *A1, int *A2, int len) {
std::vector<int> ht(len, -1), next(len, -1);
for (int i=0; i<len; i++) {
int k = (unsigned)A1[i]*69069 % len;
next[i] = ht[k]; ht[k] = i;
}
for (int i=0; i<len; i++) {
int k = (unsigned)A2[i]*69069 % len;
int prev=-1,p=ht[k];
while (p!=-1 && A1[p] != A2[i]) {
prev = p; p = next[p];
}
if (p == -1) return false;
if (prev == -1) ht[k] = next[p]; else next[prev] = next[p];
}
return true;
}
此解决方案的执行时间是紫色线在前一个图表中触及N轴(很难用这个比例判断,但是它的线性+噪音,如预期的那样)。
出于好奇,我还尝试了什么是解决方案,如果&#34;最佳&#34;意味着只是让一些不起作用的东西:
bool checkSame3(int *A1, int *A2, int len) {
std::map<int, int> counts;
for (int i=0; i<len; i++) counts[A1[i]]++;
for (int i=0; i<len; i++) {
if (--counts[A2[i]] < 0) return false;
}
return true;
}
,毫不奇怪,这比我手机上的手动编码哈希表版本慢30-40倍(但当然仍然比递归版本快得多)。
答案 1 :(得分:1)
如果允许临时更改数组,只要在最后一次递归调用返回之前还原它们,就可以将A2
中的匹配元素与索引处的元素交换在递归调用之前len - 1
,然后将它们交换回来。由于递归调用只会查看索引范围0
到len - 2
,因此不会考虑匹配元素。
答案 2 :(得分:1)
这是一个满足您所有要求的解决方案。它重新排列数组,然后重新排列它们。它使用递归,不使用其他数组,也不更改函数签名。
bool foo(int A1[], int A2[], int len){
int i;
if (len == 0){
return true;
} else {
for (i = len - 1; i >= 0; i--){
if (A1[len - 1] == A2[i]){
A2[i] = A2[len - 1];
A2[len - 1] = A1[len - 1];
bool result = foo(A1, A2, len - 1);
A2[len - 1] = A2[i];
A2[i] = A1[len - 1];
return result;
}
}
return false;
}
}