面试题3:二维数组中的查找

 

  剑指offer中面试题3,在二维数组中的查找。

  题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断,数组中是否含有该整数。

例如:

1   2 8 9
2 4 9 12
4 7 10 13
6 8 11 15

书中给出的算法是从右上角数字开始查找,并给出了C++代码。

我从左下角给出C#的实现,先不要急着看代码,先看我的解释。

矩阵查找
  1     public class Matrix : IFindMatrix
  2     {
  3         private static Matrix instance = new Matrix();
  4         public static Matrix Instance 
  5         {
  6             get 
  7             {
  8                 return instance;
  9             }
 10         }
 11         int[,] intMatrix;
 12         int rows, cols;
 13         bool noFind;
 14         public Matrix() { }
 15         public Matrix(int[,] matrix, int r, int c) 
 16         {
 17             if (matrix == null || r <= 0 || c <= 0)
 18             {
 19                 noFind = true;
 20             }
 21             else 
 22             {         
 23             intMatrix = matrix;
 24             rows = r;
 25             cols = c;
 26             noFind = false;
 27             }
 28         }
 29         public bool FindInMatrix(int number,ref int findCount) 
 30         {
 31             if (noFind)
 32             {
 33                 findCount = -1;
 34                 return false;
 35             }
 36             findCount = 0;
 37             int i = rows - 1;
 38             int j = 0;
 39             bool exist = false;
 40             do
 41             {
 42               
 43                 if (intMatrix[i, j] == number)
 44                 {
 45                     findCount++; 
 46                     exist = true;
 47                     break;
 48                 }
 49                 else if (intMatrix[i, j] > number)
 50                 {
 51                     findCount++; 
 52                     i--;
 53                 }
 54                 else
 55                 {
 56                     findCount++; 
 57                     j++;
 58                 }
 59             } while (i >= 0 && j < cols);
 60             return exist; 
 61         }
 62         public bool OptFindInMatrix(int number,ref int findCount) 
 63         {
 64 
 65             if (noFind)
 66             {
 67                 findCount = -1;
 68                 return false;
 69             }
 70             findCount = 0;
 71             int mini, minj, maxi, maxj;
 72             mini = 0;
 73             minj = 0;
 74             maxi = rows - 1;
 75             maxj = cols - 1;
 76             int i = rows - 1;
 77             int j = 0;
 78             bool exist = false;
 79             do
 80             {
 81                 
 82                 if ( intMatrix[maxi,maxj] < number)
 83                 {
 84                     findCount++;
 85                     break;
 86                 }
 87                 if (intMatrix[mini, minj] > number) 
 88                 {
 89                     findCount++;
 90                     break;
 91                 }
 92                 if (intMatrix[i,j] == number)
 93                 {
 94                     findCount++;
 95                     exist = true;
 96                     break;
 97                 }
 98                 else if (intMatrix[i,j] > number)
 99                 {
100                     findCount++;
101                     i--;
102                     maxi = i;
103                 }
104                 else
105                 {
106                     findCount++;
107                     j++;
108                     minj = j;
109                 }
110             } while (i >= 0 && j < cols);
111             return exist; 
112         }
113         public bool FindInMatrix(int number)
114         {
115             if (noFind)
116             {
117                 return false;
118             }
119             int i = rows - 1;
120             int j = 0;
121             bool exist = false;
122             do
123             {
124                 if (intMatrix[i, j] == number)
125                 {
126                     exist = true;
127                     break;
128                 }
129                 else if (intMatrix[i, j] > number)
130                 {
131                     i--;
132                 }
133                 else
134                 {
135                     j++;
136                 }
137             } while (i >= 0 && j < cols);
138             return exist; 
139         }
140         public bool OptFindInMatrix(int number)
141         {
142             if (noFind)
143             {
144                 return false;
145             }
146             int mini, minj, maxi, maxj;
147             mini = 0;
148             minj = 0;
149             maxi = rows - 1;
150             maxj = cols - 1;
151             int i = rows - 1;
152             int j = 0;
153             bool exist = false;
154             do
155             {
156                 if (intMatrix[mini, minj] > number || intMatrix[maxi, maxj] < number)
157                     break;
158                 if (intMatrix[i, j] == number)
159                 {
160                     exist = true;
161                     break;
162                 }
163                 else if (intMatrix[i, j] > number)
164                 {
165                     i--;
166                     maxi = i;
167                 }
168                 else
169                 {
170                     j++;
171                     minj = j;
172                 }
173             } while (i >= 0 && j < cols);
174             return exist; 
175         }
176         public void InitMatrix(int[,] matrix, int r, int c)
177         {
178             if (matrix == null || r <= 0 || c <= 0)
179             {
180                 noFind = true;
181             }
182             else
183             {
184                 intMatrix = matrix;
185                 rows = r;
186                 cols = c;
187                 noFind = false;
188             }
189         }
190         
191     }

  按照剑指offer实现了单例模式(只是为了练手,感觉没有什么作用),至于继承的接口是为了方便测试用的。
  从左上角或者右下角开始扫描的话可以一次减少一行或者一列的比较数量。但是从左上角和右下角呢?左上角是矩阵中的最小值,右下角是矩阵中的最大值,如果要寻找的number小于左上角或者大于右下角的话,就可以直接返回false了?

  在每次从左下角或者右上角扫描时,可以先判断number与左上角和右下角的比值,判断是否直接返回false。所以加了一个OptFindInMatrix(...)函数代表是优化的以前算法的意思,但是更想知道这种所谓的优化究竟效果如何,所以给OptFindInMatrix(....)增加了一个参数findCount,用来记录查找所增加的次数。

   写了一个测试类,一个100*100的矩阵,矩阵每个元素的赋值公式见下面伪代码。

