数据结构基础-散列表(Hash) 蓝桥杯详解版

package com;



/**
 * Created with IntelliJ IDEA.
我在学习蓝桥杯过程中的一点想法

主要目标是学会散列表(hash 算法)的原理与实现,
学会灵活的运用,能够不依赖于模板根据题目独立写出各类散列表。
数据结构 Hash 属于查找算法中的一部分

知识点
Hash 的概念
构造方法
冲突处理



第一步,首先我们需要创建一个散列表和一个公共溢出区。
    散列表
    公共溢出区
第二步,需要定义插入散列表函数。
    按照散列表的映射方式设计即可
    需要传入一个参数来表示放什么数据
    in(Name){
        1. 无冲突
        2. 冲突处理
    }
第三步:定义查询函数。
    isAt(){
        1. 如果散列表查询成功返回 True
        2. 不为成功返回 False
    }
第四步,定义散列表映射函数。

此处我们采用除留余数法即可,
    不了解这个方法的同学别担心,
    后面在写解题代码的时候,
    我会具体为大家介绍。

    int out(string s){
        处理字符串 s 生成对应的 Key 值
    }


第五步,编写主函数代码。
    输入 N
    循环 N 次://
        输入 word;
        先查询,有相同的单词有就设置 flag 为 1,ans = word
        没有的话,就执行插入操作
    根据 flag 决定输出什么。
如何设计实现散列函数
    在使用散列表的时候,我们有两个关键的技术问题需要解决:
        散列函数的设计,如何设计一个简单、均匀、存储利用率高的散列函数?
        冲突的处理,如何采取合适的处理冲突方法来解决冲突。
    如何设计实现散列函数
    在构建散列函数时,我们需要秉持两个原则:
        简单
            散列函数不应该有很大的计算量,否则会降低查找效率。
        均匀:
            函数值要尽量均匀散布在地址空间,这样才能保证存储空间的有效利用并减少冲突。



2. 除留余数法。

H(key)=key mod p

来个小例子:

H(key)=key mod 21



会发现产生了很多相同的 H(K),
这就是发生冲突,因为一个位置只能放一个数,
有两个值对应这里一个位置,是不可以的。

这种方法是最常用的方法,
这个方法的关键在于如何选取 P,
使得利用率较高并且冲突率较低,
一般情况下,我们会选取最接近表长且小于等于表长的最大素数。

缺点:
P 选取不当,会导致冲突率上升。

适用范围:

除留余数法是一种最简单、
也是最常用的构造散列函数的方法,
并且不要求事先知道关键码的分布。

这个方法非常常用,
我们后面题目的展开就是使用的这个方法。
在大部分的算法实现中也都是选取的这一种方式。


一共要创建两个数组
 一个是根据哈希值存储的哈希表
 另一个公共溢出区负责存储 在哈希表已经存在的值
 公共溢出区里面的数值挨个存储

 */


import java.util.Scanner;
import java.util.Stack;

public class Demo001 {
//    第一步,我们需要创建一个散列表和一个公共溢出区:
    static  final int h=999983;
    static String[] Value=new String [h];
    static String[] UpValue=new String [h];
//    这是公共溢出区数组的指针   用来指示公共溢出区一共有多少数值
    static int UpValueCount=0;

//    第二步,我们要定义散列表映射函数:
    /*
    采用除留余数法来定义散列表映射函数
    S是数值 哈希值可以用hashcod方法来转换得到S
    H(S)=(S%h+h)%h
    * */
    static int   Hx(String s) {
        return (s.hashCode()%h+h)%h;
    }

//    第三步,我们定义查询函数:
    static boolean isAt(String s){
        int n=Hx(s);
//        待查询数值的哈希值在哈希表相应位置为空  当然是没有了
        if(Value[n]==null){
            return false;
//        待查询数值的哈希值在哈希表相应位置不为空 且 内容相同 这就啥查到了
        }else if(Value[n].equals(s)){
            return true;
        }else {
//            在哈希表 为空 或者 不为空  但是相应位置的元素不同的情况下
//            开始遍历溢出区
            for(int i=0;i<UpValueCount;i++){
//                如果有相同的话元素  那就 返回 true  找到了
                if(UpValue[i].equals(s)){
                    return true;
                }
            }
//            经过遍历没有找到  那就是 没有   返回false
            return false;
        }
    }

//    第四步,定义插入散列表函数:
//      按照散列表的映射方式设计即可;
//      需要传入一个参数来表示放什么数据。
    /*



    * */
    static boolean in(String s) {
        int n=Hx(s);
//        如果哈希表这个位置是空的 所以直接存储进去
        if(Value[n]==null) {
            Value[n]=s;
            return true;
//        如果哈希表这个位置不是空的,则开始比较内容 如果内容相同 不进行插入
        } else if(Value[n].equals(s)){
            return false;
//        待插入元素在哈希表这个位置不为空且存的元素 不相同  那么 这个元素就存储到公共溢出区
        } else{
//            在公共溢出区遍历一遍  如果有相同的元素 则不进行插入  返回false 不在执行下面的函数
            for(int i=0;i<UpValueCount;i++){
                if(UpValue[i].equals(s)){
                    return false;
                }
            }
//            公共溢出区挨个插入
            UpValue[UpValueCount++]=s;
            return true;
        }
    }

//    主函数
    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        int n=in.nextInt();
        boolean flag=false;
        String ans="NO";


        for(int i=0;i<n;i++) {
            String word;
            word=in.next();
            // System.out.println(Hx(word));
//            如果 flag变成了 true 表示 有相同的元素 跳过这个循环
//            插入过程中如果遇到相同的元素将不再进行插入  跳过接下来的 过程
//            例如  asdaerty 第四个数值是重复的 erty将不再进行插入
            if(flag){
//                跳过当前循环继续下一个循环。
                continue;
            }
//          进行查询 如果有相同的
            if(isAt(word)){
                flag=true;
                ans=word;
            }else {
                in(word);
            }
        }
        System.out.println(ans);
    }
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值