前言
查找在实际的工作中不可谓不重要,从大量的数据中找到自己需要的数据肯定不能是数数一样一个一个来,有效的查找算法可以在实际运用中大大加快工作效率。
本人知识水平有限,文章表述可能存在些许不足,欢迎评论区指出相互讨论学习。
查找算法按照表的功能分为静态查找和动态查找(查找的表是否存在插入和删除),按照表内元素的顺序分为有序查找和无序查找。
文章开头先贴出项目的代码结构:
public class MySeek {
public static void main(String[] args) {
int[] list1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] list2 = { 6, 3, 2, 8, 5, 9, 0, 1, 7, 4 };
writeSeek(list1, 9, true);
writeSeek(list2, 3, false);
}
//打印排序结果
public static void writeSeek(int[] li, int count, boolean isOrder) {
System.out.print("原序列:[ ");
for (int i : li) {
System.out.print(i + " ");
}
System.out.println("]");
tree(li, count);
}
}
树表查找
二叉树查找
原理
二叉查找树的原理就是树中的每一个结点,它的左子结点必定小于它,它的右子结点必定大于它。根据这个原理,就可以对一棵二叉查找树进行查找。
在实现二叉树查找的时候我还对树的遍历打印进行了一定的分析和优化,可以去看【算法】Java线性表转化为二叉树与二叉树打印(优化思路过程)。
执行流程
因为需要在二叉树中进行查找,就需要先定义一个二叉树的结构。
// 二叉查找树结构
private static class TreeNode {
private int value;
private TreeNode left;
private TreeNode right;
//省略get、set与构造方法
}
由于查找的是一个线性表,第一步就是要将线性表转化为二叉树。
以下代码实现的就是以第一个元素为树的根节点,剩下元素一个个从根节点开始放入二叉树内。
/*
* 将顺序表转化为二叉查找树
* head 根节点
* li 线性表
* key 准备插入二叉树的元素位置
*/
public static void makeTree(TreeNode head, int[] li, int key) {
// 递归结束条件
if (key >= li.length) {
return;
}
// 复制头结点向下遍历
TreeNode node = head;
// 准备插入的子结点
TreeNode newNode = new TreeNode(li[key]);
while (node != null) {
if (node.value > newNode.value) {
// 左子树
// 若为空则存放,不为空则继续向下
if (node.left == null) {
node.setLeft(newNode);
break;
}
node = node.left;
} else {
// 右子树
// 若为空则存放,不为空则继续向下
if (node.right == null) {
node.setRight(newNode);
break;
}
node = node.right;
}
}
// 递归下一个元素
makeTree(head, li, key + 1);
}
当构建好一棵二叉树后,查找就变得十分简单了。
代码实现
/*
* 二叉树查找 时间复杂度:O(logn)~O(n)
*/
public static void tree(int[] li, int count) {
// 构建树
TreeNode head = new TreeNode(li[0]);
makeTree(head, li, 1);
while (head != null) {
if (head.getValue() == count) {
System.out.println("二叉树查找:查找成功,序列中存在" + count);
return;
} else if (head.getValue() > count) {
head = head.getLeft();
} else {
head = head.getRight();
}
}
System.out.println("二叉树查找:查找失败,序列中不存在" + count);
}
分块查找
原理
像是排序算法中的桶排序结合了计数排序和插入排序一样,分块查找结合了顺序查找和对分查找。
这里要引入一个索引表的概念,整个索引表的每个索引都是有序的,小索引中的每个元素必定小于大索引中的每个元素,而索引内的元素可以无序。
宏观上使用顺序查找或者对分查找确定待查找值key可能存在的块,块内使用顺序查找遍历得到结果。
执行流程
1、遍历一遍线性表,将每个元素丢进它应在的块里。 这里我的操作是block[list[i]/3].add(list[i])。
{ 6, 3, 2, 8, 5, 9, 0, 1, 7, 4 } ——> {2,0,1},{3,5,4},{6,8,7},{9}
2、对key可能存在的块进行查找。 也就是找到block[key/3].
key=5 ——> {3,5,4}
代码实现
// 分块查找
public static void block(int[] li, int count) {
ArrayList<ArrayList<Integer>> b = new ArrayList<ArrayList<Integer>>();
// 初始化块
for (int i = 0; i < 4; i++) {
b.add(new ArrayList<Integer>());
}
// 放入块
for (int i : li) {
b.get(i / 3).add(i);
}
// 查找块
int[] blockList = new int[b.get(count / 3).size()];
for (int i = 0; i < blockList.length; i++) {
blockList[i] = b.get(count / 3).get(i);
}
System.out.print("分块查找:——>");
//这里有个缺陷就是顺序查找找到的位置是块中的位置
order(blockList, count);
}
哈希查找
原理
以上的方法都是基于比较实现的,但是比较的效率肯定不如直接获得。
所以基于映射的哈希表在时间复杂度无限接近于O(1),每个值k都能存储在哈希表的f(k)中,在查找时只需对查找的count进行处理,查找f(count)的位置即可。
哈希表的每个位置也不是只有一个元素的,当一个元素位于哈希表同一位置时就叫做哈希冲突,一般解决哈希冲突的方法有开放定址法、再哈希法、链地址法、建立一个公共溢出区。这些知识与哈希查找无太大关联,在此不过多赘述。
执行流程
1、按照一定的处理方法将序列中的元素放入哈希表中。
2、将待查找元素count按照相同的处理方法查找在哈希表中的位置。
3、若位置上的元素不为count则使用与哈希表相同的解决冲突方法继续查找。
4、重复步骤3直到查找到count或者空地址结束。
后记
对于树表查找还有2-3树查找与红黑树查找等,但是这些实现过程已经不仅仅是基本的查找算法思想了,更多是对树的构建进行深度剖析,绝不是简单的几行话可以讲明白的。
有关的文章也有很多,待作者整理好能用自己的语言和代码表达清晰时,再另开一篇分享。
相关文章:
【算法】Java实现常用查找算法一(顺序查找、对分查找、插值查找、斐波那契查找)
【算法】Java实现常用排序算法一(冒泡排序、选择排序、插入排序、堆排序、快速排序)
【算法】Java实现常用排序算法二(希尔排序、归并排序、计数排序、桶排序、基数排序)
【算法】Java线性表转化为二叉树与二叉树打印(优化思路过程)