顺序查找和二分查找----java实现

一、基于无序链表的顺序查找

没啥讲的,就是顺序找。

/**
 * 顺序查找(基于无序链表)
 * 未命中和命中都需要N次比较
 * 命中最坏需要N次比较
 * @author MaoLin Wang
 * @date 2020/3/118:01
 */
public class SequentialSearchST<Key, Value> {
    /**
     * 首节点
     */
    private Node first;
    private int size;


    private class Node {
        private Key key;
        private Value value;
        private Node next;

        public Node(Key key, Value value, Node next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    /**
     * 根据key查询对应的值,一个个往下遍历直到找到相等的key,返回对应的值,否则返回null
     * @param key
     * @return
     */
    public Value get(Key key) {
        for (Node x = first; x != null; x = x.next) {

            if (key.equals(x.key)) {
                return x.value;
            }
        }
        return null;
    }

    /**
     * 加入一个元素
     * @param key
     * @param value
     */
    public void put(Key key, Value value) {
        for (Node x = first; x != null; x = x.next) {
            //key已存在,更新对应的值
            if (key.equals(x.key)) {
                x.value = value;
                return;
            }
        }
        //key不存在,新添加一个节点
        first = new Node(key, value, first);
        size++;
    }
    public boolean isEmpty() {
        return size == 0;
    }

    private int size() {
        return size;
    }
    public boolean contains(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to contains() is null");
        return get(key) != null;
    }

    /**
     * 删除key对应的节点
     * @param key
     */
    public void delete(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to delete() is null");
        first = delete(first, key);
    }

    /**
     * 递归查找,直到找到相等的key,正常删除链表节点
     * @param x
     * @param key
     * @return
     */
    private Node delete(Node x, Key key) {
        if (x == null) return null;
        if (key.equals(x.key)) {
            size--;
            return x.next;
        }
        x.next = delete(x.next, key);
        return x;
    }
    public Iterable<Key> keys()  {
        Queue<Key> queue=new Queue<>();
        while (first!=null){
            queue.enqueue(first.key);
            first=first.next;
        }
        return queue;
    }
}

优点:
适用小型问题
缺点:
对大型符号表很慢

二、基于有序数组的二分查找

查找很简单,先让目标值与中值比较,如果比中值小,则在左半边继续查找,如果比中值大,则继续要右半边查找。
这里定义了rank()方法,返回比目标key小的key的数量

 /**
     * 返回小于给定键key的key的数量
     * 1.找到给定key,则返回对应的mid坐标
     * 2.找不到给定key,返回left指针,此时left指向大于等于给定key的最小位置
     * 两种情况返回的都是小鱼给定key的key的数量
     * @param key
     * @return
     */
    public int rank(Key key){
        int left=0,right=size-1;
        while (left<=right){
            int mid=left+(right-left)/2;
            int result=keys[mid].compareTo(key);
            if (result<0){
                left=mid+1;
            }else if (result > 0){
                right=mid-1;
            }else {
                return mid;
            }
        }
        return left;
    }

基本数据结构如下:
使用泛型Key保存键,泛型Value保存值

public class BinarySearchST<Key extends Comparable<Key>,Value> {
    private Key[] keys;
    private Value[] values;
    private int size;

    public BinarySearchST(int size) {
        keys= (Key[]) new Comparable[size];
        values= (Value[]) new Comparable[size];
    }
}

查找操作:

  public Value get(Key key){
        if (isEmpty()){
            return null;
        }
        int i=rank(key);
        //如果key存在,且i<size,则keys[i]一定等于key,返回对应的value
        if (i<size && keys[i].compareTo(key)==0){
            return values[i];
        }else {
            return null;
        }
    }

添加一对数据

 public void put(Key key,Value value){
        if (key==null){
            return;
        }
        //value等于null,默认不存储null,删除对应的key-value
        if (value==null){
            delete(key);
            return;
        }
        int i=rank(key);
        if (i<size && keys[i].compareTo(key)==0){
            //存在key,则更新值
            values[i]=value;
        }else {
            if (size == keys.length) resize(2*keys.length);

            //不存在,则将从i到size的所有元素向后移动一个位置,再将新的值加入到位置i
            for (int j = size; j > i; j--) {
                keys[j]=keys[j-1];
                values[j]=values[j-1];
            }
            keys[i]=key;
            values[i]=value;
            size++;
        }
    }

删除一对数据:

public void delete(Key key){
        if (key==null)throw new IllegalArgumentException("key为空");
        if (isEmpty()) return;
		//待删除key的位置
        int i=rank(key);
        if (i<size && keys[i].compareTo(key)==0){
        	//如果key存在,则将i+1到size-1的元素向左移动一位
            for (int j = i; j < size-1 ; j++) {
                keys[j]=keys[j+1];
                values[j]=values[j+1];
            }
            //置最后一位为null
            size--;
            keys[size]=null;
            values[size]=null;

            return ;
        }
    }

其他完整代码:

/**
 * N个键的有序数组最多需要N+1次比较
 * @author MaoLin Wang
 * @date 2020/3/118:43
 */
public class BinarySearchST<Key extends Comparable<Key>,Value> {
    private Key[] keys;
    private Value[] values;
    private int size;

