14.Java实现数组链表哈希表

哈希表

哈希表又称散列表,是根据关键字key来加速访问的数据结构。通过一个关键字key通过一个映射函数到达表中一个位置访问记录,从而加快查询速度。而映射函数成为散列函数,存储记录的表成为散列表。

  • 自实现一个简单哈希表采用数组+链表的形式
  • 每一个数组维护了一个链表,链表中保存了具体数据
  • 哈希算法采用了简单的取模算法
示意图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NY349I1U-1595558207909)(C:\Users\denglw\AppData\Roaming\Typora\typora-user-images\image-20200722115546774.png)]

代码示例
  • HashTab 用户直接操作的哈希表
  • NodeLinkedList 哈希表中数组中内部维护的一个链表
  • Node 存储数据的节点 真正保存数据的类
  • HashTab中的增删改查方便本质上是通过数据key调用哈希函数确定数据在哪一个链表中从而调用链表的增删改查方法
public class HashTabDemo {

    public static void main(String[] args) {
        HashTab hashTab = new HashTab(5);
        Node node1 = new Node(1, "jack");
        Node node2 = new Node(3, "tom");
        Node node3 = new Node(8, "tomas");
        hashTab.add(node1);
        hashTab.add(node2);
        hashTab.add(node3);

        hashTab.list();
        node2.data = "tom-update";
        hashTab.update(node2);
        System.out.println("hashTab find " + hashTab.find(3).data);
        System.out.println("hashTab find " + hashTab.find(4));

        hashTab.del(node2);
        System.out.println("after delete");
        hashTab.list();
    }

}

/**
 * 面向用户使用的哈希表
 */
class HashTab {
    // maxSize用于控制hash表中链表的数量
    private final int size;
    // 具体的hash表个数
    private NodeLinkedList[] nodeLinkedLists;

    HashTab(int size) {
        this.size = size;
        nodeLinkedLists = new NodeLinkedList[size];
        // 初始化哈希表中每一个链表
        for (int i = 0; i < size; i++) {
            nodeLinkedLists[i] = new NodeLinkedList();
        }
    }

    /**
     * hash函数用于判断节点应该存在哪一个hash链上 这里采用了取模法
     *
     * @param id
     * @return
     */
    public int hash(int id) {
        return id % size;
    }

    public void add(Node node) {
        int hash = hash(node.id);
        nodeLinkedLists[hash].add(node);
    }

    public void update(Node node) {
        int hash = hash(node.id);
        nodeLinkedLists[hash].update(node);
    }

    public void del(Node node) {
        int hash = hash(node.id);
        nodeLinkedLists[hash].del(node);
    }

    public Node find(int id) {
        int hash = hash(id);
        return nodeLinkedLists[hash].find(id);
    }

    public void list() {
        for (int i = 0; i < size; i++) {
            NodeLinkedList nodeLinkedList = nodeLinkedLists[i];
            if (nodeLinkedList.isEmpty()) {
                System.out.printf("第%d链表为空\n", i);
            } else {
                System.out.printf("第%d链表数据为", i);
                nodeLinkedList.list();
            }
        }
    }


}

/**
 * 每一个数组中维护的链表
 */
class NodeLinkedList {
    Node head;

    public void add(Node node) {
        if (head == null) {
            head = node;
            return;
        }
        Node temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = node;
    }

    public void update(Node node) {
        if (head == null) {
            return;
        }
        Node temp = head;
        while (temp != null) {
            if (node.id == temp.id) {
                break;
            }
            temp = temp.next;
        }
        if (temp != null) {
            temp.data = node.data;
        }
    }

    public void del(Node node) {
        if (head == null) {
            return;
        }
        boolean isFind = false;
        Node temp = head;
        if (head.id == node.id) {
            head = head.next;
            return;
        }
        while (temp.next != null) {
            if (temp.next.id == node.id) {
                isFind = true;
                break;
            }
            temp = temp.next;
        }
        if (isFind) {
            temp.next = temp.next.next;
        }
    }

    public Node find(int id) {
        if (head == null) {
            return null;
        }
        Node temp = head;
        boolean isFind = false;
        while (temp != null) {
            if (temp.id == id) {
                isFind = true;
                break;
            }
            temp = temp.next;
        }
        if (isFind) {
            return temp;
        }
        return null;
    }

    public void list() {
        if (head == null) {
            System.out.println("linked list is empty!");
            return;
        }
        Node temp = head;
        StringBuilder builder = new StringBuilder();
        while (temp != null) {
            builder.append(temp.data.toString()).append("\t");
            temp = temp.next;
        }
        System.out.println(builder.toString());
    }

    public boolean isEmpty() {
        return head == null;
    }

    public int size() {
        if (head == null) {
            return 0;
        }
        int size = 0;
        Node temp = head;
        while (temp != null) {
            size++;
            temp = temp.next;
        }
        return size;
    }

}

/**
 * 存储数据的节点
 */
class Node {
    int id;
    Object data;
    Node next;

    public Node(int id, Object data) {
        this.id = id;
        this.data = data;
    }

}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
声明:使用这些类是使用者的自愿行为,作者对源代码的质量不提供任何形式的担保,如果使用者因使用这些类而造成的任何损失都与作者无关,作者不承担任何责任。<br><br>/*************** 这些头文件不必包含 ***************/<br>node.h: 普通链表结点<br>dnode.h: 双向循环链表结点<br>treenode.h: 二叉树结点<br>avltreenode.h: AVL 树结点<br>/**************************************************/<br><br>array.h: 安全数组,可自动增长大小(随机访问,但扩充时效率低)<br>linkedlist.h: 普通链表(可随机访问,但访问效率低)<br>dclinkedlist: 双向循环链表(不可随机访问,但插入、遍历的效率都比普通链表高)<br>hashtable.h: 哈希表(使用键值标识元素,键值一样的元素即认为相等,需重载 == 运算符并由用户定义哈希函数)<br>binstree.h: 二叉搜索树(需重载 == 和 < 运算符)<br>avltree.h: AVL 树(需重载 == 和 < 运算符)<br><br>如果要存储集合(元素不可重复)并快速查找,最佳的是 binstree.h(二叉搜索树)。<br>如果要存储二维或更高维的表格,最佳的是 hashtable.h(哈系表)。<br><br>AVL 树的插入成本非常高(删除函数也没有实现),但 AVL 的搜索效率极高,所以适用于在程序开始前初始化程序中经常要用到的集合,一般应用二叉搜索树已经足够了。<br><br>以上代码都是作者照书上改写的,并未经过严格测试,如果使用过程中发现任何问题、源代码错误或可改进的地方,非常欢迎来信与我讨论。电子邮件地址:pro_zw@lol35.com<br><br>作者会根据各位所发现的问题不断改进各类并增加新的数据结构,使其更加完善。<br><br>参考书目:<br>《数据结构-C++ 语言描述》 William Ford William Topp 著 清华大学出版社<br>《计算机程序设计艺术》 DONALD E.KNUTH 著 清华大学出版社<br>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值