递归强化练习

查找字符串 s 中连续出现次数最多的字符

自己实现的代码

char MaxDupChar(const char* s, int begin, int& n)
{
    char ret = 0;

    /* 字符串只有一个字符时 */
    if(s[begin + 1] == 0)
    {
        ret = s[begin];
        n = 1;
    }
    /* 字符串大于一个字符时 */
    else
    {
        int i = begin;

        ret = MaxDupChar(s, begin + 1, n);  // 找出除了当前字符的字符串中连续出现的最大字符,连续出现次数为 n

        /* 计算出当前字符连续出现的次数 */
        while((s[i] != '\0') && (s[i] == s[begin]))
        {
            i++;
        }

        /* 如果当前字符连续出现的次数大于等于除去当前字符的子串中字符连续出现的最大次数,则更新最大次数和字符 */
        if((i - begin) >= n)
        {
            n = (i - begin);
            ret = s[begin];
        }
    }

    return ret;
}

char MaxDupChar(const char* s, int& n)
{
    return MaxDupChar(s, 0, n);
}

老师实现的代码

 void MaxDupChar(const char* s, int& n, const char* o, int& mi, int& max)
{
    /* 该字符串为空字符串 */
    if(*s == 0)
    {
        n = 0;
        mi = -1;
        max = 0;
    }
    else
    {
        MaxDupChar(s + 1, n, o, mi, max);  // 计算以下一个字符为首字符的字符串的最多连续出现字符的下标和次数

        /* 首字符和下一个字符相同 */
        if(*s == *(s + 1))
        {
            n++;  // 连续出现的字符加1
        }
        else
        {
            n = 1;  // 重置为1
        }

        /* 如果连续出现的字符数大于当前记录的连续出现的字符数,则需要更新最大连续出现字符的起始位置和次数 */
        if(n > max)
        {
            mi = s - o;  // 根据源字符串的起始地址计算出连续出现次数最多的字符所在下标
            max = n;
        }
    }
}

char MaxDupChar(const char* s, int& n)
{
    int index = -1;
    int count = 0;
    char ret = 0;

    if(s)
    {
        MaxDupChar(s, count, s, index, n);

        if(index != -1)
        {
            ret = s[index];
        }
    }

    return ret;
}

老师实现的代码使用变量 n 来保存当前字符串的首字符连续出现的次数,而不需要每次从递归中都重新计算首字符连续出现的次数,效率更高。

判断字符串 s 是不是整数字符串

自己的实现的代码

bool IsIntStr(const char* s, int begin, int end, int& n)
{
    bool ret = false;

    /* 只有这个字符串全部为数字时,才返回 true */
    if((s[begin] >= '0') && (s[begin] <= '9'))
    {
        int num = s[begin] - '0';

        /* 当这个字符串中只有一个字符的时候 */
        if((end - begin) == 0)
        {
            n = num;
            ret = true;
        }
        /* 当这个字符串有多个字符的时候 */
        else
        {
            ret = IsIntStr(s, begin + 1, end, n);  // 以 begin + 1 为首字符的字符串是否可以转换为整形,并且当前的字符可以转换为整形

            if(ret)
            {
                /* 将当前字符转换为合适的整形数 */
                for(int i = begin; i < end; i++)
                {
                    num *= 10;
                }

                n = num + n;
            }
        }
    }

    return ret;
}

bool IsIntStr(const char* s, int& n)
{
    bool ret = true;
    bool symbol = false;  // 用于标记字符串 s 的第一个字符是否为 '-'

    /* 如果字符串的第一个字符为符号 则令 s 指向下一个字符,方便统一进行处理 */
    if((s[0] == '-') || (s[0] == '+'))
    {
        if(s[0] == '-')
        {
            symbol = true;
        }

        s = s + 1;
    }

    ret = IsIntStr(s, 0, strlen(s) - 1, n);

    if(ret && symbol)
    {
        n = -n;
    }

    return ret;
}

