Java 使用线性递归合并/乘法数组中的元素
我必须实现一个递归方法Java 使用线性递归合并/乘法数组中的元素,java,arrays,algorithm,recursion,long-integer,Java,Arrays,Algorithm,Recursion,Long Integer,我必须实现一个递归方法merge(long[]arr,int I),如果相邻元素具有相同的值,则从索引I开始将它们相乘。 例如: 应生成如下所示的数组: {1, 4, 4} 如果一个数{1,2,2,2,2,5}出现多(n)次,则所有这些数必须相乘:{1,16,5} 已合并的数字不能再合并{1,4,4,16}->{1,16,16} 所有这些都必须通过只使用这一个方法merge并在原始数组中每个元素有一个递归调用来实现 这是一个使用递归和循环的工作实现: public static long[]
merge(long[]arr,int I)
,如果相邻元素具有相同的值,则从索引I
开始将它们相乘。
例如:
应生成如下所示的数组:
{1, 4, 4}
如果一个数{1,2,2,2,2,5}
出现多(n)次,则所有这些数必须相乘:{1,16,5}
已合并的数字不能再合并{1,4,4,16}->{1,16,16}
所有这些都必须通过只使用这一个方法merge并在原始数组中每个元素有一个递归调用来实现
这是一个使用递归和循环的工作实现:
public static long[] merge(long[] ns, int i) {
final long[] EMPTY_LONG_ARRAY = {};
if (i < 0) {
return merge(ns, 0, m); // if i negative, start at 0
} else if (i >= ns.length) {
return EMPTY_LONG_ARRAY; // if out of bounds, return empty array
} else if (i == ns.length - 1) {
return ns; // base case
} else { // recursion in here
if (ns[i] == ns[i + 1]) { // if next long is equal
int occurences = 1; // first occurence
for (int j = i; j < ns.length - 1; j++) {
if (ns[j] == ns[j + 1])
occurences++;
else
break;
} // add next occurences
long[] newArray = new long[ns.length - occurences + 1]; // new array is (occurences-1) shorter
for (int j = 0; j < newArray.length; j++) { // fill new array
if (j < i) {
newArray[j] = ns[j]; // left of i: values stay the same
} else if (j > i) {
newArray[j] = ns[j + occurences - 1]; // pull values right of i (occurences-1) to the left
} else {
int counter = occurences;
long mergedValue = ns[j];
while (counter > 1) {
mergedValue *= ns[j];
counter--;
}
newArray[j] = mergedValue; // at j: value is ns[j]^occurences
}
}
if (i == ns.length - 1)
return merge(newArray, i, m);
else
return merge(newArray, i + 1, m); // if bounds permit it, jump to next number
} else {
return merge(ns, i + 1, m); // nothing to merge, go one step forward
}
}
publicstaticlong[]合并(long[]ns,inti){
final long[]空_long_数组={};
if(i<0){
返回merge(ns,0,m);//如果i为负数,则从0开始
}如果(i>=ns.长度),则为else{
返回空的长数组;//如果超出范围,则返回空数组
}else如果(i==ns.length-1){
返回ns;//基本大小写
}else{//这里是递归
如果(ns[i]==ns[i+1]){//if next long等于
int occurrences=1;//第一次出现
对于(int j=i;ji){
newArray[j]=ns[j+发生次数-1];//将i(发生次数-1)右侧的值拉到左侧
}否则{
int计数器=发生次数;
长合并值=ns[j];
而(计数器>1){
合并值*=ns[j];
计数器--;
}
newArray[j]=mergedValue;//在j处:值为ns[j]^occurrences
}
}
如果(i==ns.length-1)
返回合并(newArray,i,m);
其他的
返回merge(newArray,i+1,m);//如果边界允许,跳到下一个数字
}否则{
返回merge(ns,i+1,m);//没有要合并的内容,向前一步
}
}
这个实现产生了正确的结果,但是递归深度是错误的(在原始数组ns[]中,每个元素需要一个递归调用)
我相信这里有一位天才可以用线性递归来解决这个问题。让我们将循环转换为递归调用。这样做的唯一原因是赋值要求它-它不是更可读的(至少对我来说)因为效率的原因,人们通常想转向另一个方向:从递归到循环 首先,代码的注释版本:
public static long[] merge(long[] ns, int i) { // i not needed, but useful for recursion
long[] out = new long[ns.length]; // for result; allocate only once
for (int j = i; j < ns.length; j++) { // main loop, condition is "j == length"
int occurences = 0;
for (int k = i; k < ns.length; k++) { // inner loop - can avoid!
if (ns[j] == ns[k]) {
occurences++;
}
}
out[j] = (long) Math.pow(ns[j], occurences); // updating the result
}
// remove additional elements
return out; // this does not remove elements yet!
}
一般的想法是将所有状态作为参数传递给递归函数,并在开始时检查循环终止,将循环代码放在中间,最后在下一次迭代中递归调用。应该是什么意思?使用循环很容易。循环可以转换为尾部递归,每个元素执行一个递归调用。如果您将代码作为循环进行发布,我可以向您展示如何将其转换为递归。我在问题中添加了一个工作代码段。使用以前版本的工作代码进行了回答,这也是smallerT汉克斯:太多了,图库西
public static long[] merge(long[] ns, int i) { // i not needed, but useful for recursion
long[] out = new long[ns.length]; // for result; allocate only once
for (int j = i; j < ns.length; j++) { // main loop, condition is "j == length"
int occurences = 0;
for (int k = i; k < ns.length; k++) { // inner loop - can avoid!
if (ns[j] == ns[k]) {
occurences++;
}
}
out[j] = (long) Math.pow(ns[j], occurences); // updating the result
}
// remove additional elements
return out; // this does not remove elements yet!
}
public static long[] merge(long[] ns) {
long[] out = new long[ns.length];
int oSize = 0; // index of element-after-last in array out
long prev = ns[0]; // previous element in ns; initial value chosen carefully
out[0] = 1; // this make the 1st iteration work right, not incrasing oSize
for (int i=0; i<ns.length; i++) {
long current = ns[i];
if (current == prev) {
out[oSize] *= current; // accumulate into current position
} else {
oSize ++; // generate output
out[oSize] = current; // reset current and prev
prev = current;
}
}
// generate final output, but do not include unused elements
return Arrays.copyOfRange(out, 0, oSize+1);
}
public static long[] merge(long[] ns) {
long[] out = new long[ns.length];
int oSize = 0;
long prev = ns[0];
out[0] = 1;
int i=0;
recursiveMerge(ns, i, out, oSize, prev); // recursion!
return Arrays.copyOfRange(out, 0, oSize+1);
}
public static void recursiveMerge(long[] ns, int i, long[] out, int oSize, long prev) {
if (i == n) return; // check "loop" termination condition
// copy-pasted loop contents
long current = ns[i];
if (current == prev) {
out[oSize] *= current; // accumulate into current position
} else {
oSize ++; // generate output
out[oSize] = current; // reset current and prev
prev = current;
}
// next loop iteration is now a recursive call. Note the i+1
recursiveMerge(ns, i+1, out, oSize, prev);
}