当前位置: 首页 > 知识库问答 >
问题:

C语言中递归求不同数的方法

滕渝
2023-03-14

假设数组A:1,2,3,1,1,3。不同的整数应在数组B中:1,2,3。然后,函数应打印:[1,1][1,2][1,3][2,1][2,2][2,3][3,1][3,2][3,3]。

我试图解决不重复的整数问题,但没有递归

#include <iostream>
#include <algorithm>
using namespace std;
    
void uniqueNumber(int A[], int n, int B[], int &dimB ){
    sort(A, A+n);
    
    for( int i = 0; i < n; i++ ){
        if( A[i] == A[i+1] ){
            continue;
        }
        else {
            B[dimB++] = A[i];
        }
    }
}

但问题是我必须以递归的方式解决它,有什么想法吗?

共有2个答案

张森
2023-03-14

递归只是另一种循环方式。它通常是一种干净的方法,尽管它通常不会像实际的那样优化,而循环并且除了尾部递归算法之外,它可以通过堆栈内存,除非数据大小很小,或者算法是对数或更好。这是一种线性算法(遍历数组),所以我不喜欢递归用于真正的解决方案,尽管这是一个很好的学习练习。

重要的是,使用递归时要考虑以下几点:数据的结构,不变量是什么(也就是说,在递归发生时,可以依靠什么保持为真),以及何时应该停止(“基本”情况)。

当您递归“遍历”数据时,通常一次只查看一个元素或一小块数据,以逐步构建解决方案。

有时,您必须在开始递归之前直接处理一些特殊情况。这对于处理不属于递归所需不变量的情况是必要的,例如当没有足够的数据来满足数据的预期“形状”时。

给定您的界面:

void uniqueNumber(int A[], int n, int B[], int &dimB );

我们已经知道了一些事情。首先,这不是一个好的界面。:)数组不是函数的安全参数,很容易出错。其次,dimB是一个“out”参数,除非有必要,否则不支持它(因为我们可以将大小作为函数返回值返回)第三,我们不知道B的大小,但必须假设它至少与A一样大。(这是该接口的另一个安全问题。)

但假设函数签名是固定的,这就是我们必须处理的。那么我们想要什么呢?

目标:按排序顺序找到每个写入B的唯一数字,并更新dimB以告诉调用者有多少项目写入B。

