經常使用查找算法及實現

1、順序查找

    線性查找是在一個已知無(或有序)序隊列中找出與給定關鍵字相同的數的具體位置。原理是ios

讓關鍵字與隊列中的數從最後一個開始逐個比較,直到找出與給定關鍵字相同的數爲止,它的缺算法

點是效率低下。數組

   一、算法原理

   1)從表中的最後一個記錄開始,逐個進行記錄的關鍵字與給定值進行比較,若某個記錄的關函數

鍵字與給定值相等,則查找成功,找到所查的記錄;性能

  2)反之,若直到第一個記錄,其關鍵字和給定值比較都不相等,則代表表中沒有所查的記spa

錄,查找失敗。   設計

  二、算法分析

    順序查找的平均查找長度(Average Search Length,ASL)爲(n+1)/2,當查找不成功時,需code

要n+1次比較,時間複雜度爲O(n);排序

 三、算法實現索引

/*
  順序查找。
*/

#include <iostream>
#include <stdio.h>

int SequenceSearch( int *array, int n, int key )
{
  if( array == NULL || n < 0 )
  {
    printf( "invalid input.\n" );
    return -1;
  }
  int i ;
  array[ 0 ] = key;
  
  for( i = n; array[ i ] != array[ 0 ]; i-- )
  {
    if( array[ i ] == key )
      break;
  }
  if( i == 0 )
    return 0;
  else
    return i;
}

void Test( const char* testName, int* array, int n, int key )
{
  if( testName == NULL ) 
  {
    printf( "test invaild input.\n" );
    return;
  }

  printf( "%s begins: \n", testName );

  if( array == NULL )
  {
    printf( "test invaild input.\n" );
    return;
  }

  int found = SequenceSearch( array, n, key );

  if( found != 0 )
    printf( "found the key: %d in index %d\n", key, found );
  else
    printf( "not found the key: %d\n", key );

  printf( "\n" );
}

// 要找的數字存在
void Test1()
{
  int array[ ] = { 0, 42, 24, 61, 71, 11, 57 };
  Test( "Test1", array, 6, 71 ); 
}

// 要找的數字不存在
void Test2()
{
  int array[ ] = { 0, 421, 24, 6,421, 121, 54 };
  Test( "Test2", array, 6, 88 ); 
}


// 輸入數組爲空
void Test3()
{
  int emptyArray[ ] = { };
  Test( "Test3", emptyArray, 0, 0 ); 
}

// 輸入數組爲null,且長度異常
void Test4()
{
  Test( "Test4", NULL, -1, -999 ); 
}

int main()
{
  Test1();
  Test2();
  Test3();
  Test4();

  return 0;
}

2、二分查找

    二分查找又稱折半查找,優勢是比較次數少,查找次數快,平均性能好;其缺點是要求待查表

爲有序表,且插入刪除困難。所以,折半查找方法使用於不常常變更而查找頻繁的有序列表。

首先,假設表中元素是升序排列的,將表中間位置記錄的關鍵字與查找關鍵字比較,若是二者

相等,則查找成功;不然利用中間位置記錄將表分紅前、後兩個子表,若是中間位置記錄的關鍵

字大於查找關鍵字,則進一步查找前一子表,不然進一步查找後一子表。重複以上過程,直到

找到知足條件的記錄,使查找成功,或直到子表不存在爲止,此時查找不成功。

   一、算法原理

   要求:1)必須採用順序儲存結構。2)必須按關鍵字大小有序排列。

   基本思想:

   1)將n個元素分紅大體相等的兩部分,取a[n/2]與x作比較,若是x=a[n/2],則找到x,算法停止;

   2)若是x<a[n/2],則只要在數組a的左半部分繼續搜索x,若是x>a[n/2],則只要在數組a的右半部搜

索x.

  二、算法分析 

    總共有n個元素,漸漸跟下去就是n,n/2,n/4,....n/2^k(接下來操做元素的剩餘個數),其中k就

是循環的次數因爲你n/2^k取整後>=1即令n/2^k=1可得k=log2n,(是以2爲底,n的對數)因此時

間複雜度能夠表示O()=O(logn)。

  三、算法實現   

/*
  二分查找。
*/

#include <iostream>
#include <stdio.h>

