【数据结构与算法(Java)】哈希表(取模)

1. 哈希表基本思路

  1. 建立一个数组,存放若干链表
  2. 添加节点时,通过哈希函数,确定应当放入的链表,并将节点插入该链表

2. 基本结构

2.1 SingleNode

  • int id
  • int/String data
  • SingleNode nextSingleNode

2.2 SingleLinkedList

  • SingleNode headNode
  • isEmpty()
  • addNode()
  • displayAllNodeData()
  • findNode()
  • deleteNode()

2.3 HashTable

  • Constructor
  • hashFunction()
  • addData()
  • displayAllData()
  • findNode()
  • deleteNode()

3. 代码实现

/**
 * 取模法
 */
public class HashTable {
    private HashTableSingleLinkedList[] hashTableSingleLinkedListArray; // 链表数组
    private final int arraySize; // 链表数

    /**
     * 构造器:设置数组大小,初始化装载链表的数组,并初始化数组内所有的链表
     * @param arraySize
     */
    public HashTable(int arraySize) {
        this.arraySize = arraySize;
        // 初始化链表数组
        this.hashTableSingleLinkedListArray = new HashTableSingleLinkedList[arraySize];
        // 初始化数组里的每一个链表
        for (int i = 0; i < arraySize; i++) {
            this.hashTableSingleLinkedListArray[i] = new HashTableSingleLinkedList();
        }
    }

    /**
     * 哈希函数方法 - 此处使用"取模"
     * @param id - 节点的值
     * @return - 返回所要储存的链表在链表数组内的索引值
     */
    public int hashFunction(int id) {
        return id % this.arraySize;
    }

    /**
     * 添加数据到哈希表
     * @param id - 要添加数据的id
     * @param data - 要添加的数据
     */
    public void addData(int id, String data) {
        // 1. 若id < 0: 抛出异常
        if (id < 0) {
            throw new RuntimeException("id should be larger than 0");
        }
        // 2. 根据id和data,创建新节点
        HashTableSingleNode newNode = new HashTableSingleNode(id, data);
        // 3. 调用哈希函数,添加节点到相应的链表
        this.hashTableSingleLinkedListArray[hashFunction(id)].addNode(newNode);
    }

    /**
     * 遍历并打印所有的数据
     */
    public void displayAllData(){
        for (int i = 0; i < this.arraySize; i++) {
            System.out.println("List [" + i + "]");
            if (!hashTableSingleLinkedListArray[i].isEmpty()) {
                hashTableSingleLinkedListArray[i].displayAllNodeData();
            } else {
                System.out.println("---- null ----");
            }
        }
    }

    /**
     * 根据输入的id,查找并返回节点
     * @param id - 所要查找的节点的id
     * @return - 找到,返回节点;未找到,返回null
     */
    public HashTableSingleNode findNode(int id) {
        if (id < 0) {
            throw new RuntimeException("id < 0");
        }
        return this.hashTableSingleLinkedListArray[hashFunction(id)].findNode(id);
    }

    /**
     * 从哈希表内删除节点
     * @param id - 要删除节点的id
     */
    public void deleteNode(int id){
        if (id < 0) {
            throw new RuntimeException("id < 0");
        }
        this.hashTableSingleLinkedListArray[hashFunction(id)].deleteNode(id);
    }
}

/**
 * without head
 */
class HashTableSingleLinkedList {
    private HashTableSingleNode headNode;

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

