您当前的位置: 首页 >  搜索

庄小焱

暂无认证

  • 2浏览

    0关注

    805博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

算法训练——剑指offer(搜索算法)

庄小焱 发布时间:2022-01-31 18:48:13 ,浏览量:2

摘要

一、搜索算法原理与解题方法 1.1 二分法

1.2 十大排序算法

1.3 二叉树的查询算法

二、搜索算法练习题目 2.1 数字在升序数组中次数

数字在升序数组中出现的次数_牛客题霸_牛客网

package 查询算法;

import org.junit.Test;

import java.util.HashMap;

/**
 * @Classname JZ53数字在升序数组中出现的次数
 * @Description TODO
 * @Date 2022/2/3 13:59
 * @Created by xjl
 */
public class JZ53数字在升序数组中出现的次数 {
    public int GetNumberOfK(int[] array, int k) {
        HashMap map = new HashMap();
        for (int i : array) {
            if (!map.containsKey(i)) {
                map.put(i, 1);
            } else {
                map.put(i, map.get(i) + 1);
            }
        }
        return map.get(k);
    }

    public int GetNumberOfK2(int[] array, int k) {
        int count = 0;
        for (int i : array) {
            if (i == k) {
                count++;
            }
        }
        return count;
    }

    /**
     * @description 二分法  看见有序,肯定就是二分查找了,算法比较简单,不多说,值得一提的是,不要拘泥于递归,要会循环写法。
     * @param: array
     * @param: k
     * @date: 2022/2/3 14:08
     * @return: int
     * @author: xjl
     */
    public int GetNumberOfK3(int[] array, int k) {
        int length = array.length;
        if (length == 0) {
            return 0;
        }
        int firstK = getFirstK(array, k, 0, length - 1);
        int lastK = getLastK(array, k, 0, length - 1);
        if (firstK != -1 && lastK != -1) {
            return lastK - firstK + 1;
        }
        return 0;
    }

    private int getFirstK(int[] array, int k, int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = (start + end) >> 1;//做云运算就是除以2
        if (array[mid] > k) {
            return getFirstK(array, k, start, mid - 1);
        } else if (array[mid] < k) {
            return getFirstK(array, k, mid + 1, end);
        } else if (mid - 1 >= 0 && array[mid - 1] == k) {
            return getFirstK(array, k, start, mid - 1);
        } else {
            return mid;
        }
    }

    //循环写法
    private int getLastK(int[] array, int k, int start, int end) {
        int length = array.length;
        int mid = (start + end) >> 1;
        while (start  k) {
                end = mid - 1;
            } else if (array[mid] < k) {
                start = mid + 1;
            } else if (mid + 1 < length && array[mid + 1] == k) {
                start = mid + 1;
            } else {
                return mid;
            }
            mid = (start + end) >> 1;
        }
        return -1;
    }

    @Test
    public void test() {
        int[] array = {1, 2, 3, 3, 3, 3, 4, 5};
        int k = 3;
        int i = GetNumberOfK2(array, k);
        System.out.println(i);

    }
}
2.2 二维数组的查找

二维数组中的查找_牛客题霸_牛客网

package 查询算法;

/**
 * @Classname JZ4二维数组的中查找
 * @Description TODO
 * @Date 2022/2/3 16:12
 * @Created by xjl
 */