int BinarySearch( int *array, int start, int end, int key )
{
  int left, mid, right;
  
  bool found = false; // 判斷是否找到

  left = start;
  right = end;
  
  while( left <= right )
  {
    mid = ( left + right ) / 2;

    if( key == array[ mid ] )
    {
      found = true;
      break;
    }
    else if( key < array[ mid ] )
      right = mid - 1;
    else if( key > array[ mid ] )
      left = mid + 1;
  }
  
  if( found )
    return mid;
  else
    return -1;
}

void Test( const char* testName, int* array, int start, int end, int key )
{
  if( testName == NULL ) 
  {
    printf( "test invaild input.\n" );
    return;
  }

  printf( "%s begins: \n", testName );

  if( array == NULL )
  {
    printf( "test invaild input.\n" );
    return;
  }

  int found = BinarySearch( array, start, end, key );

  if( found >= 0  )
    printf( "found the key: %d in index %d\n", key, found );
  else
    printf( "not found the key: %d\n", key );

  printf( "\n" );
}

// 要找的數字存在
void Test1()
{
  int array[ ] = { 11, 24, 42, 56, 71, 96 };
  Test( "Test1", array, 0, 5, 71 ); 
}

// 要找的數字不存在
void Test2()
{
  int array[ ] = { 21, 24, 46, 56, 72, 82 };
  Test( "Test2", array, 0, 5, 88 ); 
}


// 輸入數組爲空
void Test3()
{
  int emptyArray[ ] = { };
  Test( "Test3", emptyArray, 0, 0, 0 ); 
}

// 輸入數組爲null,且長度異常
void Test4()
{
  Test( "Test4", NULL, -1, -1, -999 ); 
}

int main()
{
  Test1();
  Test2();
  Test3();
  Test4();

  return 0;
}

3、分塊查找

     分塊查找是折半查找和順序查找的一種改進方法,分塊查找因爲只要求索引表是

序的,對快內結點沒有排序要求,所以特別適合結點動態變化的狀況。當增長或減

少節以及節點的關鍵碼改變時,只需將該節點調整到所在的塊便可。在空間複雜性上,分塊查

的主要代價是增長了一個輔助數組。須要注意的是,當節點變化很頻繁時,可能會致使塊與

之間的節點數相差很大,沒寫快具備不少節點,而另外一些塊則可能只有不多節點,這將會導

查找效率的降低。

      分塊查找要求把一個大的線性表分解成若干塊,每塊中的節點能夠任意存放,但塊與塊之間

必須排序。假設是按關鍵碼值非遞減的,那麼這種塊與塊之間必須知足已 排序要求,實際上就

是對於任意的i,第i塊中的全部節點的關鍵碼值都必須小於第i+1塊中的全部節點的關鍵碼值。

此外,還要創建一個索引表,把每塊中的最 大關鍵碼值做爲索引表的關鍵碼值,按塊的順序存

放到一個輔助數組中,顯然這個輔助數組是按關鍵碼值費遞減排序的。查找時,首先在索引表

中進行查找,肯定要 找的節點所在的塊。因爲索引表是排序的,所以,對索引表的查找能夠採

用順序查找或折半查找;而後,在相應的塊中採用順序查找,便可找到對應的節點。

   一、算法原理

   1)先選取各塊中的最大關鍵字構成一個索引表;

   2)查找分兩個部分:先對索引表進行二分查找或順序查找,以肯定待查記錄在哪一塊中,然

後在以肯定的快中用順序法進行查找。

  二、算法分析 

    分塊查找的平均查找長度由兩部分組成,一個是對索引表進行查找的平均查找長度,一個是

對快內節點進行查找的平均查找長度,總的平均查找長度爲 E(n)=+。線性表中共有n個節點,

分紅大小相等的b塊,每塊有s=n/b個節點。假定讀索引表也採用順序查找,只考慮查找成功的

狀況,並假定對每一個節 點的查找機率是相等的,則對每塊的查找機率是1/b,對快內每一個節點的

查找機率是1/s。  查找速度介於順序查找O(n)和折半查找O(logn)之間。

  三、算法實現

/*
  分塊查找。
*/

#include <iostream>
#include <stdio.h>

struct indexBlock  // 定義塊的結構
{
  int key;
  int start;
  int end;
};  // 定義結構體數組

