您的位置:首页 > 其它

关于字符串操作的一个小例子(递归实现)

2015-04-23 16:54 351 查看
  字符串在.NET中项目中非常常用。关于String的介绍就不多说了。

  背景:今天和同事讨论一个问题,去除括号的问题。

  问题描述:一段字符串,去除字符串中小括号中的内容,小括号可能有嵌套情况。

  解决思路:1、先去除最内层的小括号;2、进行完第一步之后得到新的字符串,再执行第一步。3、直到最后没有括号。

  代码:

private static string DeleteTemp(string name)
{
for (int i = 0; i < name.Length; i++)
{
if (name[i].Equals(')'))
{
int firstIndex = name.IndexOf(name[i]);

//替换掉一个内层括号
string subString = name.Substring(0, firstIndex + 1);
int index = subString.LastIndexOf('(');
string tempString = subString.Substring(index);
subString = subString.Replace(tempString, "");

string dd = subString + name.Substring(firstIndex + 1);
name = DeleteTemp(dd);
}
}
return name;
}


  测试示例:

string name = "110kV1#母分开关由运行改热备用(110kV1#母分备自投由信号改跳闸(1区),110kV1#母分保护由跳闸改信号(1区))";
var s = DeleteTemp(name);
Console.WriteLine(s);


  输出结果:应该输出“110kV1#母分开关由运行改热备用”

  结果确实输出了“110kV1#母分开关由运行改热备用”

  总结:1、使用了String的IndexOf、LastIndexOf、SubString、Replace等函数;

     2、运用了递归调用。

  今天看《代码大全》,找到一个解决本问题的一个方法。

  思路:因为“(”和“)”是成对出现的,所以可以利用这一特点。

  步骤:1、声明变量,遇到“(”就加1;遇到“)”就减1。 2、最后所得值为0,则匹配成功。3、用SubString函数截取需要的部分。

  代码:

private static string Delete(string name)
{
int flag = 0;
int firstIndex = -1;
foreach (char charTemp in name)
{
if (charTemp.Equals('('))
{
flag++;
if (firstIndex != -1)
{
continue;
}
firstIndex = name.IndexOf(charTemp);
}
if (charTemp.Equals(')'))
{
flag--;
}
}
if (flag != 0)
{
Console.WriteLine("括号不!与配匹");
return "";
}
else
{
return name.Substring(0, firstIndex);
}
}


  晚上回到家之后(2014-04-23),突然想到Delete方法少考虑一种情况,例如当name=“110kV1#母分开关由运行改热备用(110kV1#母分备自投由信号改跳闸(1区),110kV1#母分保护由跳闸改信号(1区)),1111(cdhfhcdkj(cdsdf(dsd)fed)s)”就返回不了,想要的结果。

  使用Delete函数输出为:“110kV1#母分开关由运行改热备用”

  应该输出:“110kV1#母分开关由运行改热备用,1111”.

  对Delete就行修改,名字改为IsLegal,IsLegal函数用于判断字符串中的“(”和“)”是否成对出现,即是否是合法的字符串:代码如下

private static bool IsLegal(string name)
{
int flag = 0;
int index = 0;
while (index < name.Length)
{
switch (name[index])
{
case '(':
flag++;
break;
case ')':
flag--;
break;
}
index++;
}
return flag == 0;
    }


  今天(2015-04-28)重新思考这个问题,发现原来的算法删除一对最内部的括号后,就要再次遍历整个字符串。所以今天改进该方法。

  思路:与上面看代码大全的思路一样。

  代码:

private static string DeleteTwo(string name)
{
List<int> list = new List<int>();
string temp = name;
if (name.Contains("(") || name.Contains(")"))
{
GetIndex(name, ref list);
temp = name.Substring(0, list[0]) +
name.Substring(list[list.Count - 1] + 1, name.Length - list[list.Count - 1] - 1);
return DeleteTwo(temp);
}
return temp;
}

private static void GetIndex(string name, ref List<int> list)
{
int index = 0;
int flag = 0;
while (index < name.Length)
{
switch (name[index])
{
case '(':
flag++;
list.Add(index);
break;
case ')':
flag--;
list.Add(index);
if (flag == 0)
{
return;
}
break;
}
index++;
}
}


  图像示意:今天(2015-04-28)的思路示意图。  


  最开始时示意图:



  总结:采用今天的方法,代码会稍微多了几行,但是递归调用的次数明显减少。对自己写过的代码不断优化,重构是一件幸福的事。如果以后发现更好的办法,我会在加在本博客后面的。

  今天(2015-01-30)对我的算法重新审视了一下,发现算法中还有可优化的地方。本人文笔不太好,还是画图吧,直接了然。请看下图:

  




  代码修改:主要修改DeleteTwo方法。代码如下:

private static string DeleteTwo(string name)
{
List<int> list = new List<int>();
string temp = name;
if (name.Contains("(") || name.Contains(")"))
{
GetIndex(name, ref list);

string first_half_name = name.Substring(0, list[0]); //前半部分
string second_half_name = name.Substring(list[list.Count - 1] + 1,
name.Length - list[list.Count - 1] - 1);        //后半部分
temp = first_half_name +DeleteTwo(second_half_name);
return DeleteTwo(temp);
}
return temp;
}


  

  对GetIndex方法的修改主要是为了防止,输入不合法的情况。当‘(’和‘)’不匹配时,提示报错。红色部分就是添加的代码。

private static void GetIndex(string name, ref List<int> list)
{
int index = 0;
int flag = 0;
while (index < name.Length)
{
switch (name[index])
{
case '(':
flag++;
list.Add(index);
break;
case ')':
flag--;
list.Add(index);
if (flag == 0)
{
return;
}
break;
}
index++;
}
if (list.Count % 2 != 0)  //如果左右括号不匹配就跑出错误
{
throw new Exception("名称出错了,‘(’与‘)’不匹配!");
}
}


  日期(2015-05-06)算法再思考。在函数DeleteTwo算法中使用了递归;有关递归的问题请看 C#中的递归APS和CPS模式详解(转载)

  需要对递归进行优化,尾递归优化在于使堆栈可以不用保存上一次的返回地址/状态值,从而把递归函数当成一个普通的函数调用。

递归实际上是依赖上次的值,去求下次的值。 如果我们能把上次的值保存起来,在下次调用时传入,而不直接引用函数返回的值。 从而使堆栈释放,也就达到了尾递归优化的目的。

  在算法中引入StringBuilder储存上次函数的值,从而使堆栈不用保存上一次返回的地址/状态值,达到优化的目的。代码如下:

private static string DeleteTwo(string name, ref StringBuilder sb)
{
List<int> list = new List<int>();

if (!(name.Contains("(") || name.Contains(")")))
return sb.ToString();

GetIndex(name, ref list);
string first_half_name = name.Substring(0, list[0]); //前半部分
sb.Append(first_half_name);
string last_half_name = name.Substring(list[list.Count - 1] + 1, name.Length - list[list.Count - 1] - 1);        //后半部分
return DeleteTwo(last_half_name, ref sb);
}


  客户端代码:

       StringBuilder sb = new StringBuilder();
string name = "110kV1#母分开关由运行改热备用(110kV1#母分备自投由信号改跳闸(1区),110kV1#母分保护由跳闸改信号(1区)),111(123(3)122)";
var sname = DeleteTwo(name, ref sb);
Console.WriteLine(sname);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