6查找算法

查找算法

1二分查找

  • 二分查找时间复杂度是O(logn)
  • 如果数组元素的数量接近于整数的最大值,那么start和end下标之和,就有可能超出整型范围,造成溢出
   public static int binarySearch(int[] array,int target){
        //查找范围起点
        int start=0;
        //查找范围终点
        int end=array.length-1;
        //查找范围中位数
        int mid;
        
        while (start<=end){
            //mid=(start+end)/2 有可能溢出
            mid=start+((end-start)/2);
            if (array[mid]==target){
                return mid;
            }else if(array[mid]<target){
                start=mid+1;
            }else{
                end=mid-1;
            }
        }
        return -1;
    }

2跳表

​ 2.1概念

  • 如果链表的节点数量非常多,我们就可以抽出更多的索引层级,每一层索引的节点数量都是上一层索引的一半
  • 假设原始链表有n个节点,那么索引的层级就是log(n)-1,在每一层的访问次数是常量,因此查找节点的平均时间复杂度就是O(log(n))
  • 但相应地,增加了额外的空间开销,约等于n,也就是说优化之后的数据结构所占空间是原来的2倍

​ 2.2跳表的插入

  • 首先按照跳表查找节点的方法,找到待插入节点的前置节点(仅小于待插入节点)
  • 接下来让新插入的节点随机“晋升”,也就是成为索引节点,新节点晋升成功的几率是50%
  • 新节点在成功晋升之后,仍然有机会继续向上一层索引晋升
  • 如果超出了最高层索引的范围,我们直接让索引增加一层

​ 2.3跳表的删除

  • 首先按照跳表查找节点的方法,找到待删除的节点,按照一般链表的删除方式,把节点删除
  • 接下来我们需要把索引当中的对应节点也一一删除
  • 如果某一层索引的节点被删光了,就直接把没有节点的那一层删去就好了

​ 2.4代码实现

  • 程序中跳表采用的是双向链表,无论前后节点还是上下节点,都各有两个指针相互指向彼此
  • 程序中跳表的每一层首尾各有一个空节点,左侧的空节点是负无穷大,右侧的空节点是正无穷大

3朴素的字符串匹配算法

​ 3.1BF算法(朴素字符串匹配)

  • 暴力算法,假设主串的长度是m,模式串的长度是n,那么在这种极端情况下,BF算法的最坏时间复杂度是O(mn)
  /**
     *
     * @param str 主串
     * @param sub 子串
     * @param pos 开始匹配位置
     * @return 返回子串在主串中的开始位置
     */
    public static int BF(String str,String sub,int pos){
        //安全性校验
        if(pos < 0 || pos > str.length()) {
            return -1;
        }
        //分别给主串和子串定义指针
        int strIndex = pos;
        int subIndex = 0;
        while (strIndex < str.length() && subIndex < sub.length()) {
            if(str.charAt(strIndex) == sub.charAt(subIndex)) {
                //如果匹配上了就继续往后匹配
                strIndex++;
                subIndex++;
            } else {
                //str指针回退,并且+1从下一个开始继续匹配
                strIndex = strIndex - subIndex + 1;
                subIndex = 0;
            }
        }
        //匹配上了返回对应位置
        if(subIndex >= sub.length()) {
            return strIndex-subIndex;
        }
        //没匹配上返回-1
        return -1;
    }

​ 3.2RK算法(朴素字符串匹配的改进)

  • 第一步,我们需要生成模式串的hashcode(比如按位相加a是1...;或转换成26进制数)
  • 第二步,生成主串当中第一个等长子串的hashcode
  • 第三步,比较两个hashcode
  • 第四步:两者不同就一直重复上面2,3;如果两者相同,由于可能存在hash冲突的可能,我们还需要进一步验证
  • 第五步:逐个字符比较两个字符串
  • 注意:对于子串的哈希操作计算并不是独立的,从第二个子串开始,每一个子串的哈希操作都可以由上一个子串进行简单增量计算来得到,新hashcode=旧hashcode-第一个值hash+新值的hash
  • 总时间复杂度是O(m+n),缺点就是存在哈希冲突,如果冲突太多就退化为BF算法
 public static int rabinKarp(String str,String pattern){
     //主串长度
     int m=str.length();
     //模式串的长度
     int n=pattern.length();
     //计算模式串的hash值
     int patternCode=hash(pattern);
     //计算主串当中第一个和模式串等长的子串hash值
     int strCode=hash(str.substring(0,n));
     //用模式串的hash值和主串的局部hash值比价
     //如果匹配则进行精确比较,如果不匹配,计算主串中的相邻子串的hash值
     for (int i=0;i<(m-n+1);i++){
         if ((strCode==patternCode)&&compareString(i,str,pattern)){
             return i;
         }
         //如果不是最后一轮,更新主串从i到i+n的hash值
         if (i<(m-n)){
             strCode=nextHash(str,strCode,i,n);
         }
     }
     return -1;
 }
 
 private static int hash(String str){
     int hashcode=0;
     //这里采用最简单的hashcode计算方式
     //把a当做1,b当做2...然后按位相加
     for (int i=0;i<str.length();i++){
         hashcode+=(str.charAt(i)-'a');
     }
     return hashcode;
 }
 
 private static int nextHash(String str,int hash,int index,int n){
     hash-=(str.charAt(index)-'a');
     hash+=(str.charAt(index+n)-'a');
     return hash;
 }
 
 private static boolean compareString(int i,String str,String pattern){
     String strSub=str.substring(i,i+pattern.length());
     return strSub.equals(pattern);
 }
posted @ 2021-10-16 18:40  fao99  阅读(55)  评论(0)    收藏  举报