    /**
     * 添加新节点到链表(有序)
     * @param newNode - 要添加的节点
     */
    public void addNode(HashTableSingleNode newNode) {
        // 0. 若新节点为null,抛出异常
        if (newNode == null) {
            throw new RuntimeException("new node null");
        }
        // 1. 设置临时节点,分情况添加新节点
        HashTableSingleNode tempNode = headNode;
        // 2.
        // (1) 若头节点为null:替换头节点
        // (2) 若头节点id > 新节点id:添加到头节点之前
        if (headNode == null || headNode.getId() > newNode.getId()) {
            headNode = newNode;
            newNode.setNextNode(tempNode);
        }
        // 3. 若头节点id == 新节点id:抛出异常
        else if (headNode.getId() == newNode.getId()){
            throw new RuntimeException("exist");
        }
        // 4. 否则,逐个比较id值,寻找头节点后添加位置
        else {
            HashTableSingleNode currentNode = headNode;
            while (currentNode.getNextNode() != null) {
                // 4.1 若当前节点id > 新节点id:将新节点添加到当前节点之前
                if (currentNode.getNextNode().getId() > newNode.getId()) {
                    tempNode = currentNode.getNextNode();
                    currentNode.setNextNode(newNode);
                    newNode.setNextNode(tempNode);
                    return;
                }
                // 4.2 若当前节点id == 新节点id:抛出异常
                else if (currentNode.getNextNode().getId() == newNode.getId()) {
                    throw new RuntimeException("exist");
                }
                // 4.3 否则移动到下一节点,继续loop
                currentNode = currentNode.getNextNode();
            }
            // 4.4 遍历结束,添加新节点到链表末尾
            currentNode.setNextNode(newNode);
        }
    }

    /**
     * 遍历并打印哈希表的全部节点数据
     */
    public void displayAllNodeData() {
        if (headNode == null) {
            throw new RuntimeException("empty");
        }
        HashTableSingleNode currentNode = headNode;
        while (currentNode != null) {
            System.out.println(currentNode.toString());
            currentNode = currentNode.getNextNode();
        }
    }

    /**
     * 根据输入的id,查找并返回节点
     * @param id - 所要查找的节点的id
     * @return - 找到,返回节点;未找到,返回null
     */
    public HashTableSingleNode findNode(int id) {
        // 0. 若头节点为null:抛出异常
        if (headNode == null) {
            throw new RuntimeException("empty");
        }
        // 1. loop:
        // 1.1 若当前节点为 null:遍历完未找到,停止
        // 1.2 当前节点id == 输入的id:找到,停止
        HashTableSingleNode currentNode = headNode;
        while (currentNode != null && currentNode.getId() != id) {
            currentNode = currentNode.getNextNode();
        }
        // 2. 找到返回节点,未找到返回null
        return currentNode;
    }

    /**
     * 删除节点
     * @param id - 要删除节点的id
     */
    public void deleteNode(int id) {
        // 0. 若头节点为null:抛出异常
        if (headNode == null) {
            throw new RuntimeException("empty");
        }

        // 1. 设置两个节点变量:一个储存当前节点,一个储存当前节点的前一个节点(用于删除)
        HashTableSingleNode currentNode = headNode;
        HashTableSingleNode preNode = null;
        // 2. loop:若找到节点 or 遍历完未找到,停止
        while (currentNode != null) {
            // 2.1 找到,删除,停止遍历
            // (1) 若前节点为null,删除头节点(当前节点)
            // (2) 若前节点不为null,删除当前节点
            if (currentNode.getId() == id) {
                if (preNode == null) {
                    headNode = headNode.getNextNode();
                } else {
                    preNode.setNextNode(currentNode.getNextNode());
                }
                break;
            }
            // 2.2 未找到,分别移动前节点和当前节点到下一位
            preNode = currentNode;
            currentNode = currentNode.getNextNode();
        }
        // 3. 遍历结束,若当前节点为null:未找到,抛出异常
        if (currentNode == null) {
            throw new RuntimeException("not found");
        }
    }
}

class HashTableSingleNode {
    private int id;
    private String data;
    private HashTableSingleNode nextNode;

    public HashTableSingleNode(int id, String data) {
        this.id = id;
        this.data = data;
    }

    @Override
    public String toString() {
        return "HashTableSingleNode{" +
                "id=" + id +
                ", data='" + data + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public HashTableSingleNode getNextNode() {
        return nextNode;
    }

    public void setNextNode(HashTableSingleNode nextNode) {
        this.nextNode = nextNode;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值