查找算法

本文详细介绍了数据查找的各种方法,包括线性表查找(顺序查找、折半查找)、树表查找(二叉树查找)、散列表查找等,并通过示例代码进行了解释。

1.概念

查找就是在某种数据结构形式存储的数据集合中,找出满足指定条件的结点,若查找成功,给出记录信息或者记录在表中的位置,如果查找不成功,返回空

查找的三种方式

 1.线性表的查找

 (1) 顺序查找
原理: 从表的一端开始,逐个对记录和给定的关键字进行比较,如果找到一个记录与给定的记录一样,那么查找成功,如果没有一样的那么就查找失败
时间复杂性:O(n)
优缺点:顺序查找的优点是算法简单,对表结构没有要求,不需要排序,缺点是平均查找长度较大,查找效率低
示例代码

public class OrderSearch {  
public static int orderSearch(int searchKey,int... array){  
        for(int i=0;i<array.length;i++){  
            if(array[i]==searchKey){  
                return i;  
            }  
        }  
        return -1;  

    }  
    /**测试查找结果 
     * @param args 
     */  
    public static void main(String[] args) {  
          int[] test=new int[]{1,2,29,3,95,3,5,6,7,9,12};//升序序列  
          int index=OrderSearch.orderSearch(95, test);  
          System.out.println("查找到的位置 :"+ index);    
    }  
}  

(2)折半查找
原理:假设查找表的元素存储在数组[1….n],首先将待查的key值与表中间位置上(下标为mid)的纪录的关键字进行比较,若相等,则查找成功,如果key>r[mid].key,那就说明待查记录在后半个子表r[mid+1….n],下一步应该在后半个子表中查找如果key

public class SplitFind {   
    /** 
     * @author hbliu 
     * @param arrayData 数组 
     * @param searchData 要查找的数据 
     * @param start 开始下标 
     * @param end 结束下标 
     * @return 数据在数组中下标的位置,如果数据不在数组中,返回 -1 
     * 功   能:折半查找  
     */  
    public static int splitHalf(int[] arrayData,int searchData,int start,int end){  
        int index = (start + end)/2;  
        int data = arrayData[index];  
        if(start > end ){  
            return -1;  
        }  
        if(data == searchData){  
            return index;  
        }else{  
            if(data < searchData){  
                return splitHalf(arrayData,searchData,index+1,end);  
            }else{  
                return splitHalf(arrayData,searchData,start,index-1);  
            }  
        }  
    }  

    /** 
     * @author hbliu 
     * @param args 
     */  
    public static void main(String[] args) {  
        int[] array = { 3,5,11,17,21,23,28,30,32,50};  
        System.out.println(array.length);  
        int isExist = splitHalf(array,(int)50,0,array.length - 1);  
        System.out.println("isExist : "+isExist);  
    }  

}  

2.树表的查找(动态查找)
(1)二叉树 是一种对排序和查找都很有用的特殊二叉树
二叉树的性质: 左子树一定小于它的根节点 右子树一定大于根节点
二叉树重要性质为中序遍历可以得到一个节点值递增的有序序列
查找原理:
(1)如果排序树为空,则查找失败,返回空
(2) 若二叉排序树非空,将给定的值key 与根节点进行比较
如果 key 等于,那就查找成功 返回根节点的地址
如果key 小于根节点那么就查找左子树
如果key 大于根节点,则进一步查找右节点子树
时间复杂度 O(log2n)

例子
public class BinarySortTree {  

    private Node root = null;  


    /**查找二叉排序树中是否有key值*/  
    public boolean searchBST(int key){  
        Node current = root;  
        while(current != null){  
            if(key == current.getValue())  
                return true;  
            else if(key < current.getValue())  
                current = current.getLeft();  
            else  
                current = current.getRight();  
        }  
        return false;  
    }  


    /**向二叉排序树中插入结点*/  
    public void insertBST(int key){  
        Node p = root;  
        /**记录查找结点的前一个结点*/  
        Node prev = null;  
        /**一直查找下去,直到到达满足条件的结点位置*/  
        while(p != null){  
            prev = p;  
            if(key < p.getValue())  
                p = p.getLeft();  
            else if(key > p.getValue())  
                p = p.getRight();  
            else  
                return;  
        }  
        /**prve是要安放结点的父节点,根据结点值得大小,放在相应的位置*/  
        if(root == null)  
            root = new Node(key);  
        else if(key < prev.getValue())  
            prev.setLeft(new Node(key));  
        else prev.setRight(new Node(key));  
    }  



