zoukankan      html  css  js  c++  java
  • 数据结构笔记-----查找

    查找的概念



    搜索引擎用的是静态查找




    代码(静态查找and动态查找)


    <strong><span style="font-size:18px;">#include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include "SeqList.h"
    
    #define SIZE 20
    /* run this program using the console pauser or add your own getch, system("pause") or input loop */
    
    void print_array(int a[], int len)
    {
        int i = 0;
        
        for(i=0; i<len; i++)
        {
            printf("%d, ", a[i]);
        }
        
        printf("
    ");
    }
    
    int static_search(int a[], int len, int key)
    {//静态查找 
        int ret = -1;
        int i = 0;
        
        for(i=0; i<len; i++)
        {
            if( a[i] == key )
            {
                ret = i;
                break;
            }
        }
        
        return ret;
    }
    
    void print_list(SeqList* list)
    {
        int i = 0;
        
        for(i=0; i<SeqList_Length(list); i++)
        {
            printf("%d, ", (int)SeqList_Get(list, i));
        }
        
        printf("
    ");
    }
    
    int dynamic_search(SeqList* list, int key)
    {//动态查找 
        int ret = -1;
        int i = 0;
        
        for(i=0; i<SeqList_Length(list); i++)
        {
            if( (int)SeqList_Get(list, i) == key )
            {
                ret = i;
                
                SeqList_Delete(list, i);
                
                break;
            }
        }
        
        return ret;
    }
    
    int main(int argc, char *argv[]) 
    {
        SeqList* list = SeqList_Create(SIZE);
        int a[SIZE] = {0};
        int i = 0;
        int key = 0;
        int index = 0;
        
        srand((unsigned int)time(NULL));
        
        for(i=0; i<SIZE; i++)
        {
            a[i] = rand() % 100;
            SeqList_Insert(list, (SeqListNode*)(rand() % 100), i);
            //加入链表 动态查找 
        }
        
        key = rand() % 100;
        
        printf("Static Search Demo
    ");
        printf("Key: %d
    ", key);
        printf("Array: 
    ");
        print_array(a, SIZE);
        
        index = static_search(a, SIZE, key);
        
        if( index >= 0 )
        {
            printf("Success: a[%d] = %d
    ", index, a[index]);
        }
        else
        {
            printf("Failed!
    ");
        }
        
        printf("Dynamic Search Demo
    ");
        printf("Key: %d
    ", key);
        printf("List: 
    ");
        print_list(list);
        
        index = dynamic_search(list, key);
        
        if( index >= 0 )
        {
            printf("Success: list[%d] = %d
    ", index, key);
        }
        else
        {
            printf("Failed!
    ");
        }
        
        print_list(list);
        
    	return 0;
    }</span></strong>


    小结



    顺序表和有序表查找

    顺序表查找



    <strong><span style="font-size:18px;">#include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define SIZE 20
    /* run this program using the console pauser or add your own getch, system("pause") or input loop */
    
    void print_array(int a[], int begin, int end)
    {
        int i = 0;
        
        for(i=begin; i<=end; i++)
        {
            printf("%d, ", a[i]);
        }
        
        printf("
    ");
    }
    
    int another_search(int a[], int len, int key)
    {
        int ret = len;
        
        a[0] = key;
        //优化顺序查找 
        while( a[ret] != key )
        {//只需比较一次,提高效率 
            ret--;
        }
        
        return ret;
    }
    
    int main(int argc, char *argv[]) 
    {
        int a[SIZE + 1] = {0};
        int i = 0;
        int key = 0;
        int index = 0;
        
        srand((unsigned int)time(NULL));
        
        for(i=1; i<=SIZE; i++)
        {
            a[i] = rand() % 100;
        }
        
        key = rand() % 100;
        
        printf("Another Search Demo
    ");
        printf("Key: %d
    ", key);
        printf("Array: 
    ");
        print_array(a, 1, SIZE);
        
        index = another_search(a, SIZE, key);
        
        if( index > 0 )
        {
            printf("Success: a[%d] = %d
    ", index, a[index]);
        }
        else
        {
            printf("Failed!
    ");
        }
        
    	return 0;
    }</span></strong>


    二分查找(有序查找的一种)







    插值查找


    插值查找的速度比二分查找快,但是插值查找需要进行浮点数计算,因此,从稳定性上,插值不如二分查找

    小结



    二分查找代码


    <strong><span style="font-size:18px;">#include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #define SIZE 20
    /* run this program using the console pauser or add your own getch, system("pause") or input loop */
    
    void println(int array[], int len)
    {
        int i = 0;
        
        for(i=0; i<len; i++)
        {
            printf("%d ", array[i]);
        }
        
        printf("
    ");
    }
    
    void swap(int array[], int i, int j)
    {
        int temp = array[i];
        
        array[i] = array[j];
        
        array[j] = temp;
    }
    
    void SelectionSort(int array[], int len) // O(n*n)
    {
        int i = 0;
        int j = 0;
        int k = -1;
        
        for(i=0; i<len; i++)
        {
            k = i;
            
            for(j=i; j<len; j++)
            {
                if( array[j] < array[k] )
                {
                    k = j;
                }
            }
            
            swap(array, i, k);
        }
    }
    //二分查找 
    int binary_search(int a[], int low, int high, int key) // O(logn)
    {
        int ret = -1;
        
        if( low <= high )
        {
            int mid = (low + high) / 2;
            
            if( a[mid] == key )
            {
                ret = mid;
            }
            else if( key < a[mid] )
            {
                ret = binary_search(a, low, mid-1, key);
            }
            else if( key > a[mid] )
            {
                ret = binary_search(a, mid+1, high, key);
            }
        }
        
        return ret;
    }
    //二分查找 
    int binary_search_ex(int a[], int low, int high, int key) // O(logn)
    {//循环代替递归  
        int ret = -1;
        
        while( low <= high )
        {
            int mid = (low + high) / 2;
            
            if( a[mid] == key )
            {
                ret = mid;
                break;
            }
            else if( key < a[mid] )
            {
                high = mid - 1;
            }
            else if( key > a[mid] )
            {
                low = mid + 1;
            }
        }
        
        return ret;
    }
    
    int interpolation_search(int a[], int low, int high, int key)
    {//插值查找 
        int ret = -1;
        
        while( (low <= high) && (a[low] <= key) && (key <= a[high]) )
        {
            float fx = 1.0f * (key - a[low]) / (a[high] - a[low]);
            int mid = low + fx * (high - low);
            
            if( a[mid] == key )
            {
                ret = mid;
                break;
            }
            else if( key < a[mid] )
            {
                high = mid - 1;
            }
            else if( key > a[mid] )
            {
                low = mid + 1;
            }
        }
        
        return ret;
    }
    
    int main(int argc, char *argv[]) 
    {
        int a[SIZE] = {0};
        int i = 0;
        int key = 0;
        int index = 0;
        
        srand((unsigned int)time(NULL));
        
        for(i=1; i<=SIZE; i++)
        {
            a[i] = rand() % 100;
        }
        
        key = 50;
        
        printf("Binary Search Demo
    ");
        printf("Key: %d
    ", key);
        printf("Array: 
    ");
        
        SelectionSort(a, SIZE);
        
        println(a, SIZE);
        
        index = interpolation_search(a, 0, SIZE-1, key);
        
        if( index > 0 )
        {
            printf("Success: a[%d] = %d
    ", index, a[index]);
        }
        else
        {
            printf("Failed!
    ");
        }
        
    	return 0;
    }</span></strong>


    斐波那契数列

    黄金比例又称黄金分割,是指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1。

    0.618被公认为最具有审美意义的比例数字,这个数值的作用不仅仅体现在诸如绘画、雕塑、音乐、建筑等艺术领域,而且在管理、工程设计等方面也有着不可忽视的作用。因此被称为黄金分
    割。

    大家记不记得斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….(从第三个数开始,后边每一个数都是前两个数的和)

    然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。


    代码


    <strong><span style="font-size:18px;">  
    #include "stdafx.h"  
    #include <memory>  
    #include  <iostream>  
    using namespace std;  
      
    const int max_size=20;//斐波那契数组的长度  
      
    /*构造一个斐波那契数组*/   
    void Fibonacci(int * F)  
    {  
        F[0]=0;  
        F[1]=1;  
        for(int i=2;i<max_size;++i)  
            F[i]=F[i-1]+F[i-2];  
    }  
      
    /*定义斐波那契查找法*/    
    int Fibonacci_Search(int *a, int n, int key)  //a为要查找的数组,n为要查找的数组长度,key为要查找的关键字  
    {  
      int low=0;  
      int high=n-1;  
        
      int F[max_size];  
      Fibonacci(F);//构造一个斐波那契数组F   
      
      int k=0;  
      while(n>F[k]-1)//计算n位于斐波那契数列的位置  
          ++k;  
      
      int  * temp;//将数组a扩展到F[k]-1的长度  
      temp=new int [F[k]-1];  
      memcpy(temp,a,n*sizeof(int));  
      
      for(int i=n;i<F[k]-1;++i)  
         temp[i]=a[n-1];  
        
      while(low<=high)  
      {  
        int mid=low+F[k-1]-1;  
        if(key<temp[mid])  
        {  
          high=mid-1;  
          k-=1;  
        }  
        else if(key>temp[mid])  
        {  
         low=mid+1;  
         k-=2;  
        }  
        else  
        {  
           if(mid<n)  
               return mid; //若相等则说明mid即为查找到的位置  
           else  
               return n-1; //若mid>=n则说明是扩展的数值,返回n-1  
        }  
      }    
      delete [] temp;  
      return -1;  
    }  
      
    int _tmain(int argc, _TCHAR* argv[])  
    {  
        int a[] = {0,16,24,35,47,59,62,73,88,99};  
        int key=100;  
        int index=Fibonacci_Search(a,sizeof(a)/sizeof(int),key);  
        cout<<key<<" is located at:"<<index;  
        system("PAUSE");  
        return 0;  
    }  </span></strong>

    斐波那契查找的核心是:
    1)当key=a[mid]时,查找成功;
    2)当key<a[mid]时,新的查找范围是第low个到第mid-1个,此时范围个数为F[k-1] - 1个,即数组左边的长度,所以要在[low, F[k - 1] - 1]范围内查找;
    3)当key>a[mid]时,新的查找范围是第mid+1个到第high个,此时范围个数为F[k-2] - 1个,即数组右边的长度,所以要在[F[k - 2] - 1]范围内查找。





    线性索引查找

    索引的概念


    线性索引












    小结




    思考:


    一、数组中一个只出现一次的整数

    题目:一个数组中,有一个数字只出现一次,其他数字都出现两次,写出程序找出这个数字。

    思路:利用异或运算,如果是两个相同的数,那么他们的结果就是0,剩下的就是只出现一次的数。

    代码如下:

    <strong><span style="font-size:18px;">#include <stdio.h>  
      
    //find a number,which appearing once in an array  
    int FindNumAppearOnce(int arr[], int iLen)  
    {  
        int iNumAppearOnce;  
      
        iNumAppearOnce = arr[0];  
        for(int i = 1; i < iLen; i++)  
            iNumAppearOnce ^= arr[i];  
      
        return iNumAppearOnce;  
    }  
      
    int main(void)  
    {  
        int arr[] = {1,2,2,3,3,4,4};  
        printf("%d
    ",FindNumAppearOnce(arr, 7));  
        return 0;  
    }  </span></strong>

    二、数组中两个只出现一次的整数


    思路:我们要让这两个只出现一次的数分别在两组,然后两组分别异或运算找出只出现一次的数。方法是,首先所有的数进行异或运算,得到的是只出现一次的两个数的异或运算结果。然后根据这个结果中第一次出现1的位(从右开始算),将数组分为两组。为什么可以分成两组?因为异或运算为1就是只出现一次的那两个数的那两个bit位不同,其他的数都抵消了。
    代码如下:

    <strong><span style="font-size:18px;">#include <stdio.h>  
      
    int FindIndexOfBit1(int n)  
    {  
        int indexOfBit1 = 0;  
        while(((1 & n) == 0) && indexOfBit1 < 32){  
            n >>= 1;  
            indexOfBit1++;  
        }  
      
        return indexOfBit1;  
    }  
      
    int IsBit1(int n, int indexOfBit1){  
        n >>= indexOfBit1;  
        return n & 1;  
    }  
      
    //find two number,which appearing once in an array  
    //Input:arr - an array  
    //      iLen - the length of the array  
    //      pNum1 - number 1 founded   
    //      pNum2 - number 2 founded  
    void FindNumAppearOnce(int arr[], int iLen, int *pNum1, int *pNum2)  
    {  
        if(iLen < 2)  
            return;  
      
        //get num1^num2  
        int resultOfExclusiveOr = 0;  
        for(int i = 0; i < iLen; i++)  
            resultOfExclusiveOr ^= arr[i];  
      
        //get index of the first bit  
        int indexOfBit1 = FindIndexOfBit1(resultOfExclusiveOr);  
      
        *pNum1 = *pNum2 = 0;  
        for(int i = 0; i < iLen; i++){  
            if(IsBit1(arr[i], indexOfBit1))  
                *pNum1 ^= arr[i];  
            else  
                *pNum2 ^= arr[i];  
        }  
    }  
      
    int main(void)  
    {  
        int arr[] = {1,5,2,2,3,3,4,4};  
        int a;  
        int b;  
        FindNumAppearOnce(arr, 8, &a, &b);  
        printf("%d %d
    ", a,b);  
        return 0;  
    }  </span></strong>


    三、数组中3个只出现一次的数字
    思路:
    我们要把三个不同的数分开!首先分开两组,一个不同的和两个不同的各位一组。分开的思路:参考http://zhedahht.blog.163.com/blog/static/25411174201283084246412/
    总之:x^a、x^b、x^c三个数字中,只有一个数字的第m位是1。作为它们的区分标准。


    <strong><span style="font-size:18px;">#include <stdio.h>  
      
    //  
    int ValueOfBit1(int iNum)  
    {  
        return iNum & ~(iNum - 1);  
    }  
      
    //  
    void Swap(int *pNum1, int *pNum2)  
    {  
        int iTemp;   
        iTemp = *pNum1;  
        *pNum1 = *pNum2;  
        *pNum2 = iTemp;  
    }  
      
    //  
    void PrintUniqueTwo(int arrNums[], int iLen)  
    {  
        int iXorResult = 0;  
        for(int i = 0; i < iLen; i++)  
            iXorResult ^= arrNums[i];  
      
        int iDiffBit = ValueOfBit1(iXorResult);  
      
        int iNum1, iNum2;  
        iNum1 = iNum2 = 0;  
        for(int i = 0; i < iLen; i++)  
            if(iDiffBit & arrNums[i])  
                iNum1 ^= arrNums[i];  
            else  
                iNum2 ^= arrNums[i];  
      
        printf("%d
    %d
    ", iNum1, iNum2);  
    }  
      
    void PrintUniqueThree(int arrNums[], int iLen)  
    {  
        if(iLen < 3)  
            return;  
      
        //get a ^ b ^ c  
        int iXorResult = 0;  
        for(int i = 0; i < iLen; i++)  
            iXorResult ^= arrNums[i];  
      
        //get fun(fun(x ^ a) ^ fun(x ^ b) ^ fun(x ^ b))  
        int iFunMultiResult = 0;  
        for(int i = 0; i < iLen; i++)  
            iFunMultiResult ^= ValueOfBit1(iXorResult ^ arrNums[i]);  
        iFunMultiResult = ValueOfBit1(iFunMultiResult);  
      
        //get the first unique number  
        int iFirstUniqueNum = 0;  
        for(int i = 0; i < iLen; i++)  
            if(ValueOfBit1(arrNums[i] ^ iXorResult) == iFunMultiResult)  
                iFirstUniqueNum ^= arrNums[i];  
      
        printf("%d
    ", iFirstUniqueNum);  
      
        //move the first unique number to the end of the array  
        for(int i = 0; i < iLen; i++)  
            if(arrNums[i] == iFirstUniqueNum){  
                Swap(&arrNums[i], &arrNums[iLen - 1]);  
                break;  
            }  
      
            PrintUniqueTwo(arrNums, iLen - 1);  
    }  
      
    int main(void)  
    {  
        int arrNums[] = {2,3,4,4,1,3,5,5,0};  
        PrintUniqueThree(arrNums, 9);  
        return 0;  
    }  </span></strong>


    1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现
    一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空
    间,能否设计一个算法实现?
    将1001个元素相加减去1,2,3,……1000数列的和,得到的差即为重复的元素。
     
     int   Find(int   *   a)   
      {   
      int   i;//变量   
      for   (i   =   0   ;i<=1000;i++)   
      {   
      a[1000]   +=   a[i];   
      }   
      a[1000]   -=   (i*(i-1))/2       //i的值为1001   
      return   a[1000];   
      }

    利用下标与单元中所存储的内容之间的特殊关系,进行遍历访问单元,一旦访问过的单
    元赋予一个标记,利用标记作为发现重复数字的关键。代码如下:
    <strong><span style="font-size:18px;">void FindRepeat(int array[], int length)
    {
        int index=array[length-1]-1;
        while ( true )
        {
           if ( array[index]<0 )
               break;
           array[index]*=-1;
           index=array[index]*(-1)-1;
        }
     
        cout<<"The repeat number is "<<index+1<<endl;
    }</span></strong>


    此种方法不非常的不错,而且它具有可扩展性。在坛子上有人提出:
    对于一个既定的自然数 N ,有一个 N + M 个元素的数组,其中存放了小于等于 N 的所有
    自然数,求重复出现的自然数序列{X} 。
     
    对于这个扩展需要,自己在A_B_C_ABC(黄瓜儿才起蒂蒂)的算法的基础上得到了自己的算法
    代码:
    按照A_B_C_ABC(黄瓜儿才起蒂蒂)的算法,易经标记过的单元在后面一定不会再访问到,除非它是重复的数字,也就是说只要每次将重复数字中的一个改为靠近N+M的自然数,让遍历能访问到数组后面的单元,就能将整个数组遍历完。
    代码:
    <strong><span style="font-size:18px;">*/
    void FindRepeat(int array[], int length, int num)
    {
     int index=array[length-1]-1;
     cout<<"The repeat number is ";
     while ( true )
     {
     if ( array[index]<0 )
     {
       num--;
       array[index]=length-num;
       cout<<index+1<<'t';
     }
     
     if ( num==0 )
     {
       cout<<endl;
      return;
     }
     array[index]*=-1;
     index=array[index]*(-1)-1;
     }
    }</span></strong>












  • 相关阅读:
    Leetcode 92. Reverse Linked List II
    Leetcode 206. Reverse Linked List
    Leetcode 763. Partition Labels
    Leetcode 746. Min Cost Climbing Stairs
    Leetcode 759. Employee Free Time
    Leetcode 763. Partition Labels
    搭建数据仓库第09篇:物理建模
    Python进阶篇:Socket多线程
    Python进阶篇:文件系统的操作
    搭建数据仓库第08篇:逻辑建模–5–维度建模核心之一致性维度2
  • 原文地址:https://www.cnblogs.com/Zyf2016/p/6337833.html
Copyright © 2011-2022 走看看