@查找(Searching)就是根据给定的某个值,在查找表中确定一个关键字等于给定值的数据元素(或记录)。

一,查找表按照操作方式来分有两大种:

  1,静态查找表:只作查找操作的查找表。

  2,动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。

 

@下边是常用的一些查找方法

一,顺序表查找

  又叫线性查找,是最基本的查找技术,它的查找过程是:从表中第一个(或最后一个)记录开始,逐个进行记录的关键字和给定值比较,若某个记录的关键字给定值相等,则查找成功,找到所查的记录;如果直到最后一个(或第一个)记录,其关键字和给定值比较都不等时,则表中没有所查的记录,查询不成功

    C#实现代码:

    int[] ints={1,3,20,7,9,6,12};  //要查找的数组
        bool flag=false;
        for(int i=0;i<ints.Length;i++)
        {
            if(ints[i]==3)
            {
                Console.WriteLine(string.Format("找到了,索引号为:{0}",i));
                flag=true;
            }
        }
        if(flag==false)
        {
            Console.WriteLine("没找到");
        }
        Console.ReadKey();

这段代码非常简单,就是在数组ints中查看有没有关键字key。

  顺序查找技术是有很大缺点的,n很大时,查找效率极为低下,不过优点也是有的,这个算法非常简单,对静态查找表的记录没有任何要求,在一些小型数据的查找时,是可以适用的。

  

二,折半查找(又叫二分查找)----属于有序查找

  它的前提线性表中的记录必须是关键码有序(通常从小到大有序),线性表必须采用顺序存储

  折半查找的基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止

  C#实现代码:

    int[] ints={1,2,3,4,5,6,7};
        bool flag=false;
        int mid=0;    //中间值索引号(下标),例如上边数组,(0+6)/2=3,中间值索引下标即3,ints[3]=4。例如数组ints={1,2,3,4,5,6,7,8},(0+7)/2=3,其实这个数组中间值,是3和4,我们这里取3
        int low=0; //数组下标,数组首位
        int high=ints.Length-1; //数组下标,数组末尾

    int count=0;
        while(low<=high) //即不超出数组的界限。两边的界限,即不超出首界限,也不超出末尾界限
        {
            /*
                例如找7,第一次循环mid=6/2=3,7>3,low=4
                第二次循环,mid=(4+6)/2=5,7>5,low=6
                第三次循环,mid=(6+6)/2=6,7==7
                假如是找10,第三次循环,仍然是10>7,low=6+1=7,就会跳出循环,7<=6返回false,就会跳出循环
                假如找-1,最后没找到high=0-1=-1,0<=-1,也会返回false,跳出循环。
            */

    count++;
            mid=(low+high)/2;  //中间取值很重要,不能错。
            if(-1<ints[mid])
            {
                high=mid-1; //如果查找值小于中间值时,就查找左半区中间值左边的序列。即查找0到mid-1的序列。

      //索引范围:low...mid-1   (为什么要low<=high,当high的值减到-1时,即超出了数组的0下标时,就会跳出循环。因为0<=-1不成立,即索引超出了数组的左范围。)
            }
            else if(-1>ints[mid])
            {
                low=mid+1;   //如果查找值大于中间值时,就查找右半区中间值右边的序列。即查找mid+1到ints.Length-1的序列。

      //索引范围: mid+1...high    (为什么要low<=high,当low的值加到超出了数组的末尾下标时,就会跳出循环。因为索引超出数组的右范围。)
            }
            else           //这里的结果就是 查找值==中间值
            {
                Console.WriteLine(string.Format("找到了,数组索引号为{0}循环了{1}次",mid,count));
                flag=true;
                break;
            }
        }
        if(flag==false)
        {
            Console.WriteLine("没找到");
        }
        Console.ReadKey();

@重点总结:

1,学习查找数组的范围分割问题,以及中间值,左半区和右半区索引的范围的思想。

 