    /** 
     * 删除二叉排序树中的结点 
     * 分为三种情况:(删除结点为*p ,其父结点为*f) 
     * (1)要删除的*p结点是叶子结点,只需要修改它的双亲结点的指针为空 
     * (2)若*p只有左子树或者只有右子树,直接让左子树/右子树代替*p 
     * (3)若*p既有左子树,又有右子树 
     *      用p左子树中最大的那个值(即最右端S)代替P,删除s,重接其左子树 
     * */  
    public void deleteBST(int key){  
        deleteBST(root, key);  
    }  
    private boolean deleteBST(Node node, int key) {  
        if(node == null) return false;  
        else{  
            if(key == node.getValue()){  
                return delete(node);  
            }  
            else if(key < node.getValue()){  
                return deleteBST(node.getLeft(), key);  
            }  
            else{  
                return deleteBST(node.getRight(), key);  
            }  
        }  
    }  

    private boolean delete(Node node) {  
        Node temp = null;  
        /**右子树空,只需要重接它的左子树 
         * 如果是叶子结点,在这里也把叶子结点删除了 
         * */  
        if(node.getRight() == null){  
            temp = node;  
            node = node.getLeft();  
        }  
        /**左子树空, 重接它的右子树*/  
        else if(node.getLeft() == null){  
            temp = node;  
            node = node.getRight();  
        }  
        /**左右子树均不为空*/  
        else{  
            temp = node;  
            Node s = node;  
            /**转向左子树,然后向右走到“尽头”*/  
            s = s.getLeft();  
            while(s.getRight() != null){  
                temp = s;  
                s = s.getRight();  
            }  
            node.setValue(s.getValue());  
            if(temp != node){  
                temp.setRight(s.getLeft());  
            }  
            else{  
                temp.setLeft(s.getLeft());  
            }  
        }  
        return true;  
    }  


    /**中序非递归遍历二叉树 
     * 获得有序序列 
     * */  
    public void nrInOrderTraverse(){  
        Stack<Node> stack = new Stack<Node>();  
        Node node = root;  
        while(node != null || !stack.isEmpty()){  
            while(node != null){  
                stack.push(node);  
                node = node.getLeft();  
            }  
            node = stack.pop();  
            System.out.println(node.getValue());  
            node = node.getRight();  
        }  
    }  

    public static void main(String[] args){  
        BinarySortTree bst = new BinarySortTree();  
        /**构建的二叉树没有相同元素*/  
        int[] num = {4,7,2,1,10,6,9,3,8,11,2, 0, -2};  
        for(int i = 0; i < num.length; i++){  
            bst.insertBST(num[i]);  
        }  
        bst.nrInOrderTraverse();  
        System.out.println(bst.searchBST(10));  
        bst.deleteBST(2);  
        bst.nrInOrderTraverse();  
    }  


    /**二叉树的结点定义*/  
    public class Node{  
        private int value;  
        private Node left;  
        private Node right;  

        public Node(){  
        }  
        public Node(Node left, Node right, int value){  
            this.left = left;  
            this.right = right;  
            this.value = value;  
        }  
        public Node(int value){  
            this(null, null, value);  
        }  

        public Node getLeft(){  
            return this.left;  
        }  
        public void setLeft(Node left){  
            this.left = left;  
        }  
        public Node getRight(){  
            return this.right;  
        }  
        public void setRight(Node right){  
            this.right = right;  
        }  
        public int getValue(){  
            return this.value;  
        }  
        public void setValue(int value){  
            this.value = value;  
        }  
    }  

}  

树的查找还分B+树的查找和B-树的查找 此处略过

散列表的查找方法
散列表的基本思想:通过对元素的关键字值进行某种运算,直接求出元素的地址,即用关键字到地址的直接转换的方法
概念:
散列函数和散列地址:在记录的存储地址p和其关键字key之间建立一个确定的对应关系H 使得p=H(key),称这个对应关系H为散列函数,P为散列地址
散列表:一个有限连续的地址空间,通常散列表的存储空间就是一个一维数组,散列地址就是数组的下标
冲突和同义词: 对不同的关键字可能得到同一个散列地址 就是 key1!=key2 但是H(key1)==H(key2)

构造散列函数的几种方法
数字分析法: 如果每个关键字的位数比散列表的地址码多,每个关键字由n位数组成,如 k1k2k3k4,那么可以从关键字中提取数字分别均匀的几位或者若干位作为散列地址
平方取中法: 一个数平方后的中间几位和数的每一位都是相关的,如果取关键字平方后的中间几位作为散列地址,那么散列地址就是随机的
折叠法:将关键字分割成位数相同的几部分,取这几部分进行舍位相加。
除留余数法: 假关键m设散列表表长,选择一个不大于m的数p 用P去除关键字 除后余数为散列地址 一般取p为表长的最大的质数。
H(key)=key%p

