查找(Searching)就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素。
查找表(Search Table):由同一类型的数据元素构成的集合
关键字(Key):数据元素中某个数据项的值,又称为键值
主键(Primary Key):可唯一的标识某个数据元素或记录的关键字
查找表按照操作方式可分为:
1.静态查找表(Static Search Table):只做查找操作的查找表。
它的主要操作是:
①查询某个“特定的”数据元素是否在表中
②检索某个“特定的”数据元素和各种属性
2.动态查找表(Dynamic Search Table):
在查找中同时进行插入或删除等操作:
①查找时插入数据
②查找时删除数据
顺序查找
算法简介
顺序查找又称为线性查找,是一种最简单的查找方法。适用于线性表的顺序存储结构和链式存储结构。该算法的时间复杂度为O(n)。
基本思路
从第一个元素m开始逐个与需要查找的元素x进行比较,当比较到元素值相同(即m=x)时返回元素m的下标,如果比较到最后都没有找到,则返回-1。
缺点:是当n 很大时,平均查找长度较大,效率低;
优点:是对表中数据元素的存储没有要求。另外,对于线性链表,只能进行顺序查找。 算法实现
最基础的遍历无序列表的查找算法
时间复杂度O(n)
python版本
def sequential_search(lis, key):
length = len(lis)
for i in range(length):
if lis[i] == key:
return i
else:
return False
if __name__ == '__main__':
LIST = [1, 5, 8, 123, 22, 54, 7, 99, 300, 222]
result = sequential_search(LIST, 123)
print(result)
Java版本
import java.util.Scanner;
/*
* 顺序查找
*/
public class SequelSearch {
public static void main(String[] arg) {
int[] a={4,6,2,8,1,9,0,3};
Scanner input=new Scanner(System.in);
System.out.println("请输入你要查找的数:");
//存放控制台输入的语句
int num=input.nextInt();
//调用searc()方法,将返回值保存在result中
int result=search(a, num);
if(result==-1){
System.out.println("你输入的数不存在与数组中。");
}
else
System.out.println("你输入的数字存在,在数组中的位置是第:"+(result+1)+"个");
}
//顺序排序算法
public static int search(int[] a, int num) {
for(int i = 0; i < a.length; i++) {
if(a[i] == num){//如果数据存在
return i;//返回数据所在的下标,也就是位置
}
}
return -1;//不存在的话返回-1
}
}
二分查找
二分查找(Binary Search),是一种在有序数组中查找某一特定元素的查找算法。
查找过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则查找过程结束;
如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。
如果在某一步骤数组为空,则代表找不到。
这种查找算法每一次比较都使查找范围缩小一半。
算法描述
给予一个包含 个带值元素的数组A
1、 令 L为0 , R为 n-1
2、 如果L>R,则搜索以失败告终
3、 令 m (中间值元素)为 ⌊(L+R)/2⌋
4、 如果 AmT,令 R为 m - 1 并回到步骤二 复杂度分析
时间复杂度:折半搜索每次把搜索区域减少一半,时间复杂度为 O(logn)
空间复杂度:O(1)
算法实现
python实现
def binary_search(lis, key):
low = 0
high = len(lis) - 1
time = 0
while low < high:
time += 1
mid = int((low + high) / 2)
if key < lis[mid]:
high = mid - 1
elif key > lis[mid]:
low = mid + 1
else:
# 打印折半的次数
print("times: %s" % time)
return mid
print("times: %s" % time)
return False
if __name__ == '__main__':
LIST = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
result = binary_search(LIST, 99)
print(result)
Java实现
/**
* 二分查找(折半查找),它是一种效率较高的查找方法
* 二分查找要求:1.必须采用顺序存储结构
* 2.必须按关键字大小有序排列
*
* @param array 有序数组 *
* @param searchKey 查找元素 *
* @return searchKey的数组下标,没找到返回-1
*/
public static int binarySearch(int[] array, int searchKey) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int middle = (low + high) / 2;
if (searchKey == array[middle]) {
return middle;
} else if (searchKey < array[middle]) {
high = middle - 1;
} else {
low = middle + 1;
}
}
return -1;
}
插值查找
算法简介 插值查找是根据要查找的关键字key与查找表中最大最小记录的关键字比较后的 查找方法,其核心就在于插值的计算公式 (key-a[low])/(a[high]-a[low])*(high-low)。
时间复杂度o(logn)但对于表长较大而关键字分布比较均匀的查找表来说,效率较高。
算法思想:
基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。
当然,差值查找也属于有序查找。
注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
复杂度分析
时间复杂性:如果元素均匀分布,则O(log log n)),在最坏的情况下可能需要O(n)。
空间复杂度:O(1)。
算法实现
python版本
def binary_search(lis, key):
low = 0
high = len(lis) - 1
time = 0
while low < high:
time += 1
# 计算mid值是插值算法的核心代码
mid = low + int((high - low) * (key - lis[low])/(lis[high] - lis[low]))
print("mid=%s, low=%s, high=%s" % (mid, low, high))
if key < lis[mid]:
high = mid - 1
elif key > lis[mid]:
low = mid + 1
else:
# 打印查找的次数
print("times: %s" % time)
return mid
print("times: %s" % time)
return False
if __name__ == '__main__':
LIST = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
result = binary_search(LIST, 444)
print(result)
Java版本
package com.hotusm.algorithm.insertvaluesearch;
import java.util.Arrays;
public class InsertValueSearch {
public static boolean search(int[] arr,int key,int left,int right){
while(left<right){
int middle=left+(right-left)*((key-arr[left])/(arr[right]-arr[left]));
if(arr[middle]==key){
return true;
}
if(key<arr[middle]){
right=middle-1;
}else{
left=middle+1;
}
}
return false;
}
public static void main(String[] args) {
int[] arr=new int[]{2,3,45,1234};
Arrays.sort(arr);
System.out.println(search(arr, 45, 0, arr.length-1));
}
}
斐波那契查找
算法简介 斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、····,
在数学上,斐波那契被递归方法定义:F(1)=1,F(2)=1,F(n)=f(n-1)+F(n-2) (n>=2)。
该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)。
算法描述
斐波那契查找就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为Fn,完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。 3
复杂度分析
最坏情况下,时间复杂度为O(log2n),且其期望复杂度也为O(log2n)。 算法实现
python 版本
def fibonacci_search(lis, key):
# 需要一个现成的斐波那契列表。其最大元素的值必须超过查找表中元素个数的数值。
F = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
233, 377, 610, 987, 1597, 2584, 4181, 6765,
10946, 17711, 28657, 46368]
low = 0
high = len(lis) - 1
# 为了使得查找表满足斐波那契特性,在表的最后添加几个同样的值
# 这个值是原查找表的最后那个元素的值
# 添加的个数由F[k]-1-high决定
k = 0
while high > F[k]-1:
k += 1
print(k)
i = high
while F[k]-1 > i:
lis.append(lis[high])
i += 1
print(lis)
# 算法主逻辑。time用于展示循环的次数。
time = 0
while low <= high:
time += 1
# 为了防止F列表下标溢出,设置if和else
if k < 2:
mid = low
else:
mid = low + F[k-1]-1
print("low=%s, mid=%s, high=%s" % (low, mid, high))
if key < lis[mid]:
high = mid - 1
k -= 1
elif key > lis[mid]:
low = mid + 1
k -= 2
else:
if mid <= high:
# 打印查找的次数
print("times: %s" % time)
return mid
else:
print("times: %s" % time)
return high
print("times: %s" % time)
return False
if __name__ == '__main__':
LIST = [1, 5, 7, 8, 22, 54, 99, 123, 200, 222, 444]
result = fibonacci_search(LIST, 444)
print(result)
树表查找
1、二叉树查找算法。 算法简介 二叉查找树是先对待查找的数据进行生成树,确保树的左分支的值小于右分支的值,然后在就行和每个节点的父节点比较大小,查找最适合的范围。 这个算法的查找效率很高,但是如果使用这种查找方法要首先创建树。
算法思想 二叉查找树(BinarySearch Tree)或者是一棵空树,或者是具有下列性质的二叉树: 1)若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 2)若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 3)任意节点的左、右子树也分别为二叉查找树。 二叉查找树性质:对二叉查找树进行中序遍历,即可得到有序的数列。
复杂度分析 它和二分查找一样,插入和查找的时间复杂度均为O(logn),但是在最坏的情况下仍然会有O(n)的时间复杂度。原因在于插入和删除元素的时候,树没有保持平衡。
python实现
class BSTNode:
"""
定义一个二叉树节点类。
以讨论算法为主,忽略了一些诸如对数据类型进行判断的问题。
"""
def __init__(self, data, left=None, right=None):
"""
初始化
:param data: 节点储存的数据
:param left: 节点左子树
:param right: 节点右子树
"""
self.data