public class JZ4二维数组的中查找 {
    /**
     * @description 可以采用差分到每一个数组上 进行二分法
     * @param: target
     * @param: array
     * @date: 2022/2/3 16:40
     * @return: boolean
     * @author: xjl
     */
    public boolean Find(int target, int[][] array) {
        // 判断数组是否为空
        int len = array.length;
        int row = array[0].length;
        if (len == 0 || row == 0) {
            return false;
        }
        for (int[] arr : array) {
            if (findtarget(arr, target)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @description 每一个数数组采用二分法
     * @param: arr
     * @param: target
     * @date: 2022/2/3 16:34
     * @return: boolean
     * @author: xjl
     */
    private boolean findtarget(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;
        while (left > 1;
            if (arr[mid] == target) {
                return true;
            } else if (target < arr[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return false;
    }

    /**
     * @description 也是可以直接暴力的方式
     * 也可以采用:
     * 由于行列递增,可以得出:
     * a.在一列中的某个数字,其上的数字都比它小
     * b.在一行中的某个数字,其右的数字都比它大
     * 搜索流程:
     * a.首先从数组左下角搜索.
     * b.如果当前数字大于target,那么查找往上移一位,如果当前数字小于target,那么查找往右移一位。
     * c.查找到target,返回true; 如果越界,返回false;
     * @param: target
     * @param: array
     * @date: 2022/2/3 16:40
     * @return: boolean
     * @author: xjl
     */
    public boolean Find2(int target, int[][] array) {
        // 判断数组是否为空
        int len = array.length;//行
        int wide = array[0].length;//列
        if (len == 0 || wide == 0) {
            return false;
        }

        int left = 0;
        int down = len - 1;

        while (left < wide && down >= 0) {
            int temp = array[down][left];
            if (temp == target) {
                return true;
            } else if (target > temp) {
                left++;
            } else {
                down--;
            }
        }
        return false;
    }

    /**
     * @description 解题思路: 利用数组行列递增特性。
     * 主要思路:一维的二分查找可以舍弃一半的查找范围,二维的二分可以舍弃左上部分或者右下部分1/4的查找范围。
     * @param: target
     * @param: array
     * @date: 2022/2/3 16:54
     * @return: boolean
     * @author: xjl
     */
    public boolean Find3(int target, int[][] array) {
        // 判断数组是否为空
        int len = array.length;//行
        int wide = array[0].length;//列
        if (len == 0 || wide == 0) {
            return false;
        }
        return double_binary(array, 0, array.length, 0, array[0].length, target);
    }

    private boolean double_binary(int[][] arr, int x1, int x2, int y1, int y2, int target) {
        if (x1 == x2 || y1 == y2) return false;
        int xmid = (x1 + x2) >> 1, ymid = (y1 + y2) >> 1;
        int num = arr[xmid][ymid];
        if (num == target) return true;
        if (num > target) {
            if (double_binary(arr, x1, xmid, y1, y2, target)) return true;
            if (double_binary(arr, xmid, x2, y1, ymid, target)) return true;
        } else {
            if (double_binary(arr, xmid + 1, x2, y1, y2, target)) return true;
            if (double_binary(arr, x1, xmid + 1, ymid + 1, y2, target)) return true;
        }
        return false;
    }
}
2.3 旋转的最小的数字

旋转数组的最小数字_牛客题霸_牛客网

package 查询算法;

import org.junit.Test;

/**
 * @Classname JZ11旋转数组的最小数字
 * @Description TODO
 * @Date 2022/2/3 17:05
 * @Created by xjl
 */
public class JZ11旋转数组的最小数字 {
    /**
     * @description 可以采用的是遍历的思想 复杂度还是o(n)
     * @param: array
     * @date: 2022/2/3 17:06
     * @return: int
     * @author: xjl
     */
    public int minNumberInarray(int[] array) {
        int min = array[0];

        for (int i = 1; i < array.length; i++) {
            if (array[i] < array[i - 1]) {
                min = Math.min(min, array[i]);
                break;
            }
        }
        return min;
    }

    /**
     * @description 空间复杂度:O(1),时间复杂度:O(logn)
     * 这种二分查找难就难在,arr[mid]跟谁比.
     * 我们的目的是:当进行一次比较时,一定能够确定答案在mid的某一侧。一次比较为 arr[mid]跟谁比的问题。
     * 一般的比较原则有:
     * 

* 如果有目标值target,那么直接让arr[mid] 和 target 比较即可。 * 如果没有目标值,一般可以考虑 端点 *

* 这里我们把target 看作是右端点,来进行分析,那就要分析以下三种情况,看是否可以达到上述的目标。 *

* 情况1,arr[mid] > target:4 5 6 1 2 3 * arr[mid] 为 6, target为右端点 3, arr[mid] > target, 说明[first ... mid] 都是 >= target 的,因为原始数组是非递减,所以可以确定答案为 [mid+1...last]区间,所以 first = mid + 1 * 情况2,arr[mid] < target:5 6 1 2 3 4 * arr[mid] 为 1, target为右端点 4, arr[mid] < target, 说明答案肯定不在[mid+1...last],但是arr[mid] 有可能是答案,所以答案在[first, mid]区间,所以last = mid; * 情况3,arr[mid] == target: * 如果是 1 0 1 1 1, arr[mid] = target = 1, 显然答案在左边 * 如果是 1 1 1 0 1, arr[mid] = target = 1, 显然答案在右边 * 所以这种情况,不能确定答案在左边还是右边,那么就让last = last - 1;慢慢缩少区间,同时也不会错过答案。 * @param: array * @date: 2022/2/3 17:16 * @return: int * @author: xjl */ public int minNumberInarray2(int[] array) { if (array.length == 0) return 0; int first = 0; int last = array.length - 1; while (first < last) { // 最后剩下一个元素,即为答案 if (array[first] < array[last]) { // 提前退出 return array[first]; } int mid = (first + last) >> 1; if (array[mid] > array[last]) { // 情况1 first = mid + 1; } else if (array[mid] < array[last]) { //情况2 last = mid; } else { // 情况3 --last; } } return array[first]; } public int minNumberInarray3(int[] array) { if (array.length == 0) { return 0; } int left = 0; int right = array.length - 1; while (left < right) { if (array[left] < array[right]) { return array[left]; } int mid = (left + right) >> 1; if (array[mid] > array[right]) { left=mid+1; } else if (array[mid] < array[right]) { right=mid; } else { --right; } } return array[left]; } @Test public void test() { int[] array = {3, 100, 200, 3}; int i = minNumberInarray(array); System.out.println(i); } }

2.4 字符串的排列

字符串的排列_牛客题霸_牛客网字符串的排列_牛客题霸_牛客网

package 查询算法;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * @Classname JZ38字符串的排列
 * @Description TODO
 * @Date 2022/2/3 18:00
 * @Created by xjl
 */
public class JZ38字符串的排列 {

    public ArrayList Permutation(String str) {
        ArrayList list = new ArrayList();
        if (str != null && str.length() > 0) {
            dfs(str.toCharArray(), 0, list);
            Collections.sort(list);
        }
        return list;
    }

    private void dfs(char[] chars, int i, ArrayList list) {
        if (i == chars.length - 1) {
            list.add(String.valueOf(chars));
        } else {
            Set charSet = new HashSet();
            for (int j = i; j < chars.length; ++j) {
                if (j == i || !charSet.contains(chars[j])) {
                    charSet.add(chars[j]);
                    swap(chars, i, j);
                    dfs(chars, i + 1, list);
                    swap(chars, j, i);
                }
            }
        }
    }

    private void swap(char[] cs, int i, int j) {
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }

    public ArrayList Permutation2(String str) {
        ArrayList list = new ArrayList();
        if (str.length() == 0) {
            return list;
        }
        boolean[] vis = new boolean[str.length()];
        StringBuilder sb=new StringBuilder();
        dfs1(str, 0, sb, list, vis);
        //去重复
        ArrayList ans=new ArrayList(new HashSet(list));
        return ans;
    }

    private void dfs1(String str, int index, StringBuilder sb, ArrayList list, boolean[] vis) {
        if (index == str.length()) {
            list.add(sb.toString());
            return;
        }
        for (int i = 0; i < str.length(); i++) {
            if (!vis[i]) {
                vis[i]=true;
                sb.append(str.charAt(i));
                dfs1(str,index+1,sb,list,vis);
                sb.deleteCharAt(sb.length()-1);
                vis[i]=false;
            }
        }
    }


    @Test
    public void test(){
        ArrayList list = Permutation2("aab");
        for (String s:list){
            System.out.print(s+" ");
        }
    }
}
2.5 数字序列中某一位数字

数字序列中某一位的数字_牛客题霸_牛客网

解题思路

思路:数学
先观察数字规律
小于10,1~9,9个数字,9位
小于100,10~99,90个数字,180位
小于1000,100~999,900个数字,2700位 

各个区间的下限上限是[0,10),[10, 100),[100,1000)...位数是1,2,3...
从第1个区间的上限开始进行比较,如果大于上限,将上下限*10,将n=n-(上限-下限)*位数 直至找到n所在的区间
找到区间后,n/位数 找到所在的数字,然后n%位数,找到数字的第几位数字 

public int findNthDigit (int n) {
       int digitCont = 1;//位数
        int start=1;//区间下限
        long count=9l;//区间位数
        while(n > count){
            n-=count;
            digitCont+=1;
            start*=10;
            count=Integer.toUnsignedLong(start*digitCont*9);
        }
        //num表示n所在的数字
        int num = (n-1) / digitCont + start;
        String s=num+"";
        //r表示n所在数字中的位置
        int r = (n-1)%digitCont;
        return Integer.parseInt(s.substring(r,r+1));
    }
博文参考

关注
打赏
1657692713
查看更多评论
立即登录/注册

微信扫码登录

0.0395s