int BlockSearch( int *array, int length, int gap, int key )
{
  if( array == NULL || length <= 0 || gap <= 0 )
  {
    printf( "invalid input.\n" );
    return -1;
  }

  int i = 0;
  int j;
  int begin = -1;
  int k;

  int nBlock = length / gap; 
  
  if( nBlock * gap < length )
    nBlock++;  

  indexBlock block[ nBlock ];  // 數組分爲nBlock塊

  for( k = 0; k < nBlock; k++ )
  {
    block[ k ].start = begin + 1;  // 肯定每一個塊範圍的起始值
    begin++;
  
    block[ k ].end = begin + length / nBlock - 1;  // 肯定每一個塊範圍的結束值
    begin += ( length / nBlock - 1 );
    
    if( k < nBlock - 1 )
      block[ k ].key = array[ begin ];  // 肯定每一個塊範圍中元素的最大值
    else
      block[ k ].key = array[ length - 1 ]; // 肯定最後一塊中元素的最大值
  }

  while( i < nBlock && key > block[ i ].key )  // 肯定在哪一個塊中
  {
    i++;
  }

  if( i >= nBlock )   // 大於分的塊數,則返回-1,找不到該數
    return -1;
 
  j = block[ i ].start;  // j等於塊範圍的起始值
  
  while( j <= block[ i ].end && array[ j ] != key )  // 在肯定的塊內進行查找
  {
    j++;
  }

  if( j > block[ i ].end )  // 若是大於塊範圍的結束值,則說明沒有要查找的數,j置爲-1
  {
    j = -1; 
  }

  return j;
}

void Test( const char* testName, int* array, int length, int gap, int key )
{
  if( testName == NULL ) 
  {
    printf( "test invaild input.\n" );
    return;
  }

  printf( "%s begins: \n", testName );

  int found = BlockSearch( array, length, gap, key );

  if( found >= 0 )
    printf( "found the key: %d in index %d\n", key, found );
  else
    printf( "not found the key: %d\n", key );

  printf( "\n" );
}

// 要找的數字存在,12個元素分爲3組,每組恰好4個元素
void Test1()
{
  int array[ ] = { 5, 3, 6, 7, 9, 8, 11, 15, 18, 20, 40, 50 };
  Test( "Test1", array, 12, 4, 8 ); 
}

// 要找的數字不存在,12個元素分爲3組,每組恰好4個元素
void Test2()
{
  int array[ ] = { 5, 3, 6, 7, 9, 8, 11, 15, 18, 20, 40, 50 };
  Test( "Test2", array, 12, 4, 99 ); 
}

// 要找的數字存在,10個元素分爲3組,前兩組4個元素,最後一組2個元素
void Test3()
{
  int emptyArray[ ] = { 5, 3, 6, 7, 9, 8, 11, 15, 18, 20 };
  Test( "Test3", emptyArray, 10, 4, 11 ); 
}

// 要找的數字不存在,10個元素分爲3組,前兩組4個元素,最後一組2個元素
void Test4()
{
  int emptyArray[ ] = { 5, 3, 6, 7, 9, 8, 11, 15, 18, 20 };
  Test( "Test4", emptyArray, 10, 4, 88 ); 
}

// 輸入數組爲空
void Test5()
{
  int emptyArray[ ] = { };
  Test( "Test5", emptyArray, 0, 0, -1 ); 
}

// 輸入數組爲null,且長度異常
void Test6()
{
  Test( "Test6", NULL, -1, 0, -1 ); 
}

int main()
{
  Test1();
  Test2();
  Test3();
  Test4();
  Test5();
  Test6();

  return 0;
}

4、哈希查找

    哈希查找是經過計算數據元素的存儲地址進行查找的一種方法。咱們使用一個下標範圍比較

大的數組來存儲元素。能夠設計一個函數(哈希函數, 也叫作散列函數),使得每一個元素的關

鍵字都與一個函數值(即數組下標)相對應,因而用這個數組單元來存儲這個元素;也能夠簡

單的理解爲,按照關鍵字爲每個元素"分類",而後將這個元素存儲在相應"類"所對應的地方。

可是,不可以保證每一個元素的關鍵字與函數值是一一對應的,所以極有可能出現對於不一樣的元

素,卻計算出了相同的函數值,這樣就產生了"衝突",換句話說,就是把不一樣的元素分在了相同

的"類"之中。

   一、算法原理

   哈希查找步驟:

  1)用給定的哈希函數構造哈希表;

  2)根據選擇的衝突處理方法解決地址衝突;

  3)在哈希表的基礎上執行哈希查找。

  創建哈希表操做步驟:

  1)取數據元素的關鍵字key,計算其哈希函數值。若該地址對應的存儲空間尚未被佔用,則

