首先,在开始之前,我首先必须说明。
代码方面我会使用C++与JAVA分别编写,当然,如果这篇代码过于简单可能并没有JAVA部分。
C++方面会趋于简单,尽量不涉及复杂的语法部分,但这并不意味着不会涉及新标准语法。
JAVA会更难一些,有一些比较麻烦的语法
当然随着代码的深入C++也会引入复杂语法.
红字部分属于进阶内容,会涉及一点计算机原理。
好了,让我们开始吧。
假设我要在一个有序数组中寻找一个值为50的数据,我们该如何寻找这个数据呢?
int* temp = new int[100];
for (int i = 0; i < 100; i++) {//给数组赋值
temp[i] = i;
}
for (int i = 0; i < 100;i++) {//假设我们想找到值为80的数据的位置,虽然说你在这个代码能看到这个数据位于80的位置,第81个数据,但是在通常的代码编写中,我们并不知道它处于那个位置,因此高效的查找是很有必要的。
if (temp[i] == 80) {
cout << “这个值的是第” << i+1 << “个数据” << endl;
}
}
这样实在是太慢了,所以我们需要一种更好的查找方式。
那就是二分查找。
假设我有一个数组,这个数组是由1-1000的数字构成的。
数组有100个元素,那么这样,如果我想知道一个值为200的数据在这个数组的第几个位置。
如果我们从第一个数字开始,去一个一个寻找,这太慢了,我们无法接受这样慢的速度。
假设这个数组有100W个数字,我们难不成还需要查找100W次?
所以,我们需要二分查找来加快查找速度而不是我之前的普通查找。
#include
using namespace std;
int rank1(int key,int length, int a [] ) {//这里因为C++不知道你指针指向的数组字节数,我们必须对数组长度进行传递
int lo = 0;
int hi = length -1;//C++的原始数组并不好用,以后会讲解C++封装好的vector或array
while (lo <= hi) {
int mid = lo + (hi - lo / 2);//数组第几个数是中间的
if (key < a[mid]) {//如果我们要找的数值小于数组中间的数
hi = mid - 1;//我们去找从lo到数组中间的数
}
else if (key > a[mid]) {
lo = mid + 1;//同理,如果我们要找的数值大于数组中间的数,我们就去找上半个数组
}
else return mid;
}
return -1;//如果没找到,我们的函数必须有返回值,则返回-1进行处理
}
int main() {
int temp[100];
for (int i = 0; i < 100; i++) {//给数组赋值
temp[i] = i;
}
int result = rank1(70,100,temp);
cout << result<<endl;
result = rank1(101, 100, temp);
cout << result << endl;
}
二分查找C++代码,二分查找太简单了JAVA不再赘述。
二分查找的原理是什么?
我们用一个故事来描述它。假设我有一个数组,这个数组是由1-1000的数字构成的。
数组有100个元素,那么这样,如果我想知道一个值为200的数据在这个数组的第几个位置。你每次都可以对我说出一个位置。而我会告诉你这个位置距离真正我们想要知道的位置是大还是小。
举例:你说50,第50个数据的值为100,很明显,这个值是小于我们要寻找的数字200的,我会告诉你,小了。
那么二分查找的思想,或者说更好的猜法是什么?
我们应该先从中位数猜起,由于数组是有序的,当我告诉你这个数字大了或小了的时候,你便排除了一半的数字。
同理,我们从另外一半没有被排除的数字的中位数猜起,你便又可以排除一半的无效数字。
二分查找的原理是二进制的数字表达方式的位数。
题外话:假设我要寻找1000这个数字,你每次可以猜1位,而我会告诉你这个位的数字,那么我们仅仅需要猜4次即可获得这个数字。
而对于十进制表达的数字1000来说,将其转化为二进制则为1111101000,由于二进制中的1 0分别表示大或小,我们便可以通过猜10次来获得这个数字的正确位置。
那么现在,我们已经学会了一种算法。算法有很多种,我们怎么样才能更好的表示算法的优劣呢,或者说算法的运行时间。
那么我们必须介绍一种算法的速度的表示方法大O表示法。 假设一个算法的时间复杂度是 O(n),n 在这里代表的意思就是数据的个数。举个例子,如果你的代码用一个循环遍历 100 个元素,那么这个算法就是 O(n),n 为 100,所以这里的算法在执行时就要做 100 次工作。
二分查找法用大O表示,则为O(log n),那么对于100个元素来说,需要查找几次呢?
答案是7次。这里的n指的是元素个数,而log底的数字可能会因为各种算法不同而发生变化,不过他们都属于对数时间。
接下来我们介绍几个比较常见的算法表示时间。
O(1) 常数级 最好的。不论输入数据量有多大,这个算法的运行时间总是一样的。例子: 基于索引取出数组中对应的元素。
O(log n) 对数级 相当好。这种算法每次循环时会把需要处理的数据量减半。如果你有 100 个元素,则只需要七步就可以找到答案。1000 个元素只要十步。100,0000 元素只要二十步。即便数据量很大这种算法也非常快。例子:二分查找。
O(n) 线性级 还不错。如果你有 100 个元素,这种算法就要做 100 次工作。数据量翻倍那么运行时间也翻倍。例子:线性查找。
O(n log n) 线性对数级 还可以。比线性级差了一些,不过也没那么差劲。例子:最快的通用排序算法。
O(n^2) 二次方级 有点慢。如果你有 100 个元素,这种算法需要做 100^2 = 10000 次工作。数据量 x 2 会导致运行时间 x 4 (因为 2 的 2 次方等于 4)。例子:循环套循环的算法,比如插入排序。
O(n^3) 三次方级 特别慢。如果你有 100 个元素,那么这种算法就要做 100^3 = 100,0000 次工作。数据量 x 2 会导致运行时间 x 8。例子:矩阵乘法。
O(2^n) 指数级 超级慢。这种算法你要想方设法避免,但有时候你就是没得选。加一点点数据就会把运行时间成倍的加长。例子:旅行商问题。
O(n!) 阶乘级 比蜗牛还慢!不管干什么都要跑个 N 年才能得到结果。
你可能会发现,这个世界上难道还有n!的算法吗?
那么我们来介绍一个非常经典的问题,旅行商问题。
有一位旅行商,他要前往5个城市。
请问5个城市的可能顺序有多少种?
可能你已经意识到了,如果不考虑出发点和终点的话,我们一共有5!个选择,这足足有120种。
而涉及6个城市的时候我们有6!个选择,有720种排列方式。
旅行商问题至今没有一个最佳答案,我们只能选择一些近似答案。