三,插值查找(折半查找的改进算法)----属于有序查找

   为什么一定要折半,而不是折四分之一或者折更多呢?

  打个比方,在英文字典里查apple,你下意识的是翻前面的书页,还是后面的书页。如果你查zoo,你又怎么查?很显然,这里你绝对不会是从中间开始查起,而是有一定目的的往前或往后翻。

  同样的,比如要在取值范围0~10000之间查找5,我们自然会考虑从数组下标较小的开始查找。

  看来,我们的折半查找,还是有改进空间的。

  C#实现代码:

    插值查找,就是将上边代码中mid=(low+high)/2;换成mid=low+(high-low)*(2-ints[low])/(ints[high]-ints[low]);  //死记公式。

    插值查找是根据要查找的关键字key与查找表中最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式: key-ints[low]/ints[high]-ints[low],要查找的值减去最小值除以最大值减去最小值,然后乘以数组长度减1(即最后一位的索引号),再加1。

    对于分部比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好得多。反之,数组中如果分部类似{0,1,2,2000,2001....,999998,999999}这种极端不均匀的数据,用插值查找未必是很合适的选择。

 

四,斐波那契查找(利用了黄金分割原理来实现的)----属于有序查找

  我们折半查找是从中间分,也就是说,每一次查找总是一分为二,无论数据偏大还是偏小,很多时候这都未必就是最合理的做法。除了插值查找,我们再介绍一种有序查找,斐波那契查找,它是利用了黄金分割原理来实现的。

  首先介绍一下什么是斐波那契数列:1,1,2,3,5,8,13,21…… ,可以看到这里面的规律吧.就是每一项是前面相邻两项之和. 

  1,常见的一道面试题目:
  有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少???其实,这个题目咋一看比较难,但要是仔细分析的话,可以发现它就是我们数学史上一个非常经典的斐波那契数数列问题
  ---注意,是一对兔子,一次生一对。不是一只,一只怎么生。。。。
  其实说到斐波那契数数列,我们并不陌生,我记得在我读大一的时候的C语言课程上,老师就给我们讲了这个问题。比如很经典的 f(n)=f(n-1)+f(n-2),就是说当前数是由它的前2项数相加而得到的

  当然,这个听说在生活上也特别的由名,最有名的或许就是我们常听到的,美学标准黄金分割点。有兴趣的读者可以到百度上去看看。  

  OK,我们先可以把每个月出生的兔子总数画出来,先看出规律来,应该这个问题就不难了!~
 

月份

兔子总数

1

1

2

1

3

2(生1对)

4

3(又生1对)

5

5(生2对)

6

8(生3对)

7

13(生5对)

……..

  通过上面表格的规律我们就可以很容易的得出代码如下,代码都比较简单,所以不做过多的解释
  class Program
    {
        static void Main(string[] args)
        {
            Console.Write("请输入一个数字:");
            int i =Convert.ToInt32( Console.ReadLine());
            Fibonacci(i);
            Console.ReadKey();
         }
        public static void Fibonacci(int  month)
        {
            int number1 = 1, number2 = 1, sum = 0;
            if (month < 1)
    {
                sum = 1;
    }
            Console.WriteLine();
            Console.WriteLine("第1个月,有{0}对",number1);
            Console.WriteLine("第1个月,有{0}对", number2);
    //前两个月不生,到第三个月生一对兔子。所以循环从第3个月开始。
            for (int i = 3; i <= month; i++)
            {
                sum = number1 + number2;
                Console.WriteLine("第{0}月,有{1}对",i,sum);
                number1 = number2;  //值后移,当i=5时,number1=number2=2,本来number1=1
                number2 = sum;   //值改变。当i=5时,number2=number1+number2=sum=3
            }
        }
    } 

结果如下:

/**
     * @param month:可以任意给定的整数
     * 若想知道第一个月到第10个月,则传入10
     * 若想知道到第25个月的,就传入25,依次类推
     * 第一个月1对,第二个月一对
     * @return
     */  

    public static void getRabbitAmount(int month)
    {
        long a = 1, b = 1;
        long temp;
        for(int i = 3; i <= month; i++)
        {

    //从第三个月开始
            temp = b;
            b = a + b;
            a = temp;
            Console.WriteLine(b);
        }
    }

posted on 2014-05-06 18:25  学到老死  阅读(278)  评论(0)    收藏  举报