@查找(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 |
1 |
|
2 |
1 |
|
3 |
2(生1对) |
|
4 |
3(又生1对) |
|
5 |
5(生2对) |
|
6 |
8(生3对) |
|
7 |
13(生5对) |
|
…….. |
|
{
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);
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);
}
}
浙公网安备 33010602011771号