JAVA 数据结构与算法(四)—— 哈希算法

一、哈希算法

1、哈希表概述

(1)哈希表介绍

  • 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
  • 哈希表hashtable(key,value) 就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。

(2)哈希表结构
哈希表结构图

图中左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。

2、哈希算法示例

(1)创建一个Student类

/*Student类,代表一个Student*/
public class Student {
    public int id;
    public  String name;
    /*指向下一个节点的引用,默认为空*/
    public Student nextStudent;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

(2)创建SchoolLinkedList类,代表一条链表,链表中存放Student对象

/*SchoolLinkedList类,表示链表*/
public class SchoolLinkedList {
    /*头指针,执行第一个Student,所以直接指向Student*/
    private Student headStudent;

    /*添加student到School:假定id时自增长,
        所以添加的时候会添加到链表的末尾*/
    public void addStudent(Student student){
        /*如果要添加的是第一个student*/
        if(headStudent == null){
            headStudent = student;
            return;
        }
        /*如果要添加的不是第一个Student,则定位到链表的末尾,
            将student添加到末尾位置*/
        Student tempStudent = headStudent;
        while (true){
            /*判断链表是否到最后*/
            if(tempStudent.nextStudent == null){
                break;
            }
            /*没有到链表的最后就继续向后定位*/
            tempStudent = tempStudent.nextStudent;
        }
        /*将student添加到链表最后*/
        tempStudent.nextStudent = student;
    }
    /* 遍历链表中的信息*/
    public void show(int num){
        if(headStudent == null ){
            System.out.println("第"+ num +"链表的信息为空");
            return;
        }

        System.out.println("第"+ num +"链表中的信息为:");
        Student student = headStudent;
        while(true){
            System.out.println("        id = "+ student.id +"  name = " + student.name);
            if(student.nextStudent == null){
                break;
            }
            student = student.nextStudent;
        }
    }

    /*查找student*/
    public Student search(int id){
        if(headStudent == null){
            return null;
        }

        Student tempStudent = headStudent;
        while(true){
            /*链表为空直接返回null*/
            if(tempStudent.id == id){
                break;
            }
            /*没有查找到就退出*/
            if(tempStudent.nextStudent == null){
                tempStudent = null;
                break;
            }
            tempStudent = tempStudent.nextStudent;
        }
        return tempStudent;
    }
}

(3)创建哈希表,用于管理多条链表

/*哈希表,用于管理多条链表*/
public class HashTable {
    private SchoolLinkedList[]  schoolLinkedListArray;
    /*size表示共有多少链表*/
    private int size;

    /*构造器*/
    public HashTable(int size) {
        this.size = size;
        /*初始化*/
        schoolLinkedListArray = new SchoolLinkedList[size];
        /*初始化每一个链表*/
        for (int i = 0; i < size; i++){
            schoolLinkedListArray[i] = new SchoolLinkedList();
        }
    }

    /*添加student*/
    public void add(Student student){
        /*根据student的id判断该student应该添加到那个链表*/
        int schoolLinkedListNum = (student.id) % size;
        /*添加到对应链表*/
        schoolLinkedListArray[schoolLinkedListNum].addStudent(student);
    }

    /*遍历所有的链表即哈希表*/
    public void showHashTable(){
        for (int i = 0; i < size; i++) {
            schoolLinkedListArray[i].show(i);
        }
    }

    /*查找student*/
    public void searchById(int id){
        int schoolLinkedListNum = id % size;
        Student student = schoolLinkedListArray[schoolLinkedListNum].search(id);
        if(student != null){
            System.out.println("在第" + schoolLinkedListNum + "条链表中找到id为" + id + "的student");
        }else{
            System.out.println("没有找到该student");
        }
    }
}

(4)测试类

public class HashTableTest {
    public static void main(String[] args) {
        /*创建哈希表*/
        HashTable hashTable = new HashTable(7);

        /**/
        int num ;
        Scanner scanner = new Scanner(System.in);
        while(true){
            System.out.println("\n请选择相应的操作: 1:添加student  2:显示student  3:查找student  4:退出系统");

            num = scanner.nextInt();
            switch (num){
                case 1:
                    /*输入要添加的student信息*/
                    System.out.print("请输入id:");
                    int id = scanner.nextInt();
                    System.out.print("请输入名字:");
                    String name = scanner.next();

                    /*添加student*/
                    Student student = new Student(id, name);
                    hashTable.add(student);
                    break;
                case 2:
                    hashTable.showHashTable();
                    break;
                case 3:
                    System.out.println("请输入要查找的student的id:");
                    id = scanner.nextInt();
                    hashTable.searchById(id);
                    break;
                case 4:
                    scanner.close();
                    System.out.println("系统退出");
                    System.exit(0);
                    break;
                default:
                    break;
            }
        }
    }
}

(5)测试结果

请选择相应的操作: 1:添加student  2:显示student  3:查找student  4:退出系统
1
请输入id:0
请输入名字:张三

请选择相应的操作: 1:添加student  2:显示student  3:查找student  4:退出系统
1
请输入id:5
请输入名字:李四

请选择相应的操作: 1:添加student  2:显示student  3:查找student  4:退出系统
1
请输入id:12
请输入名字:王五

请选择相应的操作: 1:添加student  2:显示student  3:查找student  4:退出系统
20链表中的信息为:
        id = 0  name = 张三
第1链表的信息为空
第2链表的信息为空
第3链表的信息为空
第4链表的信息为空
第5链表中的信息为:
        id = 5  name = 李四
        id = 12  name = 王五
第6链表的信息为空

请选择相应的操作: 1:添加student  2:显示student  3:查找student  4:退出系统
3
请输入要查找的student的id:
5
在第5条链表中找到id为5的student

请选择相应的操作: 1:添加student  2:显示student  3:查找student  4:退出系统
3
请输入要查找的student的id:
10
没有找到该student


请选择相应的操作: 1:添加student  2:显示student  3:查找student  4:退出系统
4
系统退出

3、哈希算法的应用场景

(1)全加密
日常用户密码加密通常使用的都是 md5、sha等哈希函数,因为不可逆,而且微小的区别加密之后的结果差距很大,所以安全性更好。

(2)唯一标识
比如 URL 字段或者图片字段要求不能重复,这个时候就可以通过对相应字段值做 md5 处理,将数据统一为 32 位长度从数据库索引构建和查询角度效果更好,此外,还可以对文件之类的二进制数据做 md5 处理,作为唯一标识,这样判定重复文件的时候更快捷。

(3)数据校验
比如从网上下载的很多文件(尤其是P2P站点资源),都会包含一个 MD5 值,用于校验下载数据的完整性,避免数据在中途被劫持篡改。

(4)散列函数
前面已经提到,PHP 中的 md5、sha1、hash 等函数都是基于哈希算法计算散列值

(5)负载均衡
对于同一个客户端上的请求,尤其是已登录用户的请求,需要将其会话请求都路由到同一台机器,以保证数据的一致性,这可以借助哈希算法来实现,通过用户 ID 尾号对总机器数取模(取多少位可以根据机器数定),将结果值作为机器编号。

(6)分布式缓存
分布式缓存和其他机器或数据库的分布式不一样,因为每台机器存放的缓存数据不一致,每当缓存机器扩容时,需要对缓存存放机器进行重新索引(或者部分重新索引),这里应用到的也是哈希算法的思想。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章