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);     
}