老师实现的代码

bool IsIntStr(const char* s, int& n, int& weight)
{
    bool ret = false;

    /* 该字符串没有字符 */
    if(*s == '\0')
    {
        weight = 1;
        ret = true;
    }
    /* 当前字符串存在字符 */
    else
    {
        ret = ((*s >= '0') && (*s <= '9'));  // 当前字符是不是可以转换为数字
        ret = ret && IsIntStr(s + 1, n, weight);  // 除去当前字符的子串是不是可以转换为数字

        if(ret)
        {
            n = (*s - '0') * weight + n;
            weight *= 10;
        }
    }

    return ret;
}

bool IsIntStr(const char* s, int& n)
{
    bool ret = false;
    bool symbol = false;  // 用于标记字符串 s 的第一个字符是否为 '-'
    int weight = 0;

    n = 0;  // 计算前先对 n 清零

    if(s)
    {
        /* 如果字符串的第一个字符为符号 则令 s 指向下一个字符,方便统一进行处理 */
        if((s[0] == '-') || (s[0] == '+'))
        {
            if(s[0] == '-')
            {
                symbol = true;
            }

            s = s + 1;
        }

        ret = IsIntStr(s, n, weight);

        if(ret && symbol)
        {
            n = -n;
        }
    }

    return ret;
}

这里使用权重来保存每一位应该乘以的数值,而不需要在每次都从0去计算应该乘以的值,节省了效率。

在不排序的情况下求数组元素的中位数

int Partition(int a[], int begin, int end)
{
    int ret = 0;
    int e = a[begin];

    while(begin < end)
    {
        while((begin < end) && (a[end] >= e))
        {
            end--;
        }

        swap(a[begin], a[end]);

        while((begin < end) && (a[begin] < e))
        {
            begin++;
        }

        swap(a[begin], a[end]);
    }

    ret = begin;

    return ret;
}

int FindNumK(int a[], int begin, int end, int k)
{
    int ret = -1;

    /* 数组只有一个元素的时候,直接返回 */
    if(begin == end)
    {
        ret = a[begin];
    }
    else
    {
        int pivot = Partition(a, begin, end);  // 第 pivot 大的数

        /* 找到第 k 大的数,直接返回 */
        if(pivot == k)
        {
            ret = a[k];
        }
        /* 如果找到的下标比 k 小,则从该下标往右开始找 */
        else if(pivot < k)
        {
            ret = FindNumK(a, pivot + 1, end, k);
        }
        /* 如果找到的下标比 k 大,则从该下标往左开始找 */
        else
        {
            ret = FindNumK(a, begin, pivot - 1, k);
        }
    }

    return ret;
}

int FindMid(int a[], int n)
{
    int ret = -1;
    int k = (n - 1) / 2;  // 中位数在数组中的下标

    /* 如果要寻找的下标不合法,直接抛出异常 */
    if(k <= (n - 1))
    {
        ret = FindNumK(a, 0, n - 1, k);
    }
    else
    {
        THROW_EXCEPTION(InvalidParameterException, "k is not valid ...");
    }

    return ret;
}

找数组中中位数的问题可以转换为找数组中第 (n - 1) / 2 大的元素;这里使用了快排里的划分序列的方法来找第 k 大个数。

查找并打印二叉树中的最大路径

bool FindNodePath(BTreeNode<int>* root, BTreeNode<int>* node, DualEndQueue<BTreeNode<int>*>& dq, int& val)
{
    bool ret = false;

    if(root)
    {
        if(root == node)
        {
            dq.push(node);
            val = root->value;
            ret = true;
        }
        else
        {
            if(FindNodePath(root->left, node, dq, val) || FindNodePath(root->right, node, dq, val))
            {
                dq.push(root);
                val += root->value;
                ret = true;
            }
        }
    }

    return ret;
}

