Java SE《巩固篇》——查找

查找功能是数据处理的一个基本功能。数据查找并不复杂,但是如何实现数据又快又好地查找呢?

顺序查找

顺序查找是在一个已知无(或有序)序队列中找出与给定关键字相同的数的具体位置。
【算法原理】
让关键字与队列中的数从最后一个开始逐个比较,直到找出与给定关键字相同的数为止。
【算法描述】

public static int ordersearch(int[] arr,int des){
    for(int i=0;i<=arr.length-1;i++){
    if(des==arr[i])
        return i;
    }
    return -1;
}

二分查找

二分查找法是对一组有序的数字中进行查找,传递相应的数据,进行比较查找到与原数据相同的数据,查找到了返回对应的数组下标,没有找到返回-1;
【算法描述】
1. 递归方式

public static int binSearch(int srcArray[], int start, int end, int key) {  
    int mid = (end - start) / 2 + start;  
    if (srcArray[mid] == key) {  
        return mid;  
    }  
    if (start >= end) {  
        return -1;  
    } else if (key > srcArray[mid]) {  
        return binSearch(srcArray, mid + 1, end, key);  
    } else if (key < srcArray[mid]) {  
        return binSearch(srcArray, start, mid - 1, key);  
    }  
    return -1;  
} 


2. 非递归方式

public static int binSearch(int srcArray[], int key) {  
    int mid;  
    int start = 0;  
    int end = srcArray.length - 1;  
    while (start <= end) {  
        mid = (end - start) / 2 + start;  
        if (key < srcArray[mid]) {  
            end = mid - 1;  
        } else if (key > srcArray[mid]) {  
            start = mid + 1;  
        } else {  
            return mid;  
        }  
    }  
    return -1;  
} 

插值查找

插值查找比较灵活,并不是简单的从中间进行的,它是根据我们需要查询的值的渐进进行搜索的。插值查找的不同点在于每一次并不是从中间切分,而是根据离所求值的距离进行搜索的。它是二分查找的变形。

【算法描述】

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;
}

二叉树查找

二叉查找树(Binary Search Tree),又被称为二叉搜索树。它是特殊的二叉树:对于二叉树,假设x为二叉树中的任意一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。那么,这棵树就是二叉查找树。

【如图】
这里写图片描述

【二叉查找树特点】
1. 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2. 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3. 任意节点的左、右子树也分别为二叉查找树。
4. 没有键值相等的节点(no duplicate nodes)。

【二叉查找树节点定义】

public class BSTNode<T extends Comparable<T>> {
    T key;                // 关键字(键值)
    BSTNode<T> left;      // 左孩子
    BSTNode<T> right;     // 右孩子
    BSTNode<T> parent;    // 父结点
    public BSTNode(T key, BSTNode<T> parent, BSTNode<T> left, BSTNode<T> right) {
        this.key = key;
        this.parent = parent;
        this.left = left;
        this.right = right;
    }
}
public class BSTree<T extends Comparable<T>> {
    private BSTNode<T> mRoot;    // 根结点
    ......
}
BSTree是二叉树,它保护了二叉树的根节点mRoot;mRoot是BSTNode类型,而BSTNode是二叉查找树的节点,它是BSTree的内部类。
BSTNode包含二叉查找树的几个基本信息:
(01) key    -- 它是关键字,是用来对二叉查找树的节点进行排序的。
(02) left   -- 它指向当前节点的左孩子。
(03) right  -- 它指向当前节点的右孩子。
(04) parent -- 它指向当前节点的父结点。

【二叉树遍历】

若二叉树非空,则执行以下操作
1. 前序遍历
(01) 访问根结点
(02) 先序遍历左子树
(03) 先序遍历右子树
2. 中序遍历
(01) 中序遍历左子树
(02) 访问根结点
(03) 中序遍历右子树
3. 后序遍历
(01) 后序遍历左子树
(02) 后序遍历右子树
(03) 访问根结点

【如图所示】
这里写图片描述

对于上面的二叉树而言:
(01) 前序遍历结果: 3 1 2 5 4 6
(02) 中序遍历结果: 1 2 3 4 5 6 
(03) 后序遍历结果: 2 1 4 6 5 3

【算法描述】

