因为自己大三了,最近也一直在积极的准备面试,复习相关的知识点。整理这些资料,一来是方便自己以后查看,二来可以方便他人,希望自己的资料能够给别人带来一些帮助。这些知识点也是我自己在网上找的,如果有部分雷同,这不是巧合。
好了,废话不多说了。
五大查找算法:
分类 | 平均时间复杂度 | 特殊要求 |
---|---|---|
顺序查找 | O(n) | 无 |
二分查找 | O(lgn) | 有序数组 |
散列查找 | O(1) | 无 |
分块查找 | O(lgn) | 无 |
二叉查找 | O(lgn) | 无相同的数据 |
- 顺序查找:
顾名思义,就是从数组的头部开始顺序查找,如果找到目标元素,就返回目标元素的位置,如果没有找到,返回 -1.
public int findInSort(int [] arr,int target)
{
// 考虑到代码的健壮性,首先判断一下数组是否合法
if(arr.length == 0 || arr == null)
return -1;
for(int i = 0; i < arr.length; i++)
{
if(arr[i] == target)
return i;
}
return -1;
}
- 二分查找
用二分法进行查找,首先要满足的条件是该数组元素是有序的,因为二分法利用的就是数组内元素的有序性。如果在数组内找到元素,返回元素的位置,否则返回 -1.
这是最简单的二分查找,即数组内如果存在该目标元素,只会存在一个,有兴趣的同学可以尝试以下变体:
- 查找数组内第一个等于目标元素的地址
- 查找数组内最后一个等于目标元素的地址
- 查找数组内第一个小于等于目标元素的地址
- 查找数组内最后一个大于等于目标元素的地址
public int BinaryFind(int [] arr, int target)
{
if(arr.length == 0 || arr == null)
return -1;
int left = 0;
int right = arr.length - 1;
//循环的条件
while(left <= right)
{
// 这里考虑到数值可能会溢出以及性能问题,故采用移位的写法
int mid = left + ((right - left) >> 1);
if(arr[mid] < target)
left = mid + 1;
else if(arr[mid] > target)
right = mid - 1;
else
return mid;
}
return -1;
}
- 散列查找
散列查找的关键是首先要建立散列表。这里当散列表出现冲突的时候,采用的是开放寻址法。当你需要处理的是大量数据的时候,建议采用链表法。而面对的是小数据量的时候,采用开放寻址法,更适合。
如何设计一个好的散列表呢?
- 设计一个合适的散列函数
- 定义装载因子的阈值,设计合理的扩容策略
- 选择合适的散列冲突解决方法
public class hashTable {
int [] elem; // 原始容器,用于存储元素
int count; // 容器的长度
private static final int NUllKEY = -32768; // 用来初始化容器
// 构造函数,初始化容器数组
public hashTable(int count)
{
this.count = count;
elem = new int [count];
for(int i = 0; i < count; i++)
elem[i] = NUllKEY;
}
// 散列函数,这里采用取余数的方法
public int hash(int key)
{
return key % count;
}
// 构建哈希表
public void buildHashTable(int key)
{
int addr = hash(key);
while(elem[addr] != NUllKEY)
{
addr = (addr + 1) % count; // 开发寻址法,解决散列冲突
if(addr == hash(key)) // 回到原点,说明容器数组已满
{
System.out.println("容器已满,你自己看着办!");
return;
}
}
elem[addr] = key;
}
// 查找
public boolean findInHashTable(int key)
{
int addr = hash(key);
while(elem[addr] != key)
{
addr = (addr + 1) % count;
if(addr == hash(key) || elem[addr] == NUllKEY)
{
System.out.println("这里没有你要找的东西!");
return false;
}
}
System.out.println("你要找的东西是不是在容器第" + addr + "个");
return true;
}
}
// 测试类
public class testHash {
public static void main(String[] args) {
int [] arr = {12,67,46,58,36,89,45};
hashTable demo = new hashTable(arr.length);
for(int a: arr){
demo.buildHashTable(a);
}
for(int a: arr)
{
System.out.println(demo.findInHashTable(a));
}
}
}
4.分块查找
我所理解的分块查找类似于桶排序的思想。即将原始容器分成多个块,每个块负责相应的范围大小。查找的过程:首先根据目标值选择大致的范围,即目标值可能在哪一个块里,当选定好哪一个块后,再在目标块里进行查找。所以在进行分块查找编码的时候,我们需要留意的地方是分块初始容器块的大小。
public class blockSearch {
int [] index; // 用来表示容器的总块
// 实际存储元素的块,这里采用的是二维动态数组
ArrayList<ArrayList<Integer>> list = new ArrayList<>();
// 初始化容器
public blockSearch(int [] index)
{
this.index = index; // index 的长度就是需要分的块数
for(int i = 0; i < index.length; i++)
list.add(new ArrayList<>());
}
// 选择相应的块,进行存储
public void insertIntoBlock(int key)
{
int i = whichBlock(key);
list.get(i).add(key);
}
// 判断当前元素属于哪一个块
public int whichBlock(int key)
{
int start = 0;
int end = index.length - 1;
while(start <= end)
{
int mid = start + ((end - start) >> 1);
if(index[mid] <= key)
{
start = mid + 1;
}
else
{
end = mid - 1;
}
}
return start;
}
// 顺序打印
public void print()
{
for(int i = 0; i < list.size(); i++ )
{
ArrayList<Integer> ll = list.get(i);
System.out.println("当前" + i + "个容器内的数据为:");
for(int j = 0; j < ll.size(); j++)
{
System.out.println(ll.get(j));
}
}
}
// 查找
public boolean findInBlock(int key)
{
int i = whichBlock(key);
ArrayList<Integer> ll = list.get(i);
for(int j = 0; j < ll.size(); j++)
{
if(ll.get(j) == key)
{
System.out.println(key + "找到了,在第" + i + "个区内");
return true;
}
}
return false;
}
}
// 测试类
public class testBlock {
public static void main(String[] args){
int [] index = {10,20,30};
blockSearch demo = new blockSearch(index);
int [] arr = {3,6,8,12,15,18,21,23,25};
for(int item : arr)
{
demo.insertIntoBlock(item);
}
demo.print();
demo.findInBlock(12);
demo.findInBlock(13);
}
}
- 二叉查找
二叉查找利用的就是二叉排序树的特性。即任何一个节点的左子节点小于该节点,右子节点大于该节点。
// 节点的数据结构
public class TreeNode {
public int data = 0;
public TreeNode left = null;
public TreeNode right = null;
public TreeNode parent = null;
public TreeNode(){}
public TreeNode(int data)
{
this.data = data;
}
}
// 树的结构
public class searchTree {
public TreeNode root = null;
// 创建二叉树
public void createBinTree(int data)
{
if(root == null)
{
root = new TreeNode(data);
System.out.println("该节点作为根节点成功插入二叉树!");
return;
}
TreeNode newNode = new TreeNode(data);
TreeNode currentNode = root;
TreeNode pNode; // pNode 作为 currentNode 的标记节点,走在 currentNode 的后面
while(true)
{
pNode = currentNode;
if(currentNode.data > data)
{
currentNode = currentNode.left;
if(currentNode == null)
{
pNode.left = newNode;
newNode.parent = pNode;
System.out.println("该节点已作为左子节插入!");
return;
}
}
else
{
currentNode = currentNode.right;
if(currentNode == null)
{
pNode.right = newNode;
newNode.parent = pNode;
System.out.println("该节点已作为右子节插入");
return;
}
}
}
}
// 前序遍历二叉树
public void printAll(TreeNode root)
{
if(root != null)
{
System.out.println(root.data);
printAll(root.left);
printAll(root.right);
}
}
// 查找二叉树
public boolean findInBinTree(int data)
{
if(root.data == data){
return true;
}
TreeNode current = root;
while(current != null)
{
if(current.data > data)
{
current = current.left;
}
else if(current.data < data)
{
current = current.right;
}
else
{
System.out.println("找到"+ data + "这个玩意了,!");
return true;
}
}
return false;
}
}
// 测试类
public class TestDemo {
public static void main(String [] args){
int [] arr = {10,7,8,6,11,12,5};
searchTree demoTree = new searchTree();
for(int data : arr)
{
demoTree.createBinTree(data);
}
demoTree.printAll(demoTree.root);
demoTree.findInBinTree(3);
demoTree.findInBinTree(10);
demoTree.findInBinTree(11);
}
}
这是博主的第一篇博客,如果有什么错误的地方,欢迎指正。同时也欢迎加博主的 QQ 一起交流。 QQ:741736214