void UpdateMaxPath(BTreeNode<int>* root, int& max, SharedPointer<LinkStack<BTreeNode<int>*>>& sp)
{
    sp = new LinkStack<BTreeNode<int>*>();
    sp->push(root);
    max = root->value;
}

void UpdateMaxPath(BTreeNode<int>* root,
                int& cmax, SharedPointer< LinkStack<BTreeNode<int>*> >& cp,
                int& max, SharedPointer< LinkStack<BTreeNode<int>*> >& sp)
{
    DualEndQueue<BTreeNode<int>*> dq;
    int tmax = 0;
    bool find = FindNodePath(root, cp->top(), dq, tmax);  // 当前结点是否可以到达子树的根结点

    if(find)
    {
        tmax = tmax - dq.back()->value + cmax;  // tmax 为 root 到子树叶结点的路径和
    }

    /* 如果找到从当前结点到子树根结点的路径,并且当前结点到子树叶结点的值大于了子树的路径和,则更新子树的最大路径 */
    if(find && (tmax > cmax))
    {
        if(tmax > cmax)
        {
            dq.dismiss();  // 由于 dq 中包含了子树的根结点,所以需要将这个根结点清除

            while(dq.length() > 0)
            {
                cp->push(dq.back());
                dq.dismiss();
            }

            cmax = tmax;
        }
    }

    /* 如果根结点的值大于这颗子树的最大路径和,则更新子树的最大路径和为根结点的值 */
    if(root->value > cmax)
    {
        cp->clear();
        cp->push(root);

        cmax = root->value;
    }

    /* 如果这棵树当前没有最大路径或者子树的最大路径和大于了这棵树的历史最大路径,则更新 */
    if(sp.isNull() || (cmax > max))
    {
        sp = cp;
        max = cmax;
    }

    dq.clear();
}

void FindMaxBinTreePath(BTreeNode<int>* root, int& max, SharedPointer<LinkStack<BTreeNode<int>*>>& sp)
{
    if(root)
    {
        int lmax = 0;
        int rmax = 0;
        SharedPointer<LinkStack<BTreeNode<int>*>> pl;
        SharedPointer<LinkStack<BTreeNode<int>*>> pr;

        FindMaxBinTreePath(root->left, lmax, pl);  // 寻找左子树的路径最大值
        FindMaxBinTreePath(root->right, rmax, pr); // 寻找右子树的路径最大值

        if(pl.isNull() && pr.isNull())  // 根结点的左右结点都为空,无需递归,根结点的值即为最大值
        {
            UpdateMaxPath(root, max, sp);
        }
        else if(!pl.isNull() && pr.isNull())  // 左子树不为空,右子树为空
        {
            UpdateMaxPath(root, lmax, pl, max, sp);  // 根据左子树的最大路径来更新最大路径
        }
        else if(pl.isNull() && !pr.isNull())  // 左子树为空,右子树不为空
        {
            UpdateMaxPath(root, rmax, pr, max, sp);  // 根据右子树的最大路径来更新最大路径
        }
        else  // 左右子树都不为空
        {
            /* 根据左子树的最大路径和右子树的最大路径来更新最大路径 */
            UpdateMaxPath(root, lmax, pl, max, sp);
            UpdateMaxPath(root, rmax, pr, max, sp);
        }
    }
}

void FindMaxBinTreePath()
{
    BTree<int> bt;
    BTreeNode<int>* node = nullptr;
    int max = 0;
    SharedPointer<LinkStack<BTreeNode<int>*>> sp;

    bt.insert(2, nullptr);

    node = bt.find(2);

    bt.insert(-3, node);
    bt.insert(-1, node);

    node = bt.find(-1);

    bt.insert(-2, node);
    bt.insert(0, node);

    FindMaxBinTreePath(bt.root(), max, sp);

    cout << "maxPath = " << max << endl;
    cout << "path: ";

    while(sp->size() > 0)
    {
        cout << sp->top()->value;

        if(sp->size() > 1)
        {
            cout << " -> ";
        }

        sp->pop();
    }

    cout << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值