示例代码
package 散列表;  

import java.util.Random;  



public class HashProcess {  

    public static void main(String[] args) {  
        int[] arr = new int[10];  
        int[] hashTable = new int[arr.length*2+1];  
        Random random = new Random();  
        for(int i=0 ; i<arr.length ; i++){  

            arr[i] = random.nextInt(10)%10+10;  
        }  
        for(int a : arr){  
            InsertHash(hashTable,a);  
            System.out.print(a + " ");  
        }  

        System.out.println(SearchHash(arr[6],hashTable.length,hashTable));  

    }  



    private static boolean SearchHash(int a,int len,int[] ht) {  
        int addr = Hash(a,len);  
        while(ht[addr] != a){  
            addr = (addr+1)%len;  
            if(ht[addr] == 0 || addr == Hash(a,len)){  
                return false;  
            }  
        }  
        return true;  
    }  



    private static void InsertHash(int[] hashTable, int a) {  
        int len = hashTable.length;  
        int addr = Hash(a,len);  
        while(hashTable[addr] != 0){  
            addr = (addr+1)%len;  
        }  
        hashTable[addr] = a;  
    }  

    private static int Hash(int a,int len) {  
        return a%len;     
    }  

}  
内容概要:文章以“智能网页数据标注工具”为例,深入探讨了谷歌浏览器扩展在毕业设计中的实战应用。通过开发具备实体识别、情感分类等功能的浏览器扩展,学生能够融合前端开发、自然语言处理(NLP)、本地存储与模型推理等技术,实现高效的网页数据标注系统。文中详细解析了扩展的技术架构,涵盖Manifest V3配置、内容脚本与Service Worker协作、TensorFlow.js模型在浏览器端的轻量化部署与推理流程,并提供了核心代码实现,包括文本选择、标注工具栏动态生成、高亮显示及模型预测功能。同时展望了多模态标注、主动学习与边缘计算协同等未来发展方向。; 适合人群:具备前端开发基础、熟悉JavaScript和浏览器机制,有一定AI模型应用经验的计算机相关专业本科生或研究生,尤其适合将浏览器扩展与人工智能结合进行毕业设计的学生。; 使用场景及目标:①掌握浏览器扩展开发全流程,理解内容脚本、Service Worker与弹出页的通信机制;②实现在浏览器端运行轻量级AI模型(如NER、情感分析)的技术方案;③构建可用于真实场景的数据标注工具,提升标注效率并探索主动学习、协同标注等智能化功能。; 阅读建议:建议结合代码实例搭建开发环境,逐步实现标注功能并集成本地模型推理。重点关注模型轻量化、内存管理与DOM操作的稳定性,在实践中理解浏览器扩展的安全机制与性能优化策略。
基于Gin+GORM+Casbin+Vue.js的权限管理系统是一个采用前后端分离架构的企业级权限管理解决方案,专为软件工程和计算机科学专业的毕业设计项目开发。该系统基于Go语言构建后端服务,结合Vue.js前端框架,实现了完整的权限控制和管理功能,适用于各类需要精细化权限管理的应用场景。 系统后端采用Gin作为Web框架,提供高性能的HTTP服务;使用GORM作为ORM框架,简化数据库操作;集成Casbin实现灵活的权限控制模型。前端基于vue-element-admin模板开发,提供现代化的用户界面和交互体验。系统采用分层架构和模块化设计,确保代码的可维护性和可扩展性。 主要功能包括用户管理、角色管理、权限管理、菜单管理、操作日志等核心模块。用户管理模块支持用户信息的增删改查和状态管理;角色管理模块允许定义不同角色并分配相应权限;权限管理模块基于Casbin实现细粒度的访问控制;菜单管理模块动态生成前端导航菜单;操作日志模块记录系统关键操作,便于审计和追踪。 技术栈方面,后端使用Go语言开发,结合Gin、GORM、Casbin等成熟框架;前端使用Vue.js、Element UI等现代前端技术;数据库支持MySQL、PostgreSQL等主流关系型数据库;采用RESTful API设计规范,确保前后端通信的标准化。系统还应用了单例模式、工厂模式、依赖注入等设计模式,提升代码质量和可测试性。 该权限管理系统适用于企业管理系统、内部办公平台、多租户SaaS应用等需要复杂权限控制的场景。作为毕业设计项目,它提供了完整的源码和论文文档,帮助学生深入理解前后端分离架构、权限控制原理、现代Web开发技术等关键知识点。系统设计规范,代码结构清晰,注释完整,非常适合作为计算机相关专业的毕业设计参考或实际项目开发的基础框架。 资源包含完整的系统源码、数据库设计文档、部署说明和毕
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值