    public BinarySearchST(int size) {
        keys= (Key[]) new Comparable[size];
        values= (Value[]) new Comparable[size];
    }
    public int Size(){
        return size;
    }

    public Value get(Key key){
        if (isEmpty()){
            return null;
        }
        int i=rank(key);
        if (i<size && keys[i].compareTo(key)==0){
            return values[i];
        }else {
            return null;
        }
    }

    public void put(Key key,Value value){
        if (key==null){
            return;
        }
        if (value==null){
            delete(key);
            return;
        }
        int i=rank(key);
        if (i<size && keys[i].compareTo(key)==0){
            //存在key,则更新值
            values[i]=value;
        }else {
            if (size == keys.length) resize(2*keys.length);

            //不存在,则将从i到size的所有元素向后移动一个位置,再将新的值加入到位置i
            for (int j = size; j > i; j--) {
                keys[j]=keys[j-1];
                values[j]=values[j-1];
            }
            keys[i]=key;
            values[i]=value;
            size++;
        }
    }

    /**
     * 返回小于给定键key的key的数量
     * @param key
     * @return
     */
    public int rank(Key key){
        int left=0,right=size-1;
        while (left<=right){
            int mid=left+(right-left)/2;
            int result=keys[mid].compareTo(key);
            if (result<0){
                left=mid+1;
            }else if (result > 0){
                right=mid-1;
            }else {
                return mid;
            }
        }
        return left;
    }
    public boolean isEmpty(){
        return size==0;
    }

    public  Key minKey(){
        return keys[0];
    }
    public Key maxKey(){
        return keys[size-1];
    }
    public Key selectKey(int k){
        return keys[k];
    }

    /**
     * 向上取整  找出大于等于该键的最小键
     * @param key
     * @return
     */
    public Key ceiling(Key key){
        int i=rank(key);
        return keys[i];
    }

    /**
     * 向下取整 找出小于等于该键的最小键
     * @param key
     * @return
     */
    public Key floor(Key key){
        if (key==null)throw new IllegalArgumentException("key为空");
        int i=rank(key);
        if (i<size && keys[i].compareTo(key)==0){
            return keys[i];
        }else if (i==0){
            return null;
        }else {
            return keys[i-1];
        }
    }

    public void delete(Key key){
        if (key==null)throw new IllegalArgumentException("key为空");
        if (isEmpty()) return;

        int i=rank(key);
        if (i<size && keys[i].compareTo(key)==0){
            for (int j = i; j < size-1 ; j++) {
                keys[j]=keys[j+1];
                values[j]=values[j+1];
            }
            size--;
            keys[size]=null;
            values[size]=null;

            return ;
        }
    }

    private void resize(int capacity) {
        Key[]   tempKey = (Key[])   new Comparable[capacity];
        Value[] tempValue = (Value[]) new Object[capacity];
        for (int i = 0; i < size; i++) {
            tempKey[i] = keys[i];
            tempValue[i] = values[i];
        }
        values = tempValue;
        keys = tempKey;
    }

    public void delMin(){
        delete(minKey());
    }
    public void delMax(){
        delete(maxKey());
    }
    public Iterable<Key> keys(Key lo,Key hi){
        Queue<Key>queue=new Queue<>();
        int right = rank(hi);
        for (int i = rank(lo); i < right; i++) {
            queue.enqueue(keys[i]);
        }
        if (contains(hi)){
            queue.enqueue(keys[right]);
        }
        return queue;
    }
    public boolean contains(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to contains() is null");
        return get(key) != null;
    }

    public static void main(String[] args) {
        BinarySearchST<String,Integer> binarySearchST=new BinarySearchST<>(5);

        binarySearchST.put("a",3);
        binarySearchST.put("d",2);
        binarySearchST.put("c",1);
        binarySearchST.put("b",0);
        binarySearchST.put("e",4);
        System.out.println(binarySearchST.size);
        binarySearchST.delete("b");
        String s = binarySearchST.selectKey(3);
        System.out.println(s);

    }
}

二分查找平均情况下的插入成本为 lgN,最坏情况下为lgN,一般情况比顺序查找快的多
但是在键是随机的情况下,构造一个有序数组所需要访问数组的次数是数组长度的平方级别。
因此最坏情况下,插入的成本为2N

优点:
最优的查找效率和控件需求,能够进行有序性相关的操作
缺点:
插入操作慢

发布了75 篇原创文章 · 获赞 13 · 访问量 8369

猜你喜欢

转载自blog.csdn.net/weixin_43696529/article/details/104686373