五大查找算法

因为自己大三了,最近也一直在积极的准备面试,复习相关的知识点。整理这些资料,一来是方便自己以后查看,二来可以方便他人,希望自己的资料能够给别人带来一些帮助。这些知识点也是我自己在网上找的,如果有部分雷同,这不是巧合。

好了,废话不多说了。

五大查找算法:

分类平均时间复杂度特殊要求
顺序查找O(n)
二分查找O(lgn)有序数组
散列查找O(1)
分块查找O(lgn)
二叉查找O(lgn)无相同的数据
  1. 顺序查找:

顾名思义,就是从数组的头部开始顺序查找,如果找到目标元素,就返回目标元素的位置,如果没有找到,返回 -1.

public int findInSort(int [] arr,int target)
    {
        // 考虑到代码的健壮性,首先判断一下数组是否合法
        if(arr.length == 0 || arr == null)
            return -1;

        for(int i = 0; i < arr.length; i++)
        {
            if(arr[i] == target)
                return i;
        }
        return -1;
    }

  1. 二分查找

用二分法进行查找,首先要满足的条件是该数组元素是有序的,因为二分法利用的就是数组内元素的有序性。如果在数组内找到元素,返回元素的位置,否则返回 -1.

这是最简单的二分查找,即数组内如果存在该目标元素,只会存在一个,有兴趣的同学可以尝试以下变体:

  • 查找数组内第一个等于目标元素的地址
  • 查找数组内最后一个等于目标元素的地址
  • 查找数组内第一个小于等于目标元素的地址
  • 查找数组内最后一个大于等于目标元素的地址
 public int BinaryFind(int [] arr, int target)
    {
        if(arr.length == 0 || arr == null)
            return -1;

        int left = 0;
        int right = arr.length - 1;
	//循环的条件
        while(left <= right)
        {
	// 这里考虑到数值可能会溢出以及性能问题,故采用移位的写法
            int mid = left + ((right - left) >> 1);
            if(arr[mid] < target)
                left = mid + 1;
            else if(arr[mid] > target)
                right = mid - 1;
            else
                return mid;
        }
        return -1;
    }

  1. 散列查找

散列查找的关键是首先要建立散列表。这里当散列表出现冲突的时候,采用的是开放寻址法。当你需要处理的是大量数据的时候,建议采用链表法。而面对的是小数据量的时候,采用开放寻址法,更适合。

如何设计一个好的散列表呢?

  • 设计一个合适的散列函数
  • 定义装载因子的阈值,设计合理的扩容策略
  • 选择合适的散列冲突解决方法
public class hashTable {
    int [] elem;   // 原始容器,用于存储元素
    int count;     // 容器的长度

    private static final int NUllKEY = -32768;   // 用来初始化容器

    // 构造函数,初始化容器数组
    public hashTable(int count)
    {
        this.count = count;
        elem = new int [count];
        for(int i = 0; i < count; i++)
            elem[i] = NUllKEY;
    }

    // 散列函数,这里采用取余数的方法
    public int hash(int key)
    {
        return key % count;
    }

    // 构建哈希表
    public void buildHashTable(int key)
    {
        int addr = hash(key);
        while(elem[addr] != NUllKEY)
        {
            addr = (addr + 1) % count; // 开发寻址法,解决散列冲突
            if(addr == hash(key))   // 回到原点,说明容器数组已满
            {
                System.out.println("容器已满,你自己看着办!");
                return;
            }
        }
        elem[addr] = key;
    }

    // 查找
    public boolean findInHashTable(int key)
    {
        int addr = hash(key);
        while(elem[addr] != key)
        {
            addr = (addr + 1) % count;
            if(addr == hash(key) || elem[addr] == NUllKEY)
            {
                System.out.println("这里没有你要找的东西!");
                return false;
            }
        }
        System.out.println("你要找的东西是不是在容器第" + addr + "个");
        return true;
    }
}

// 测试类
public class testHash {
    public static void main(String[] args) {
        int [] arr = {12,67,46,58,36,89,45};
        hashTable demo = new hashTable(arr.length);
        for(int a: arr){
            demo.buildHashTable(a);
        }

        for(int a: arr)
        {
            System.out.println(demo.findInHashTable(a));
        }
    }
}

4.分块查找

我所理解的分块查找类似于桶排序的思想。即将原始容器分成多个块,每个块负责相应的范围大小。查找的过程:首先根据目标值选择大致的范围,即目标值可能在哪一个块里,当选定好哪一个块后,再在目标块里进行查找。所以在进行分块查找编码的时候,我们需要留意的地方是分块初始容器块的大小。

