Dans le passé, j'ai déjà écrit un logiciel qui utilise une pile pour vérifier les équations équilibrées, mais je suis maintenant invité à écrire un algorithme similaire de manière récursive pour rechercher les parenthèses et les crochets correctement imbriqués.
Bons exemples: () [] () ([] () [])
Mauvais exemples: ((] ([)]
Supposons que ma fonction s'appelle: isBalanced.
Chaque passage doit-il évaluer une sous-chaîne plus petite (jusqu'à atteindre un cas de base de 2 à gauche)? Ou, devrais-je toujours évaluer la chaîne complète et déplacer les index vers l'intérieur?
Il y a plusieurs façons de le faire, mais l'algorithme le plus simple consiste simplement à traiter en avant-gauche, en passant la pile en tant que paramètre.
FUNCTION isBalanced(String input, String stack) : boolean
IF isEmpty(input)
RETURN isEmpty(stack)
ELSE IF isOpen(firstChar(input))
RETURN isBalanced(allButFirst(input), stack + firstChar(input))
ELSE IF isClose(firstChar(input))
RETURN NOT isEmpty(stack) AND isMatching(firstChar(input), lastChar(stack))
AND isBalanced(allButFirst(input), allButLast(stack))
ELSE
ERROR "Invalid character"
Ici, il est implémenté en Java. Notez que je l'ai changé maintenant pour que la pile insère front au lieu de back de la chaîne, pour plus de commodité. Je l'ai également modifié pour qu'il ignore les symboles autres que des parenthèses au lieu de le signaler comme une erreur.
static String open = "([<{";
static String close = ")]>}";
static boolean isOpen(char ch) {
return open.indexOf(ch) != -1;
}
static boolean isClose(char ch) {
return close.indexOf(ch) != -1;
}
static boolean isMatching(char chOpen, char chClose) {
return open.indexOf(chOpen) == close.indexOf(chClose);
}
static boolean isBalanced(String input, String stack) {
return
input.isEmpty() ?
stack.isEmpty()
: isOpen(input.charAt(0)) ?
isBalanced(input.substring(1), input.charAt(0) + stack)
: isClose(input.charAt(0)) ?
!stack.isEmpty() && isMatching(stack.charAt(0), input.charAt(0))
&& isBalanced(input.substring(1), stack.substring(1))
: isBalanced(input.substring(1), stack);
}
Harnais de test:
String[] tests = {
"()[]<>{}",
"(<",
"]}",
"()<",
"(][)",
"{(X)[XY]}",
};
for (String s : tests) {
System.out.println(s + " = " + isBalanced(s, ""));
}
Sortie:
()[]<>{} = true
(< = false
]} = false
()< = false
(][) = false
{(X)[XY]} = true
Tout d’abord, pour répondre à votre question initiale, sachez que si vous travaillez avec de très longues chaînes, vous ne voulez pas faire de copies exactes moins une lettre à chaque appel de fonction. Vous devez donc privilégier l'utilisation d'index ou vérifier que la langue de votre choix ne permet pas de faire des copies en arrière-plan.
Deuxièmement, j'ai un problème avec toutes les réponses ici qui utilisent une structure de données de pile. Je pense que le but de votre mission est que vous compreniez qu'avec la récursion, votre fonction appelle crée une pile . Vous n'avez pas besoin d'utiliser une structure de données de pile pour conserver vos parenthèses, car chaque appel récursif est une nouvelle entrée sur une pile implicite.
Je vais démontrer avec un programme C qui correspond à (
et )
. Ajouter les autres types comme [
et ]
est un exercice pour le lecteur. Tout ce que je maintiens dans la fonction est ma position dans la chaîne (transmise en tant que pointeur) car la récursivité est ma pile.
/* Search a string for matching parentheses. If the parentheses match, returns a
* pointer that addresses the nul terminator at the end of the string. If they
* don't match, the pointer addresses the first character that doesn't match.
*/
const char *match(const char *str)
{
if( *str == '\0' || *str == ')' ) { return str; }
if( *str == '(' )
{
const char *closer = match(++str);
if( *closer == ')' )
{
return match(++closer);
}
return str - 1;
}
return match(++str);
}
Testé avec ce code:
const char *test[] = {
"()", "(", ")", "", "(()))", "(((())))", "()()(()())",
"(() ( hi))) (())()(((( ))))", "abcd"
};
for( index = 0; index < sizeof(test) / sizeof(test[0]); ++index ) {
const char *result = match(test[index]);
printf("%s:\t", test[index]);
*result == '\0' ? printf("Good!\n") :
printf("Bad @ char %d\n", result - test[index] + 1);
}
Sortie:
(): Good!
(: Bad @ char 1
): Bad @ char 1
: Good!
(())): Bad @ char 5
(((()))): Good!
()()(()()): Good!
(() ( hi))) (())()(((( )))): Bad @ char 11
abcd: Good!
L'idée est de conserver une liste des crochets ouverts, et si vous trouvez un crochet de fermeture, vérifiez s'il ferme le dernier ouvert:
Lorsque la chaîne est finalement vide, si la liste des crochets est également vide (donc tous les crochets ont été fermés), retournez true
, sinon false
ALGORITHME(en Java):
public static boolean isBalanced(final String str1, final LinkedList<Character> openedBrackets, final Map<Character, Character> closeToOpen) {
if ((str1 == null) || str1.isEmpty()) {
return openedBrackets.isEmpty();
} else if (closeToOpen.containsValue(str1.charAt(0))) {
openedBrackets.add(str1.charAt(0));
return isBalanced(str1.substring(1), openedBrackets, closeToOpen);
} else if (closeToOpen.containsKey(str1.charAt(0))) {
if (openedBrackets.getLast() == closeToOpen.get(str1.charAt(0))) {
openedBrackets.removeLast();
return isBalanced(str1.substring(1), openedBrackets, closeToOpen);
} else {
return false;
}
} else {
return isBalanced(str1.substring(1), openedBrackets, closeToOpen);
}
}
TEST:
public static void main(final String[] args) {
final Map<Character, Character> closeToOpen = new HashMap<Character, Character>();
closeToOpen.put('}', '{');
closeToOpen.put(']', '[');
closeToOpen.put(')', '(');
closeToOpen.put('>', '<');
final String[] testSet = new String[] { "abcdefksdhgs", "[{aaa<bb>dd}]<232>", "[ff{<gg}]<ttt>", "{<}>" };
for (final String test : testSet) {
System.out.println(test + " -> " + isBalanced(test, new LinkedList<Character>(), closeToOpen));
}
}
SORTIE:
abcdefksdhgs -> true
[{aaa<bb>dd}]<232> -> true
[ff{<gg}]<ttt> -> false
{<}> -> false
Notez que j'ai importé les classes suivantes:
import Java.util.HashMap;
import Java.util.LinkedList;
import Java.util.Map;
public static boolean isBalanced(String str) {
if (str.length() == 0) {
return true;
}
if (str.contains("()")) {
return isBalanced(str.replaceFirst("\\(\\)", ""));
}
if (str.contains("[]")) {
return isBalanced(str.replaceFirst("\\[\\]", ""));
}
if (str.contains("{}")) {
return isBalanced(str.replaceFirst("\\{\\}", ""));
} else {
return false;
}
}
Cela n’a pas vraiment d’importance sur le plan logique: si vous conservez une pile de tous les parens non équilibrés que vous passez à chaque étape de la récursivité, vous n’aurez plus besoin de regarder en arrière. Peu importe si vous coupez la chaîne à chaque appel récursif ou si vous incrémentez simplement un index et ne regardez que le premier caractère actuel.
Dans la plupart des langages de programmation comportant des chaînes non mutables, il est probablement plus coûteux (en termes de performances) de raccourcir la chaîne que de transmettre une chaîne légèrement plus grande sur la pile. D'autre part, dans un langage comme C, vous pouvez simplement incrémenter un pointeur dans le tableau de caractères. Je suppose que la langue dépend beaucoup de laquelle de ces deux approches est la plus «efficace». Ils sont tous deux équivalents d'un point de vue conceptuel.
Je dirais que cela dépend de votre conception. Vous pouvez utiliser deux compteurs ou empiler avec deux symboles différents ou vous pouvez le gérer en utilisant la récursivité, la différence réside dans l'approche de conception.
func evalExpression(inStringArray:[String])-> Bool{
var status = false
var inStringArray = inStringArray
if inStringArray.count == 0 {
return true
}
// determine the complimentary bracket.
var complimentaryChar = ""
if (inStringArray.first == "(" || inStringArray.first == "[" || inStringArray.first == "{"){
switch inStringArray.first! {
case "(":
complimentaryChar = ")"
break
case "[":
complimentaryChar = "]"
break
case "{":
complimentaryChar = "}"
break
default:
break
}
}else{
return false
}
// find the complimentary character index in the input array.
var index = 0
var subArray = [String]()
for i in 0..<inStringArray.count{
if inStringArray[i] == complimentaryChar {
index = i
}
}
// if no complimetary bracket is found,so return false.
if index == 0{
return false
}
// create a new sub array for evaluating the brackets.
for i in 0...index{
subArray.append(inStringArray[i])
}
subArray.removeFirst()
subArray.removeLast()
if evalExpression(inStringArray: subArray){
// if part of the expression evaluates to true continue with the rest.
for _ in 0...index{
inStringArray.removeFirst()
}
status = evalExpression(inStringArray: inStringArray)
}
return status
}
Dans le langage de programmation Scala, je le ferais comme ceci:
def balance(chars: List[Char]): Boolean = {
def process(chars: List[Char], myStack: Stack[Char]): Boolean =
if (chars.isEmpty) myStack.isEmpty
else {
chars.head match {
case '(' => process(chars.tail, myStack.Push(chars.head))
case ')' => if (myStack.contains('(')) process(chars.tail, myStack.pop)
else false
case '[' => process(chars.tail, myStack.Push(chars.head))
case ']' => {
if (myStack.contains('[')) process(chars.tail, myStack.pop) else false
}
case _ => process(chars.tail, myStack)
}
}
val balancingAuxStack = new Stack[Char]
process(chars, balancingAuxStack)
}
Veuillez éditer pour le rendre parfait.
Je ne faisais que suggérer une conversion en Scala.
Solution PHP pour vérifier les parenthèses équilibrées
<?php
/**
* @param string $inputString
*/
function isBalanced($inputString)
{
if (0 == strlen($inputString)) {
echo 'String length should be greater than 0';
exit;
}
$stack = array();
for ($i = 0; $i < strlen($inputString); $i++) {
$char = $inputString[$i];
if ($char === '(' || $char === '{' || $char === '[') {
array_Push($stack, $char);
}
if ($char === ')' || $char === '}' || $char === ']') {
$matchablePairBraces = array_pop($stack);
$isMatchingPair = isMatchingPair($char, $matchablePairBraces);
if (!$isMatchingPair) {
echo "$inputString is NOT Balanced." . PHP_EOL;
exit;
}
}
}
echo "$inputString is Balanced." . PHP_EOL;
}
/**
* @param string $char1
* @param string $char2
* @return bool
*/
function isMatchingPair($char1, $char2)
{
if ($char1 === ')' && $char2 === '(') {
return true;
}
if ($char1 === '}' && $char2 === '{') {
return true;
}
if ($char1 === ']' && $char2 === '[') {
return true;
}
return false;
}
$inputString = '{ Swatantra (() {} ()) Kumar }';
isBalanced($inputString);
?>