//查找"二叉树x"中键值为key的节点
private BSTNode<T> search(BSTNode<T> x, T key) {
    if (x==null)
        return x;
    int cmp = key.compareTo(x.key);
    if (cmp < 0)
        return search(x.left, key);
    else if (cmp > 0)
        return search(x.right, key);
    else
        return x;
}
public BSTNode<T> search(T key) {
    return search(mRoot, key);
}

哈希查找

哈希查找是通过计算数据元素的存储地址进行查找的一种方法。哈希查找的本质是先将数据映射成它的哈希值。哈希查找的核心是构造一个哈希函数,它将原来直观、整洁的数据映射为看上去似乎是随机的一些整数。

哈希查找的操作步骤:
1) 用给定的哈希函数构造哈希表
2) 根据选择的冲突处理方法解决地址冲突
3) 在哈希表的基础上执行哈希查找
建立哈希表的步骤:
1) step1取数据元素的关键字key,计算其哈希函数值(地址)。若该地址对应的存储空间还没有被占用,则将该元素存入;否则执行step2解决冲突。
2) step2根据选择的冲突处理方法,计算关键字key的下一个存储地址。若下一个存储地址仍被占用,则继续执行step2,直到找到能用的存储地址为止。
常用做哈希的方法:
第一种:"直接定址法"
很容易理解,key=Value+C;这个"C"是常量。Value+C其实就是一个简单的哈希函数。
第二种:"除法取余法"
很容易理解,key=value%C;解释同上。
第三种:"数字分析法"
比如有一组value1=112233,value2=112633,value3=119033,针对这样的数我们分析数中间两个数比较波动,其他数不变。那么我们取key的值就可以是key1=22,key2=26,key3=90。
第四种:"平方取中法"
见名知意。
第五种:"折叠法"
比如value=135790,要求key2位数的散列值。那么我们将value变为13+57+90=160,然后去掉高位"1",此时key=60。这样做的目的就是key与value的每一位数都相关,来做到"散列地址"尽可能分散的目地。
影响哈希查找效率的一个重要因素是哈希函数本身。当两个不同的数据元素的哈希值相同时,就会发生冲突。为减少发生冲突的可能性,哈希函数应该将数据尽可能分散地映射到哈希表的每一个表项中。
解决冲突的方法有以下两种:
(1)开放地址法  
如果两个数据元素的哈希值相同,则在哈希表中为后插入的数据元素另外选择一个表项。当程序查找哈希表时,如果没有在第一个对应的哈希表项中找到符合查找要求的数据元素,程序就会继续往后查找,直到找到一个符合查找要求的数据元素,或者遇到一个空的表项。  
(2)链地址法
将哈希值相同的数据元素存放在一个链表中,在查找哈希表的过程中,当查找到这个链表时,必须采用线性查找方法。

【算法描述】
实现哈希函数为"除法取余法",解决冲突为"开放地址线性探测法"

public static int searchHash(int[] hash, int hashLength, int key) 
{  
    // 哈希函数  
    int hashAddress = key % hashLength;  
    // 指定hashAdrress对应值存在但不是关键值,则用开放寻址法解决  
    while (hash[hashAddress] != 0 && hash[hashAddress] != key) {  
        hashAddress = (++hashAddress) % hashLength;  
    }  
    // 查找到了开放单元,表示查找失败  
    if (hash[hashAddress] == 0)  
        return -1;  
    return hashAddress;  
}  
public static void insertHash(int[] hash, int hashLength, int data) 
{  
    // 哈希函数  
    int hashAddress = data % hashLength;  
    // 如果key存在,则说明已经被别人占用,此时必须解决冲突  
    while (hash[hashAddress] != 0) {  
        // 用开放寻址法找到  
        hashAddress = (++hashAddress) % hashLength;  
    }  
    // 将data存入字典中  
    hash[hashAddress] = data;  
} 
public static void main(String[] args) {  
    //“除法取余法”  
    int hashLength = 13;  
    int [] array  = { 13, 29, 27, 28, 26, 30, 38 };  
    //哈希表长度  
    int[] hash = new int[hashLength];  
    //创建hash  
    for (int i = 0; i < array.length; i++)  
    {  
        insertHash(hash, hashLength, array[i]);  
    }        
    int result = searchHash(hash,hashLength, 29);  
    if (result != -1)  
        System.out.println("已经在数组中找到,索引位置为:" + result);  
    else  
        System.out.println("没有此原始");  
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值