public class blockSearch {
    int [] index;  // 用来表示容器的总块
    // 实际存储元素的块,这里采用的是二维动态数组
    ArrayList<ArrayList<Integer>> list = new ArrayList<>();

    // 初始化容器
    public blockSearch(int [] index)
    {
        this.index = index; // index 的长度就是需要分的块数
        for(int i = 0; i < index.length; i++)
            list.add(new ArrayList<>());
    }

    // 选择相应的块,进行存储
    public void insertIntoBlock(int key)
    {
        int i = whichBlock(key);
        list.get(i).add(key);
    }

    // 判断当前元素属于哪一个块
    public int whichBlock(int key)
    {
        int start  = 0;
        int end = index.length - 1;
        while(start <= end)
        {
            int mid = start + ((end - start) >> 1);
            if(index[mid] <= key)
            {
                start = mid + 1;
            }
            else
            {
                end = mid - 1;
            }
        }
        return start;
    }
    
    // 顺序打印
    public void print()
    {
        for(int i = 0; i < list.size(); i++ )
        {
            ArrayList<Integer> ll = list.get(i);
            System.out.println("当前" + i + "个容器内的数据为:");
            for(int j = 0; j < ll.size(); j++)
            {
                System.out.println(ll.get(j));

            }
        }
    }

    // 查找
    public boolean findInBlock(int key)
    {
        int i = whichBlock(key);
        ArrayList<Integer> ll = list.get(i);
        for(int j = 0; j < ll.size(); j++)
        {
            if(ll.get(j) == key)
            {
                System.out.println(key + "找到了,在第" + i + "个区内");
                return true;
            }
        }
        return false;
    }
}

// 测试类
public class testBlock {
    public static void main(String[] args){
        int [] index = {10,20,30};
        blockSearch demo = new blockSearch(index);
        int [] arr = {3,6,8,12,15,18,21,23,25};
        for(int item : arr)
        {
            demo.insertIntoBlock(item);
        }
        demo.print();
        demo.findInBlock(12);
        demo.findInBlock(13);
    }
}

  1. 二叉查找

二叉查找利用的就是二叉排序树的特性。即任何一个节点的左子节点小于该节点,右子节点大于该节点。

// 节点的数据结构
public class TreeNode {
    public int data = 0;
    public TreeNode left = null;
    public TreeNode right = null;
    public TreeNode parent = null;

    public TreeNode(){}

    public TreeNode(int data)
    {
        this.data = data;
    }
}

// 树的结构
public class searchTree {
    public TreeNode root = null;

    // 创建二叉树
    public void createBinTree(int data)
    {
        if(root == null)
        {
            root = new TreeNode(data);
            System.out.println("该节点作为根节点成功插入二叉树!");
            return;
        }

        TreeNode newNode = new TreeNode(data);
        TreeNode currentNode = root;
        TreeNode pNode;  // pNode 作为 currentNode 的标记节点,走在 currentNode 的后面

        while(true)
        {
            pNode = currentNode;
            if(currentNode.data > data)
            {
                currentNode = currentNode.left;
                if(currentNode == null)
                {
                    pNode.left = newNode;
                    newNode.parent = pNode;
                    System.out.println("该节点已作为左子节插入!");
                    return;
                }
            }
            else
            {
                currentNode = currentNode.right;
                if(currentNode == null)
                {
                    pNode.right = newNode;
                    newNode.parent = pNode;
                    System.out.println("该节点已作为右子节插入");
                    return;
                }
            }
        }
    }

    // 前序遍历二叉树
    public void printAll(TreeNode root)
    {
        if(root != null)
        {
            System.out.println(root.data);
            printAll(root.left);
            printAll(root.right);
        }
    }

    // 查找二叉树
    public boolean findInBinTree(int data)
    {
        if(root.data == data){
            return true;
        }
        TreeNode current = root;
        while(current != null)
        {
            if(current.data > data)
            {
                current = current.left;
            }
            else if(current.data < data)
            {
                current = current.right;
            }
            else
            {
                System.out.println("找到"+ data + "这个玩意了,!");
                return true;
            }
        }
        return false;
    }

}
// 测试类
public class TestDemo {
    public static void main(String [] args){
        int [] arr = {10,7,8,6,11,12,5};
        searchTree demoTree = new searchTree();
        for(int data : arr)
        {
            demoTree.createBinTree(data);
        }

        demoTree.printAll(demoTree.root);

        demoTree.findInBinTree(3);
        demoTree.findInBinTree(10);
        demoTree.findInBinTree(11);
    }
}


这是博主的第一篇博客,如果有什么错误的地方,欢迎指正。同时也欢迎加博主的 QQ 一起交流。 QQ:741736214

转载于:https://my.oschina.net/happywe/blog/3040062

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值