所以基本上我们想这样做:

  • 对数字进行排序
  • 使用索引i迭代数组
    • 如果当前值(A[i])与之前的值(A[i-1])不同,则将当前值附加到B,并增加dimB
    • 不要从A[-1]

    上面的不变量是:

    • 对于任何索引i,其前面都有一个有效值
    • 索引是

    主要步骤将是:

    >

    否则我们的不变量就会满足:我们至少有一个元素。第一个元素保证不在B中,所以排序A,然后立即将A[0]添加到B中,然后开始递归。当A的大小正好为1时,也可以处理这种情况。递归将立即返回。

    通常,递归算法由两个函数编写:第一个函数处理特殊情况并进行其他设置,然后调用第二个函数进行递归工作,知道所有特殊情况都已处理。

    考虑到上述因素后,这里是uniqueNumbers函数:

    void uniqueNumber(int A[], int n, int B[], int &dimB ) {
        if (n > 0) {
            std::sort(A, A+n);
            B[dimB++] = A[0];
            detail::uniqueNumberRec(1, A, n, B, dimB);
        }
    }
    

    由于递归帮助函数不是直接调用的,而是实现细节,我把它放在细节命名空间中,这是记录用户不应该直接调用它的常见做法,它也有助于将混乱排除在全局命名空间之外。

    那么递归函数是做什么的呢?

    它采用当前索引和初始函数采用的参数,这是上述描述的直接结果:

    namespace detail {
    void uniqueNumberRec(int curIdx, int A[], int n, int B[], int &dimB ) {
    
        // Base case: stop when we reach the end
        if (curIdx == n) {
            return;
        }
    
        // if current element differs from previous, include it in answer
        if (A[curIdx] != A[curIdx-1]) {
            B[dimB++] = A[curIdx];
        }
    
        // recurse onto next element in sequence, so increment curIdx
        uniqueNumberRec(curIdx + 1, A, n, B, dimB);
    }
    } // namespace detail
    

    重要的是要注意传递到递归函数(从initail函数)的初始索引是1,而不是0,因为我们已经将第一个元素添加到输出中,所以已经过去了。

    只要我们知道反复调用的curIdx 1最终将达到n,我们就知道递归将取得进展并将终止。我们已经在第一个函数中验证了n为正。

    有几件事值得注意:

    • 如果n==0,我们什么都不做
    • 如果n==1,我们将A的唯一元素添加到B中,然后递归,但由于curIdx==1和n==1,递归立即停止
    • 如果n

    同样值得注意的是:代码中有一个bug:

        if( A[i] == A[i+1] ){
    

    如果i是A中的最后一项,则A[i 1]读取超出界限。这是不可接受的。这就是为什么我在确保有前一个之后,阅读了前一个。

    编译器资源管理器示例:https://godbolt.org/z/8z3cf5Tab

鞠鸿雪
2023-03-14

我可以提供你的问题的解决方案,使用算法深度优先搜索。

#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
void showContentSet(std::set<int>& input)
{
    for(auto iterator=input.begin(); iterator!=input.end(); ++iterator)
    {
        std::cout<<*iterator<<", ";
    }
    return;
}
void dfs(int current, int previous, std::vector<int>& first, std::set<int>& second, std::vector<int>& visited)
{
    if(visited[current]==1)
    {
        return;
    }
    visited[current]=1;
    for(int next=(current+1); next<first.size(); ++next)
    {
        if(next==previous)
        {
            continue;
        }
        if(first[next]==first[current])
        {
            continue;
        }
        else
        {
            second.insert(first[current]);
            second.insert(first[next]);
        }
        dfs(next, current, first, second, visited);
    }
    if(current==0)
    {
        for(auto& item : second)
        {
            for(auto& itemSet : second)
            {
                std::cout<<"["<<item<<", "<<itemSet<<"]"<<", ";
            }
        }
    }
    return;
}
void solve()
{
    const int maximumSize=20;
    std::vector<int> visited(maximumSize, 0);
    std::vector<int> inputVector={1, 2, 3, 1, 1, 3};
    std::set<int> inputSet;
    dfs(0, -1, inputVector, inputSet, visited);
    return;
}
int main()
{
    solve();
    return 0;
}

结果如下:

[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3],
 类似资料:
  • 我是新的编码和需要作出曼德尔布罗特函数。对于那些不知道的人来说,Mandelbrot集合是一组复数。从本质上讲,你可以从一个复数开始,然后把它平方,然后把它加到原来的复数中。例如,如果我使用数字1,集合将是0、1、2、5、26。。。我从0,1,(1^2)1=2,(2^2)1=5,(5^2)1=26得到这个值。现在,我的递归函数应该使用两个输入来求这个集合的和:一个数字n,它是我们进入集合的距离。例

  • 主要内容:斐波那契数列,数字阶乘,多个函数组成递归很对编程语言都支持递归函数,Go语言也不例外,所谓递归函数指的是在函数内部调用函数自身的函数,从数学解题思路来说,递归就是把一个大问题拆分成多个小问题,再各个击破,在实际开发过程中,递归函数可以解决许多数学问题,如计算给定数字阶乘、产生斐波系列等。 构成递归需要具备以下条件: 一个问题可以被拆分成多个子问题; 拆分前的原问题与拆分后的子问题除了数据规模不同,但处理问题的思路是一样的; 不能无限制的

  • 递归,就是在运行的过程中调用自己。 语法格式如下: func recursion() { recursion() /* 函数调用自身 */ } func main() { recursion() } Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。 递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。 阶乘 以下实

  • 主要内容:递归的进入,递归的退出,递归的条件,更多关于递归函数的内容一个函数在它的函数体内调用它自身称为 递归调用,这种函数称为 递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。 递归函数不是C语言的专利, Java、 C#、 JavaScript、 PHP 等其他编程语言也都支持递归函数。 下面我们通过一个求阶乘的例子,看看递归函数到底是如何运作的。阶乘 n! 的计算公式如下: 根据公式编写如

  • 我或多或少只是在学习C语言,我被分配了一个简单的任务,处理双链表、动态数据分配和递归。我创建了一个只有10个整数的数组,我试图使用递归将这些整数放入一个排序的双链表中。我在向链表中插入节点时遇到了一些问题;我想我已经搞定了第一个节点,但我不确定其他节点是否有意义。现在我也遇到了一个分割错误。。。谢谢你的帮助!

  • 我有一个很难处理的任务。 我试图编写一个递归函数(完全没有循环),给定一个数组及其长度,它将打印一对子数组,每个子数组的和将是整个数组和的一半。换句话说,数组被分成两组整数,以便它们的和相等。 例如,给定数组{1,2,2,0,5},函数应输出{1,2,2}{0,5} 我必须递归地做,用一个只得到数组本身及其大小的函数。我也只允许使用一个额外的递归函数来解决这个问题。 任何想法或想法都将受到最大的赞