关于查找
查找又称检索,是从大量的数据中找出所需的数据,也可以简单地说是确定一个已给定的数据是否出现在某个数据表中。表通常是指文件,而文件是记录的集合,每个记录都是由若干个数据项组成的,其间一个或一组能唯一识别该记录的数据项称为该记录的关键字(keyword)。查找包括很多种:顺序查找、折半查找、分块查找以及哈希查找法。
顺序查找
核心思想:把序列中的元素进行遍历,找到符合条件的结果就结束
比较次数:最快1次,最慢n次
何时使用:
- 针对于无序序列,如果要实现查找序列中的所有满足条件的元素,那么比较次数一定是n次,因为找到其中一个,并不一定意味着找到所有的,因而要把所有的元素都遍历一遍才行。
- 如果是有序序列,要查找序列中所有满足条件的元素,比较次数要少一些,只需从开始满足的地方,到第一个不满足的地方就可以结束了,不必要等到序列末尾。
1 int Find(int[] ary, int target) 2 { 3 for (int i = 0; i < ary.Length; i++) 4 { 5 if (ary[i] == target) 6 { 7 //找到了就返回找到的位置 8 return i; 9 } 10 } 11 //没找到就返回-1,表示没找到 12 return -1; 13 }
折半查找
核心思想:先确定待查找记录所在的的区间,然后逐步缩小查找范围,直到找到或找不到记录为止
时间复杂度:折半查找算法的效率是O(logn)
何时使用:
- 当表的长度n很大时,该方法很能显示出其时间效率
- 适合表中元素很少发生改动
1 int binary(int v[],int n,int cha) 2 { 3 int low,high,mid; 4 low=0; 5 high=n-1; 6 7 while(low<=high) 8 { 9 mid=(low+high)/2; //折半 10 if(cha<v[mid]) 11 { 12 high=mid-1; //前半部分查找 13 } 14 else if(cha>v[mid]) 15 { 16 low=mid+1; //后半部分查找 17 } 18 else 19 return mid; 20 } 21 return 0; //没找到 22 }
PS:折半查找查找的表还是线性表
分块查找
核心思想:分块查找是顺序查找和折半查找的结合体
时间复杂度:介于顺序与折半
何时使用:
- 要求表中元素是逐段有序的,块与块之间可以无序
1 int SeqSearch(SeqList<int> R, int Key) 2 { 3 int i; 4 R.Data[R.GetLength()] = Key; 5 for (i = 0; R.Data[i] != Key; i++) ; 6 if (i > R.Maxsize - 2) 7 return -2; 8 else 9 return i; 10 }
哈希法(散列法)
简介一下:哈希法不同于前面三种查找方法(这三种都是通过对关键字进行一系列的比较,逐步缩小范围,知道确定节点的存储位置或确定查找失败),哈希法不经过任何比较,一次存取就能得到所查元素,因此必须在记录的存储位置和它的关键字之间建立一个确定的对应关系,使得每个关键字和结构中一个唯一的存储位置相对应,这样查找时只需对节点进行某种运算就能确定节点在表中的位置
核心思想:找到哈希函数(比较次数与所含节点的个数无关)
1 Search 2 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 8 namespace DataStructure 9 { 10 public interface IListDS<T> 11 { 12 int GetLength(); //求长度 13 void Clear(); //清空操作 14 bool IsEmpty(); //判断线性表是否为空 15 bool IsFull(); //判断数据表是否已满 16 void Append(T item); //附加操作 17 void Insert(T item, int i); //插入操作 18 T Delete(int i); //删除操作 19 T GetElement(int i); //取表元 20 int Locate(T value); //按值查找 21 void Reserve(); //倒置 22 } 23 24 public class SeqList<T> : IListDS<T> 25 { 26 private int maxsize; //顺序表的容量 27 private T[] data; //数组,用于存储数据表中的数据元素 28 private int last; //指示数据表中最后一个元素的位置 29 30 //索引器 31 public T this[int index] 32 { 33 get { return data[index]; } 34 set { data[index] = value; } 35 } 36 37 public T[] Data 38 { 39 get { return data; } 40 set { data = value; } 41 } 42 43 //属性 最后一个元素的位置 44 public int Last 45 { 46 get { return last; } 47 } 48 49 //属性 容量 50 public int Maxsize 51 { 52 get { return maxsize; } 53 set { maxsize = value; } 54 } 55 56 //构造函数 57 public SeqList(int size) 58 { 59 data = new T[size]; 60 maxsize = size; 61 last = -1; 62 } 63 64 public int GetLength() 65 { 66 return last + 1; 67 } 68 69 public void Clear() 70 { 71 last = -1; 72 } 73 74 public bool IsEmpty() 75 { 76 if (last == -1) 77 return true; 78 else 79 return false; 80 } 81 82 public bool IsFull() 83 { 84 if (last == maxsize - 1) 85 return true; 86 else 87 return false; 88 } 89 90 public void Append(T item) 91 { 92 if (IsFull()) 93 { 94 Console.WriteLine("List is full !"); 95 return; 96 } 97 data[++last] = item; 98 } 99 100 public void Insert(T item, int i) 101 { 102 if (IsFull()) 103 { 104 Console.WriteLine("List is Full !"); 105 return; 106 } 107 if (i < 1 || i > last + 2) 108 { 109 Console.WriteLine("Postion is Error !"); 110 return; 111 } 112 if (i == last + 2) 113 { 114 data[i - 1] = item; 115 } 116 else 117 { 118 for (int j = last; j >= i - 1; j--) 119 { 120 data[j + 1] = data[j]; 121 } 122 data[i - 1] = item; 123 } 124 ++last; 125 } 126 127 public T Delete(int i) 128 { 129 T tem = default(T); 130 if (IsEmpty()) 131 { 132 Console.WriteLine("List is Empty !"); 133 return tem; 134 } 135 if (i < 1 || i > last + 1) 136 { 137 Console.WriteLine("Postion is Error !"); 138 return tem; 139 } 140 if (i == last + 1) 141 { 142 tem = data[last--]; 143 return tem; 144 } 145 else 146 { 147 tem = data[i - 1]; 148 for (int j = i; j <= last; j++) 149 { 150 data[j] = data[j + 1]; 151 } 152 } 153 --last; 154 return tem; 155 } 156 157 public T GetElement(int i) 158 { 159 if (IsEmpty() || i < 1 || i > last + 1) 160 { 161 Console.WriteLine("List is Empty or Postion is Error !"); 162 return default(T); 163 } 164 return data[i - 1]; 165 } 166 167 public int Locate(T value) 168 { 169 if (IsEmpty()) 170 { 171 Console.WriteLine("List is Empty !"); 172 return -1; 173 } 174 int i = 0; 175 for (i = 0; i <= last; i++) 176 { 177 if (value.Equals(data[i])) 178 break; 179 } 180 if (i > last) 181 { 182 return -1; 183 } 184 return i; 185 } 186 187 public void Reserve() 188 { 189 T tem; 190 int len = GetLength(); 191 for (int i = 0; i <= len / 2; i++) 192 { 193 tem = data[i]; 194 data[i] = data[len - 1]; 195 data[len - 1] = tem; 196 } 197 } 198 199 /// <summary> 200 /// 将两个升序整型顺序表合并成一个升序顺序表 201 /// 思路:依次扫描La Lb中的元素,比较La和Lb中当前元素,将较小的元素值赋值给Lc,如此直到一个被扫描完,然后把未完的那个剩余的元素付给Lc 202 /// </summary> 203 /// <param name="La"></param> 204 /// <param name="Lb"></param> 205 /// <returns></returns> 206 public SeqList<int> Merge(SeqList<int> La, SeqList<int> Lb) 207 { 208 SeqList<int> Lc = new SeqList<int>(La.Maxsize + Lb.Maxsize); 209 int i = 0; 210 int j = 0; 211 212 while ((i <= La.GetLength() - 1) && (j <= Lb.GetLength() - 1)) 213 { 214 if (La[i] < Lb[j]) 215 { 216 Lc.Append(La[i++]); 217 } 218 else 219 { 220 Lc.Append(Lb[j++]); 221 } 222 } 223 //如果La中还有元素 224 while (i <= La.GetLength() - 1) 225 { 226 Lc.Append(La[i++]); 227 } 228 //如果Lb中还有元素 229 while (i < Lb.GetLength() - 1) 230 { 231 Lc.Append(Lb[i++]); 232 } 233 234 return Lc; 235 } 236 237 /// <summary> 238 /// 已知顺序表La 构造顺序表Lb 使其包含La中所有不相同的数据元素 239 /// 思路: 先把La中的第一个元素付给Lb 然后从La的第二个元素开始 每一个元素与Lb的每一个元素比较 不同则附加到Lb末尾 240 /// </summary> 241 /// <param name="La"></param> 242 /// <returns></returns> 243 public SeqList<int> Purge(SeqList<int> La) 244 { 245 SeqList<int> Lb = new SeqList<int>(La.Maxsize); 246 Lb.Append(La[0]); 247 for (int i = 1; i <= La.GetLength() - 1; ++i) 248 { 249 int j = 0; 250 for (j = 0; j <= Lb.GetLength() - 1; ++j) 251 { 252 if (La[i].CompareTo(Lb[j]) == 0) 253 { 254 break; 255 } 256 } 257 //没有相同的元素,把La中的元素附加到Lb 258 if (j > Lb.GetLength() - 1) 259 { 260 Lb.Append(La[i]); 261 } 262 } 263 return Lb; 264 } 265 266 267 268 269 } 270 271 /// <summary> 272 /// 定义解决冲突的链表的结点类型 273 /// </summary> 274 public class chaintype 275 { 276 private int key; 277 private chaintype next; 278 279 public int Key 280 { 281 get { return key; } 282 set { key = value; } 283 } 284 285 public chaintype Next 286 { 287 get { return next; } 288 set { next = value; } 289 } 290 } 291 292 293 public class SearchArithMetic 294 { 295 296 /// <summary> 297 /// 插入排序 298 /// </summary> 299 /// <param name="R"></param> 300 public void InsertSort(SeqList<int> R) 301 { 302 for (int i = 1; i < R.Maxsize; i++) 303 { 304 if (R.Data[i] < R.Data[i - 1]) 305 { 306 int temp = R.Data[i]; 307 int j = 0; 308 for (j = i - 1; j >= 0 && temp < R.Data[j]; j--) 309 { 310 R.Data[j + 1] = R.Data[j]; 311 } 312 R.Data[j + 1] = temp; 313 } 314 } 315 } 316 317 /// <summary> 318 /// 顺序查找算法 319 /// </summary> 320 /// <param name="R"></param> 321 /// <param name="Key"></param> 322 /// <returns></returns> 323 public int SeqSearch(SeqList<int> R, int Key) 324 { 325 int i; 326 R.Data[R.GetLength()] = Key; 327 for (i = 0; R.Data[i] != Key; i++) ; 328 if (i > R.Maxsize - 2) 329 return -2; 330 else 331 return i; 332 } 333 334 /// <summary> 335 /// 二分查找算法 336 /// </summary> 337 /// <param name="R"></param> 338 /// <param name="Key"></param> 339 /// <returns></returns> 340 public int BinSearch(SeqList<int> R, int Key) 341 { 342 int low = 0, high = R.GetLength() - 1, mid; //设置当前查找区间的上下界的初值 343 while (low <= high) 344 { 345 mid = (low + high) / 2; 346 if (R.Data[mid] == Key) return mid; 347 if (R.Data[mid] > Key) 348 high = mid - 1; 349 else 350 low = mid + 1; 351 } 352 return -1; 353 } 354 355 /// <summary> 356 /// 除模取余法的哈希函数 357 /// </summary> 358 /// <param name="key"></param> 359 /// <param name="Mod"></param> 360 /// <returns></returns> 361 public int Hash(int key, int Mod) 362 { 363 return key % Mod; 364 } 365 366 /// <summary> 367 /// 在哈希表中插入记录,用链表法解决冲突 368 /// </summary> 369 /// <param name="a"></param> 370 /// <param name="key"></param> 371 /// <param name="Mod"></param> 372 /// <returns></returns> 373 public bool HashInsert(chaintype[] a, int key, int Mod) 374 { 375 int i; 376 i = Hash(key, Mod); 377 chaintype pre; 378 chaintype cur; 379 pre = a[i]; 380 cur = a[i]; 381 382 while (cur != null && cur.Key != key) 383 { 384 pre = cur; 385 cur = cur.Next; 386 } 387 388 /*未查找到时插入该记录在对应的链表尾*/ 389 if (cur == null) 390 { 391 cur = new chaintype(); 392 cur.Key = key; 393 cur.Next = null; 394 /*在该链插入第一个记录*/ 395 if (a[i] == null) 396 a[i] = cur; 397 else 398 pre.Next = cur; 399 return true; 400 } 401 return false; 402 } 403 404 405 public chaintype HashSearch(chaintype[] a, int key, int Mod) 406 { 407 chaintype p; 408 int i = Hash(key, Mod); 409 p = a[i]; 410 while (p != null && p.Key != key) 411 { p = p.Next; } 412 if (p == null) return null; 413 else 414 return p; 415 } 416 } 417 class Search 418 { 419 /* 420 /// <summary> 421 /// 顺序查找 422 /// </summary> 423 /// <param name="R"></param> 424 /// <param name="Key"></param> 425 /// <returns></returns> 426 public static int SeqSearch(SeqList<int> R, int Key) 427 { 428 int i; 429 R.Data[R.GetLength()] = Key; 430 for (i = 0; R.Data[i] != Key; i++) ; 431 if (i > R.Maxsize - 2) 432 return -2; 433 else 434 return i; 435 } 436 437 /// <summary> 438 /// 二分查找 439 /// </summary> 440 /// <param name="R"></param> 441 /// <param name="Key"></param> 442 /// <returns></returns> 443 public static int BinSearch(SeqList<int> R, int Key) 444 { 445 int low = 0, high = R.GetLength() - 1, mid; //设置当前查找区间的上下界的初值 446 while (low <= high) 447 { 448 mid = (low + high) / 2; 449 if (R.Data[mid] == Key) return mid; 450 if (R.Data[mid] > Key) 451 high = mid - 1; 452 else 453 low = mid + 1; 454 } 455 return -2; 456 } 457 458 459 static void Main() 460 { 461 SeqList<int> queue = new SeqList<int>(10); 462 queue.Append(10); 463 queue.Append(20); 464 queue.Append(30); 465 queue.Append(40); 466 queue.Append(50); 467 queue.Append(60); 468 queue.Append(70); 469 queue.Append(80); 470 queue.Append(90); 471 472 //int location = SeqSearch(queue, 90); 473 int location = BinSearch(queue, 90); 474 Console.WriteLine("您查找的数据为表中第{0}位!", location + 1); 475 Console.ReadKey(); 476 477 } 478 */ 479 480 public static void Main() 481 { 482 SeqList<int> numList = null; 483 484 numList = new SeqList<int>(12); 485 chaintype[] a = new chaintype[13]; 486 487 char selectFlag; 488 while (true) 489 { 490 Console.WriteLine("请输入操作选项:"); 491 Console.WriteLine("1.创建顺序表"); 492 Console.WriteLine("2.对顺序表执行顺序查找"); 493 Console.WriteLine("3.对顺序表执行二分查找"); 494 Console.WriteLine("4.创建哈希表"); 495 Console.WriteLine("5.在哈希表中查找关键字"); 496 Console.WriteLine("6.退出"); 497 selectFlag = Convert.ToChar(Console.ReadLine()); 498 SearchArithMetic searchA = new SearchArithMetic(); 499 switch (selectFlag) 500 { 501 case '1': 502 { 503 numList.Insert(70, numList.GetLength() + 1); 504 numList.Insert(30, numList.GetLength() + 1); 505 numList.Insert(40, numList.GetLength() + 1); 506 numList.Insert(10, numList.GetLength() + 1); 507 numList.Insert(80, numList.GetLength() + 1); 508 numList.Insert(20, numList.GetLength() + 1); 509 numList.Insert(90, numList.GetLength() + 1); 510 numList.Insert(100, numList.GetLength() + 1); 511 numList.Insert(75, numList.GetLength() + 1); 512 numList.Insert(60, numList.GetLength() + 1); 513 numList.Insert(45, numList.GetLength() + 1); 514 515 Console.WriteLine("已插入顺序表的数字是:"); 516 Console.WriteLine("70 30 40 10 80 20 90 100 75 60 45"); 517 break; 518 } 519 case '2': 520 { 521 Console.WriteLine("请输入要查找的数字:"); 522 long time1 = System.DateTime.Now.Ticks; 523 int num = Convert.ToInt32(Console.ReadLine()); 524 int i = searchA.SeqSearch(numList, num); 525 if (i == -1) 526 Console.WriteLine("{0}在数字列表中不存在", num); 527 else 528 Console.WriteLine("{0}在数字列表中的序号为{1}", num, i); 529 long time2 = System.DateTime.Now.Ticks; 530 Console.WriteLine("顺序查找用时{0}", time2 - time1); 531 break; 532 } 533 case '3': 534 { 535 long time1 = System.DateTime.Now.Ticks; 536 new SearchArithMetic().InsertSort(numList); 537 long time2 = System.DateTime.Now.Ticks; 538 Console.WriteLine("请输入要查找的数字:"); 539 int num = Convert.ToInt32(Console.ReadLine()); 540 long time3 = System.DateTime.Now.Ticks; 541 int i = searchA.BinSearch(numList, num); 542 if (i == -1) 543 Console.WriteLine("{0}在数据列表中不存在", num); 544 else 545 Console.WriteLine("{0}在数字列表中的序号为{1}", num, i + 1); 546 long time4 = System.DateTime.Now.Ticks; 547 Console.WriteLine("排序用时{0}", time2 - time1); 548 Console.WriteLine("二分查找用时{0}", time4 - time3); 549 Console.WriteLine("查找总用时{0}", time4 - time1); 550 break; 551 } 552 case '4': 553 { 554 searchA.HashInsert(a, 70, 13); 555 searchA.HashInsert(a, 30, 13); 556 searchA.HashInsert(a, 40, 13); 557 searchA.HashInsert(a, 10, 13); 558 searchA.HashInsert(a, 80, 13); 559 searchA.HashInsert(a, 20, 13); 560 searchA.HashInsert(a, 90, 13); 561 searchA.HashInsert(a, 100, 13); 562 searchA.HashInsert(a, 75, 13); 563 searchA.HashInsert(a, 60, 13); 564 searchA.HashInsert(a, 45, 13); 565 break; 566 } 567 case '5': 568 { 569 Console.WriteLine("请输入要查找的数字:"); 570 long time1 = System.DateTime.Now.Ticks; 571 int num = Convert.ToInt32(Console.ReadLine()); 572 chaintype p = searchA.HashSearch(a, num, 13); 573 if(p==null) 574 Console.WriteLine("{0}在数据列表中不存在", num); 575 else 576 Console.WriteLine("您查找的关键字是:{0}",p.Key ); 577 long time2 = System.DateTime.Now.Ticks; 578 Console.WriteLine("哈希表查找用时{0}", time2 - time1); 579 break; 580 } 581 case '6': 582 { 583 return; 584 } 585 586 587 } 588 Console.WriteLine("按任意键继续"); 589 Console.ReadLine(); 590 } 591 } 592 593 } 594 }
结语
适当的情景使用适当的算法,慢慢在实际中摸索,IT小菜鸟成长ing