用卡特兰数算法解决左括号“(“和有括号“)“正常排列的问题

一、先了解两个数学概念

在了解卡特兰数之前,需要了解两个数学概念:
1、组合计算公式
在这里插入图片描述
c(m,n) = m!/(n! * (m-n)!)

2、两个集合相等的判断

如果集合A有一个公式,可以一一对应到集合B,而集合B也有一个公式一定可以一一映射到集合A,那么A集合和B集合相等。
比如如果国家集合和国旗集合,如果国家列表中每个国家都能找到国旗列表对应的国旗,而国旗列表中总能在国家列表中找到对应的国家,那么我们说国旗列表和国家列表的数量一样

二、卡特兰数介绍

卡特兰数又称卡塔兰数,
number,是组合数学中一个常出现在各种计数问题中出现的数列。其前几项为:
1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, …
k(0) = 1, k(1) = 1时,如果接下来的项满足:
k(n) = k(0) * k(n - 1) + k(1) * k(n - 2) + … + k(n - 2) * k(1) + k(n - 1) * k(0)
或者
k(n) = c(2n, n) - c(2n, n-1)
或者
k(n) = c(2n, n) / (n + 1)
就说这个表达式,满足卡特兰数

三、实际题目解决:

假设给你N个0,和N个1,你必须用全部数字拼序列
返回有多少个序列满足:任何前缀串,1的数量都不少于0的数量。

这道题其实就是左括号和有括号合法的问题,一共的排列有c(2n,n)中,减去不合理的前缀串c(2n, n + 1)种

这里就是用到集合相等的概念,我们要找到不合法的串的数量,也就是可以找到多少前缀串中1的数量小于0的数量这个数量等价于在一个集合中有n+1个0,n - 1个1的组合的总数量,也就是c(2n, n+1)。

java代码如下:

public static long ways2(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }
        long a = 1;
        long b = 1;
        int limit = n << 1;
        for (int i = 1; i <= limit; i ++) {
            if (i <= n) {
                a = a * i;
            } else {
                b = b * i;
            }
        }
        return (b / a)/(n+1);
    }

四、类似的题目

类似的问题还有:

  1. n个左括号,n个右括号,如何排列,才是符合正常的(任何前缀串,(的数量都不少于)的数量)
  2. 股票n次涨,n次跌,有多少种排列,不会有跌破当前值的时候
  3. 进出栈:n次进栈,n次出栈,有多少种合理的方式
  4. 二叉树的排列问题:有N个二叉树节点,每个节点彼此之间无任何差别,返回由N个二叉树节点,组成的不同结构数量是多少?

教授老师:左程云

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
逆波兰算法同样适用于有括号的表达式。我们可以使用一个栈来处理括号。当我们遇到括号时,将其压入栈中。当我们遇到括号时,我们可以从栈中弹出所有操作符,直到遇到括号为止。最后,我们将所有弹出的操作符应用于操作数。最后,我们将计算结果压入栈中。 以下是一个处理有括号的逆波兰算法实现的示例代码: ```csharp using System; using System.Collections.Generic; public class Program { public static void Main() { string expression = "2 * (3 + 4)"; Console.WriteLine("Expression: " + expression); Console.WriteLine("Result: " + Evaluate(expression)); } public static double Evaluate(string expression) { Stack<double> stack = new Stack<double>(); Stack<char> operators = new Stack<char>(); foreach (char c in expression) { if (char.IsDigit(c)) { stack.Push(double.Parse(c.ToString())); } else if (c == '(') { operators.Push(c); } else if (c == ')') { while (operators.Peek() != '(') { ApplyOperator(stack, operators.Pop()); } operators.Pop(); // remove the left parenthesis } else if (IsOperator(c)) { while (operators.Count > 0 && Precedence(c) <= Precedence(operators.Peek())) { ApplyOperator(stack, operators.Pop()); } operators.Push(c); } } while (operators.Count > 0) { ApplyOperator(stack, operators.Pop()); } return stack.Pop(); } private static bool IsOperator(char c) { return c == '+' || c == '-' || c == '*' || c == '/'; } private static int Precedence(char c) { switch (c) { case '+': case '-': return 1; case '*': case '/': return 2; default: throw new ArgumentException("Invalid operator: " + c); } } private static void ApplyOperator(Stack<double> stack, char op) { double operand2 = stack.Pop(); double operand1 = stack.Pop(); switch (op) { case '+': stack.Push(operand1 + operand2); break; case '-': stack.Push(operand1 - operand2); break; case '*': stack.Push(operand1 * operand2); break; case '/': stack.Push(operand1 / operand2); break; default: throw new ArgumentException("Invalid operator: " + op); } } } ``` 在上面的代码中,我们使用了两个栈,一个用于存储操作数,另一个用于存储操作符。我们遍历表达式中的每个字符,如果它是一个数字,则将其压入操作数栈中。如果它是一个运算符,则将其压入操作符栈中。如果它是括号,则将其压入操作符栈中。如果它是括号,则从操作符栈中弹出所有操作符,直到遇到括号为止,并将这些操作符应用于操作数。最后,我们返回操作数栈中的唯一元素,这就是表达式的计算结果。 在上面的示例中,我们可以计算 "2 * (3 + 4)" 这个表达式,它的计算结果是 14。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值