將該元素存入;不然執行step2解決衝突。

  2)根據選擇的衝突處理方法,計算關鍵字key的下一個存儲地址。若下一個存儲地址仍被佔

用,則繼續執行step2,直到找到能用的存儲地址爲止。

  哈希查找步驟爲:

  1)Step1 對給定k值,計算哈希地址 Di=Hk);若HST爲空,則查找失敗;若HST=k,則查

找成功;不然,執行step2(處理衝突)。

  2)Step2 重複計算處理衝突的下一個存儲地址 Dk=RDk-1),直到HST[Dk]爲空,或

HST[Dk]=k爲止。若HST[Dk]=K,則查找成功,不然查找失敗。

  二、算法分析 

  時間複雜度幾乎是O(1),取決於產生衝突的多少。

  三、算法實現

/*
  哈希查找。實現哈希函數爲「除法取餘法」,解決衝突爲「開放地址線性探測法」。
--除法取餘法:key=value%C;
--開放地址線性探測法:若是兩個數據元素的哈希值相同,則在哈希表中爲後插入的數據元素另外選擇一個表項。當程序查找哈希表時,
  若是沒有在第一個對應的哈希表項中找到符合查找要求的數據元素,程序就會繼續日後查找,直到找到一個符合查找要求的數據元素,
  或者遇到一個空的表項。 
*/

#include <iostream>
#include <stdio.h>

void InsertHash( int *hashArray, int length, int data )
{
  int hashAddress = data % length; // 哈希函數
  
  while( hashArray[ hashAddress ] != 0 )  // 若是key存在,則說明已經被別人佔用,此時必須解決衝突,這裏假設哈希表元素都初始化爲0,而插入元素都大於0
  {
    hashAddress = ( ++hashAddress ) % length;  // 用開放尋址法找到
  }

  hashArray[ hashAddress ] = data;
}

int HashSearch( int *hashArray, int length, int key )
{
  if( hashArray == NULL || length <= 0 )
  {
    printf( "invalid input.\n" );
    return -1;
  }
  
  int hashAddress = key % length;

  while( hashArray[ hashAddress ] != 0 && hashArray[ hashAddress ] != key )
  {
    hashAddress = ( ++hashAddress ) % length;
  }

  if( hashArray[ hashAddress ] == 0 )
    return -1;
  else
    return hashAddress;
}

void Test( const char* testName, int* hashArray, int length, int key )
{
  if( testName == NULL ) 
  {
    printf( "test invaild input.\n" );
    return;
  }

  printf( "%s begins: \n", testName );

  int found = HashSearch( hashArray, length, key );

  if( found >= 0 )
    printf( "found the key: %d in index %d\n", key, found );
  else
    printf( "not found the key: %d\n", key );

  printf( "\n" );
}

// 要找的數字存在,數組12個元素,哈希表的大小選爲離數組大小二倍最近的質數,這裏是29
void Test1()
{
  int length = 29;
  int arrayLength = 12;
  int array[ ] = { 5, 3, 6, 7, 9, 8, 11, 15, 18, 20, 40, 50 };

  int *hash = new int[ length ];
  
  for( int i = 0; i < length; i++ )
  {
    hash[ i ] = 0;    // 初始化哈希表元素爲0
  }
  
  for( int j = 0; j < arrayLength; j++ )
  {
    InsertHash( hash, length, array[ j ] );
  }

  Test( "Test1", hash, length, 15 ); 

  delete [] hash;
}

// 要找的數字不存在,數組12個元素,哈希表的大小選爲離數組大小二倍最近的質數,這裏是29
void Test2()
{
  int length = 29;
  int arrayLength = 12;
  int array[ ] = { 5, 3, 6, 7, 9, 8, 11, 15, 18, 20, 40, 50 };

  int *hash = new int[ length ];
  
  for( int i = 0; i < length; i++ )
  {
    hash[ i ] = 0;    // 初始化哈希表元素爲0
  }
  
  for( int i = 0; i < arrayLength; i++ )
  {
    InsertHash( hash, length, array[ i ] );
  }

  Test( "Test2", hash, length, 2 ); 

  delete [] hash;
}

// 輸入數組爲null,且長度異常
void Test3()
{
  Test( "Test3", NULL, -1, -1 ); 
}

int main()
{
  Test1();
  Test2();
  Test3();
  
  return 0;
}

相關文章
相關標籤/搜索