矩阵元素的赋值
//伪代码,告诉矩阵值的计算方式            
int id = 0;
            for (int i = 0; i < 100; i++)
                for (int j = 0; j < 100; j++)
                {
                    id++;
                    a[i][j]=i*j+id;
                }

  测试类顺便练习使用的XML的存储,使用XML保存了测试数据和测试结构,xml的使用方法见代码 ,如果要运行代码,首先要调用一下InitTestData()函数生成测试数据,当然你还可以通过其他的公式生成测试数据。

  测试类里面有一个接口InFindMatrix,测试类构造函数里面把Matrix 的实例赋值给 InFindMatrix,测试函数只使用用InFindMatrix。

  测试类test()可以实现各种测试用例。下见代码。

  

测试类代码
  1     public class MatrixTest 
  2     {
  3         private static readonly int rows = 100;
  4         private static readonly int cols = 100;
  5         private static readonly int count = 10000;
  6         public IFindMatrix InFindMatrix;
  7         XmlDocument xmlTestDoc;
  8         XmlDocument XmlResultData;
  9         private void InitTestData()
 10         {
 11             xmlTestDoc = new XmlDocument();
 12             xmlTestDoc.AppendChild(xmlTestDoc.CreateXmlDeclaration("1.0", "utf-8", null));
 13             XmlElement TestDataSet = xmlTestDoc.CreateElement("TestDataSet");
 14             XmlAttribute attCount = xmlTestDoc.CreateAttribute("count");
 15             attCount.Value = count.ToString();
 16             TestDataSet.Attributes.Append(attCount);
 17             XmlAttribute attRows = xmlTestDoc.CreateAttribute("rows");
 18             attRows.Value = rows.ToString();
 19             TestDataSet.Attributes.Append(attRows);
 20             XmlAttribute attCols = xmlTestDoc.CreateAttribute("cols");
 21             attCols.Value = cols.ToString();
 22             TestDataSet.Attributes.Append(attCols);
 23             xmlTestDoc.AppendChild(TestDataSet);
 24 

 25             int id = 0;
 26             for (int i = 0; i < rows; i++)
 27                 for (int j = 0; j < cols; j++)
 28                 {
 29                     id++;
 30                     //testData
 31                     XmlElement eleTestItem = xmlTestDoc.CreateElement("TestData");
 32                     XmlAttribute attId = xmlTestDoc.CreateAttribute("id");
 33                     attId.Value = id.ToString();
 34                     XmlAttribute attI = xmlTestDoc.CreateAttribute("i");
 35                     attI.Value = i.ToString();
 36                     XmlAttribute attJ = xmlTestDoc.CreateAttribute("j");
 37                     attJ.Value = j.ToString();
 38                     XmlAttribute attValue = xmlTestDoc.CreateAttribute("value");
 39                     attValue.Value = (i * j + id).ToString();
 40 
 41                     eleTestItem.Attributes.Append(attId);
 42                     eleTestItem.Attributes.Append(attI);
 43                     eleTestItem.Attributes.Append(attJ);
 44                     eleTestItem.Attributes.Append(attValue);
 45                     TestDataSet.AppendChild(eleTestItem);
 46                 }
 47             xmlTestDoc.Save("testDataSet.xml");
 48 
 49         }
 50         public  int[,] LoadData(ref int rows, ref int cols)
 51         {
 52             int[,] matrix;
 53             rows = cols = -1;
 54              xmlTestDoc = new XmlDocument();
 55             xmlTestDoc.Load("testDataSet.xml");
 56             XmlNode xmln = xmlTestDoc.SelectSingleNode("/TestDataSet");
 57             string str = (xmln as XmlElement).GetAttribute("rows").ToString();
 58             if (!int.TryParse(str, out rows))
 59                 return null;
 60             str = (xmln as XmlElement).GetAttribute("cols").ToString();
 61             if (!int.TryParse(str, out cols))
 62                 return null;
 63             if (rows <= 0 || cols <= 0)
 64             {
 65                 return null;
 66             }
 67             matrix = new int[rows, cols];
 68             XmlNodeList xnl = xmlTestDoc.SelectNodes("/TestDataSet/TestData");
 69             XmlElement ele;
 70             int i, j, value;
 71             foreach (XmlNode xn in xnl)
 72             {
 73                 ele = (xn as XmlElement);
 74                 str = ele.GetAttribute("i");
 75                 int.TryParse(str, out i);
 76                 str = ele.GetAttribute("j");
 77                 int.TryParse(str, out j);
 78                 str = ele.GetAttribute("value");
 79                 int.TryParse(str, out value);
 80                 matrix[i, j] = value;
 81             }
 82             return matrix;
 83         }
 84         public MatrixTest() 
 85         {
 86             InFindMatrix = Matrix.Instance;
 87             int rows, cols;
 88             rows = cols = -1;
 89             int[,] matrix = LoadData(ref rows, ref cols);
 90             InFindMatrix.InitMatrix(matrix, rows, cols);
 91         }
 92         public void test() 
 93         {
 94             XmlResultData = new XmlDocument();
 95             XmlResultData.AppendChild(XmlResultData.CreateXmlDeclaration("1.0", "utf-8", null));
 96             XmlElement ResultDataSet = XmlResultData.CreateElement("ResultDataSet");
 97             XmlResultData.AppendChild(ResultDataSet);
 98 
 99             int falseCount = 0;
100             double betterCount = 0;
101             int whileNum = 0;
102             while (whileNum < 20000)
103             {
104                 XmlElement ele = XmlResultData.CreateElement("testResult");
105                 XmlElement eleFind = XmlResultData.CreateElement("FindValue");
106                 eleFind.InnerText = whileNum.ToString();
107                 ele.AppendChild(eleFind);
108                 XmlElement eleExpect;
109                 XmlElement eleActual;
110                 XmlElement eleFindCount;
111                 XmlElement eleOptFindCount;
112                 bool bExpect = true;
113                 bool bActual = false;
114                 int FindCount, OptFindCount;
115                 FindCount = OptFindCount = -1;
116                 bExpect = InFindMatrix.FindInMatrix(whileNum, ref FindCount);
117                 bActual = InFindMatrix.OptFindInMatrix(whileNum, ref OptFindCount);
118                 if (bExpect != bActual)
119                 {
120                     Console.WriteLine("mFind算法出现错误");
121                     Console.WriteLine("OptFindCount算法出现错误");
122                     Console.ReadLine();
123                 }
124                 if (bActual)
125                 {
126                    betterCount = betterCount - FindCount*2;
127                 }
128                 else 
129                 {
130                     falseCount++;
131                     betterCount = betterCount + (FindCount - OptFindCount) * 2.5 - OptFindCount * 1.5;
132                 }
133                 eleExpect = XmlResultData.CreateElement("Expect");
134                 eleExpect.InnerText = bExpect.ToString();
135                 ele.AppendChild(eleExpect);
136                 eleActual = XmlResultData.CreateElement("Actual");
137                 eleActual.InnerText = bActual.ToString();
138                 ele.AppendChild(eleActual);
139                 eleFindCount = XmlResultData.CreateElement("FindCount");
140                 eleFindCount.InnerText = FindCount.ToString();
141                 ele.AppendChild(eleFindCount);
142                 eleOptFindCount = XmlResultData.CreateElement("OptFindCount");
143                 eleOptFindCount.InnerText = OptFindCount.ToString();
144                 ele.AppendChild(eleOptFindCount);
145                 ResultDataSet.AppendChild(ele);
146                 XmlResultData.AppendChild(ResultDataSet);
147                 whileNum++;
148             }
149             XmlResultData.Save("testResult.xml");
150             Console.WriteLine(betterCount);
151             }
152         
153     }

  100*100矩阵,矩阵中元素值的跨度1~10000+99*99,测试数据是从0~20000,把OptFindInMatrix和FindInMatrix每次的寻找次数求和查看结果,根据测试结果实际上所谓的优化OptFindInMatrix(...)函数的比较次数总数是要远多于FindInMatrix(...),平均寻找每个数OptFindInMatrix要比FindInMatrix多193次比较

  测试数据0~40000时,OptFindInMatrix(...)函数的比较次数总数小于FindInMatrix(...)平均寻找每个数OptFindInMatrix要比FindInMatrix少26次比较.

  因为如果要找的数据再矩阵中时,OptFindInMatrix要比FindInMatrix多两次比较的次数,因此具体的OptFindInMatrix是否可以减少比较次数要和矩阵的数据和查找数结合起来,就测试数据来讲,矩阵中元素值的跨度1~10000+99*99,测试数据是从0~40000时,OptFindInMatrix和FindInMatrix的比较次数大概相等,要查找的数据范围是矩阵数据范围的两倍时,可能OptFindInMatrix更优秀一点。(实际情况可能更复杂一点)

 

 

posted on 2013-03-21 22:27  菜包子  阅读(433)  评论(5编辑  收藏  举报

导航