英特尔采访问题: 检查具有3个差异括号的字符串是否使用堆栈没有进行平衡。
好吧,第一个问题就是实现它,我使用堆栈轻松完成。 但是,如果我被要求在没有O(n)空间的情况下实施它,我相信我会被卡住。
要求: O(1)空格,并尽可能高效,应该是O(n)。
你会如何处理这个问题? 3个指针,3个计数器?
答案 0 :(得分:1)
这取决于具体要求。如果你真的只需要平衡 parantheses,那么有三个计数器的方法可以使用,例如像这样:
#include <stdio.h>
int accept(char (*next)(void))
{
char x;
int b[3] = { 0 };
while ( (x = next()) )
{
switch (x)
{
case '{':
++b[0];
break;
case '[':
++b[1];
break;
case '(':
++b[2];
break;
case '}':
--b[0];
break;
case ']':
--b[1];
break;
case ')':
--b[2];
break;
}
if (b[0] < 0 || b[1] < 0 || b[2] < 0) return 0;
}
return (b[0] == 0 && b[1] == 0 && b[2] == 0);
}
char getnextchar(void)
{
int c = getchar();
if (c == EOF) return 0;
return (unsigned char)c;
}
int main(void)
{
puts(accept(getnextchar) ? "balanced" : "unbalanced");
return 0;
}
请注意,getnextchar()
函数仅用于获取字符串的灵活性。如果您愿意,可以将其简化为直接拍摄char *
。
如果要求嵌套也需要正确,那么我在程序中看不到堆栈的唯一选项就是递归。当然,你的C实现很可能使用堆栈进行函数调用,所以在实践中,这是没用的......但是,嘿,你的程序中没有堆栈 / em>的!例如:
#include <stdio.h>
int accept(char (*next)(void), char c)
{
char x;
int ok = 1;
while ( (x = next()) )
{
switch (x)
{
case '{':
ok = accept(next, '}');
break;
case '[':
ok = accept(next, ']');
break;
case '(':
ok = accept(next, ')');
break;
case '}':
case ']':
case ')':
return x == c;
}
if (!ok) return 0;
}
return !c;
}
char getnextchar(void)
{
int c = getchar();
if (c == EOF) return 0;
return (unsigned char)c;
}
int main(void)
{
puts(accept(getnextchar, 0) ? "balanced" : "unbalanced");
return 0;
}
答案 1 :(得分:1)
你甚至不能在诚实的O(1)空间中计算一种括号,因为你需要计数器的log(n)位。
如果你可以欺骗并假装整数占用O(1)空间,你可以在一个整数内构建一个堆栈。例如,假设[
为0,(
为1,{
为2,表示您的堆栈内容为基数3。当然,这需要使用O(n)位而不是O(log(n))位的整数,如同一种括号一样。
实际上可以做得更好:由于Ritchie和Springsteel有一个O(log(n))空间algorithm(pdf),但它是一个严肃的CS理论工作,你是在面试期间不应该发现类似的东西。
编辑如果你可以修改输入字符串,那么就是一个简单的解决方案,它使用字符串的扫描部分进行存储,如@jxh的回答。
答案 2 :(得分:1)
O ( n )并不意味着一次通过。向后扫描以找到先前打开的括号当然必须被视为“不是堆栈”。为了处理字符串中“死区”的部分因为它们被关闭,算法可以向后移动新遇到的开括号,覆盖死区。
一些伪代码:
bool matched_parens (char *s) {
char *p = s;
char *cur_open = s;
if (s == NULL || *s == '\0') return true;
if (!IS_OPEN(*s)) return false;
while (*++p) {
if (IS_OPEN(*p)) {
*++cur_open = *p;
continue;
}
if (!IS_MATCHED(*cur_open, *p)) return false;
if (cur_open > s) --cur_open;
else {
cur_open = s = ++p;
if (*p == '\0') return true;
if (!IS_OPEN(*p)) return false;
}
}
return false;
}
如果由于字符串无法被覆盖而被迫向后扫描死区,则向后扫描的数字仅受字符串本身大小的限制,算法变为 O (< EM>名词 2 的)。
答案 3 :(得分:0)
假设通过&#34;三个差异括号&#34;你的意思是三种类型,所以&#34; [&#34;,&#34; {&#34;,&#34;(&#34; ...
不使用堆栈我试试这个(伪代码......还是尝试编写代码)
OPEN_PARENS = [ "{", "[", "(" ]
Find last occurrence of one of OPEN_PARENS
Find next occurrence of corresponding closing paren.
If find another closer that does not correspond then error.
When closing paren found replace it with a space (and also the opening paren)
Repeat until no opening paren is found
Check there are no closing parens.
只是一个想法,还要测试它。它还假设原始输入字符串是可变的。
这不使用堆栈并且不占用额外的内存,但现在是O(n ^ 2)-ish解析而不是O(n)解析。
编辑:Matt指出这仍然使用内存。它是已经为字符串分配的内存,但是如果字符串是const,那么这不会起作用。所以......正如Matt建议只存储指向最后一个删除的开括号的指针也可以。我认为你必须为每种类型的括号添加一个计数。错误..也许是这样的。我现在没有时间,但会在晚上重新检查这个逻辑。
numSq = 0;
numCurley = 0;
numCurve = 0;
OPEN_PARENS = [ "{", "[", "(" ]
Find last occurrence of one of OPEN_PARENS before the last seen (if any other wise search from end of string),
store pointer to it and increment a count for that type of parenthesis.
If none found exit success
Find next occurrence of corresponding closing paren.
If find another closer that does not correspond then decrement count
for that closer. If count reaches negative then there is an error in string.
When closing param found start again
它的工作原理是,一旦你匹配内部最匹配的括号,匹配进一步可以&#34;忽略&#34;他们,但需要跟踪计数,因为必须有某种&#34;记忆&#34;在系统中。然而,使用一个指针和3个计数器将是O(1)存储器。仍然是相同的O(n ^ 2) - 解析时间。
答案 4 :(得分:0)
国家机器救援:
(手工制作,所以可能会有一些拼写错误; - )
这只适用于三对不同的括号(好吧,至少不适用于相同类型的嵌套集)
#include <stdio.h>
#include <string.h>
int check_parens(char *str)
{
int state;
/*
** 1 (
** 2 ([
** 3 ([{
** 4 ({
** 5 ({[
** 6 [
** 7 [(
** 8 [({
** 9 [{
** 10 [{(
** 11 {
** 12 {(
** 13 {([
** 14 {[
** 15 {[(
*/
for (state=0; *str; str++) {
if ( !strchr( "()[]{}", *str )) continue;
switch(state) {
case 0:
if (*str == '(') { state =1; continue; }
if (*str == '[') { state =6; continue; }
if (*str == '{') { state =11; continue; }
return -1;
case 1: /* ( */
if (*str == ')') { state =0; continue; }
if (*str == '[') { state =2; continue; }
if (*str == '{') { state = 4; continue; }
return state;
case 2: /* ([ */
if (*str == ']') { state = 1; continue; }
if (*str == '{') { state = 3; continue; }
return state;
case 3: /* ([{ */
if (*str == '}') { state = 2; continue; }
return state;
case 4: /* ({ */
if (*str == '}') { state = 1; continue; }
if (*str == '[') { state = 5; continue; }
return state;
case 5: /* ({[ */
if (*str == ']') { state = 4; continue; }
return state;
case 6: /* [ */
if (*str == ']') { state = 0; continue; }
if (*str == '(') { state = 7; continue; }
if (*str == '{') { state = 9; continue; }
return state;
case 7: /* [( */
if (*str == ')') { state = 6; continue; }
if (*str == '{') { state = 8; continue; }
return state;
case 8: /* [({ */
if (*str == '}') { state = 7; continue; }
return state;
case 9: /* [{ */
if (*str == '}') { state = 6; continue; }
if (*str == '(') { state = 10; continue; }
return state;
case 10: /* [{( */
if (*str == ')') { state = 9; continue; }
return state;
case 11: /* { */
if (*str == '}') { state = 0; continue; }
if (*str == '(') { state = 12; continue; }
if (*str == '[') { state = 14; continue; }
return state;
case 12: /* {( */
if (*str == ')') { state = 11; continue; }
if (*str == '[') { state = 13; continue; }
return state;
case 13: /* {([ */
if (*str == ']') { state = 12; continue; }
return state;
case 14: /* {[ */
if (*str == ']') { state = 11; continue; }
if (*str == '(') { state = 15; continue; }
return state;
case 15: /* {[( */
if (*str == ')') { state = 14; continue; }
return state;
}
}
return state;
}
int main(int argc, char **argv)
{
int result;
result= check_parens(argv[1]);
printf("%s := %d\n", argv[1], result);
return result;
}
更新:加工括号应该是adjecent,或者在字符串的外部(忽略普通字符)所以我们可以通过从两边工作来减少字符串
int check_parens2(char *str, unsigned len)
{
while (len) {
char *cp;
str[len] = 0;
fprintf(stderr, "%s\n", str);
/* if there is any non-paren character at the beginning or end we can skip it */
cp = strchr( "()[]{}", *str ) ;
if (!cp) { len--; str++; continue; }
cp = strchr( "()[]{}", str[len-1] ) ;
if (!cp) { len--; continue; }
/* if the first two characters match : we can skip them */
/* TODO: we should also skip enclosed *normal* characters */
if (str[0] == '(' && str[1] == ')' ) { str +=2; len -= 2; continue; }
if (str[0] == '[' && str[1] == ']' ) { str +=2; len -= 2; continue; }
if (str[0] == '{' && str[1] == '}' ) { str +=2; len -= 2; continue; }
/* the same if the last two characters match */
if (str[len-2] == '(' && str[len-1] == ')' ) { len -= 2; continue; }
if (str[len-2] == '[' && str[len-1] == ']' ) { len -= 2; continue; }
if (str[len-2] == '{' && str[len-1] == '}' ) { len -= 2; continue; }
/* if begin&end match : we can skip them */
if (str[0] == '(' && str[len-1] == ')' ) { str++; len -= 2; continue; }
if (str[0] == '[' && str[len-1] == ']' ) { str++; len -= 2; continue; }
if (str[0] == '{' && str[len-1] == '}' ) { str++; len -= 2; continue; }
break;
}
return len;
}
答案 5 :(得分:0)
这可以通过保留分隔符的计数器来完成,分隔符在每次遇到开始分隔符时递增,并在每次遇到结束分隔符时递减。为了考虑正确的嵌套,可以使用每次遇到结束分隔符时从当前位置向后迭代字符串的函数。此函数通过计算在此向后遍历期间遇到的开始和结束分隔符来查找相应的开始分隔符。
这是空间中的O(1),但时间复杂度中不是O(n):
#include <stdio.h>
#include <string.h>
int is_balanced(const char *str);
int is_corresponding_open(const char *to, const char *from, const char c);
int main(void)
{
char input[4096];
for (;;) {
puts("Enter an expression:");
fgets(input, sizeof input, stdin);
if (input[0] == '\n') {
break;
}
input[strcspn(input, "\r\n")] = '\0';
printf("%s is %s\n",
input,
is_balanced(input) ? "balanced" : "not balanced");
}
return 0;
}
int is_balanced(const char *str_ptr)
{
const char *start = str_ptr;
int count_paren = 0;
int count_brace = 0;
int count_bracket = 0;
int valid_nesting = 1;
while (*str_ptr && valid_nesting) {
switch (*str_ptr) {
case '(':
++count_paren;
break;
case '{':
++count_brace;
break;
case '[':
++count_bracket;
break;
case ')':
if (is_corresponding_open(start, str_ptr, '(')) {
--count_paren;
} else {
valid_nesting = 0;
}
break;
case '}':
if (is_corresponding_open(start, str_ptr, '{')) {
--count_brace;
} else {
valid_nesting = 0;
}
break;
case ']':
if (is_corresponding_open(start, str_ptr, '[')) {
--count_bracket;
} else {
valid_nesting = 0;
}
break;
}
++str_ptr;
}
return !(count_paren || count_brace || count_bracket) && valid_nesting;
}
int is_corresponding_open(const char *to, const char *from, const char c)
{
int validity = 0;
int nesting = 0;
while (to <= from) {
if (nesting == 1 && *from == c) {
validity = 1;
break;
}
if (*from == ')' || *from == '}' || *from == ']') {
++nesting;
} else if (*from == '(' || *from == '{' || *from == '[') {
--nesting;
}
--from;
}
return validity;
}
示例互动:
Enter an expression:
(1 (2 (3)))
(1 (2 (3))) is balanced
Enter an expression:
(1 [2 {3 4}] 5)
(1 [2 {3 4}] 5) is balanced
Enter an expression:
(1 {2 [3}])
(1 {2 [3}]) is not balanced
Enter an expression:
1 [2 (3)]
1 [2 (3)] is balanced
Enter an expression: