查找与搜索算法

二分查找

public class BinarySearch {

  public static void main(String[] args) {
    BinarySearch binarySearch = new BinarySearch();
    int[] datas = { 1, 23, 43, 56, 67, 69, 75, 81, 94 };
    System.out.println(binarySearch.binarySearch(datas, 75));
  }

  public int binarySearch(int[] datas, int target) {
    int low = 0, high = datas.length - 1, mid;
    while (low <= high) {
      mid = low + (high - low) / 2;
      // 相等直接返回
      if (datas[mid] == target) {
        return mid;
      } else if (datas[mid] > target) {
        // 在左边一半
        high = mid - 1;
      } else {
        // 在右边一半
        low = mid + 1;
      }
    }
    return -1;
  }
}

寻找左侧边界的二分搜索

int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; // 注意
    
    while (left < right) { // 注意
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid; // 注意
        }
    }
    // target 比所有数都大
	if (left == nums.length) return -1;
	// 类似之前算法的处理方式
	return nums[left] == target ? left : -1;
}

两数之和

如果有一个整数数组 nums 和一个目标值 target,如何在该数组中找出和为目标值的那两个整数,并返回他们的数组下标,比如下面的例子:

nums[] = { 5, 12, 43, 34, 6, 23}
targrt = 35

import java.util.HashMap;
import java.util.Map;

public class TwoSum {

  public static void main(String[] args) {
    int[] results = twoSum(new int[] { 5, 12, 43, 34, 6, 23 }, 35);
    System.out.println(results[0] + " , " + results[1]);
  }

  public static int[] twoSum(int[] nums, int target) {
    int[] result = new int[2];
    if (nums == null || nums.length == 0) {
      return result;
    }
    // 存储元素以及索引
    Map<Integer, Integer> numSet = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
      // 是否包含差值
      if (numSet.containsKey(target - nums[i])) {
        result[0] = numSet.get(target - nums[i]);
        result[1] = i;
        return result;
      } else {
        // 不包含则直接放进入 hash 表中
        numSet.put(nums[i], i);
      }
    }
    return result;
  }
}

二维数组中的查找

public class FindNum {

  public static void main(String[] args) {
    int[][] array = { { 1, 4, 6, 28 }, { 2, 7, 32, 34 }, { 10, 11, 67, 79 } };
    System.out.println(find(32, array));
  }

  public static boolean find(int target, int[][] array) {
    int size = array.length;
    int length = array[0].length;
    int i, j;
    for (i = size - 1, j = 0; i >= 0 && i < size && j >= 0 && j < length;) {
      // 相等,直接返回
      if (array[i][j] == target) {
        return true;
      }
      // 比当前数大,往右边移动
      if (array[i][j] < target) {
        j++;
        continue;
      }
      // 比当前数小,往上边移动
      if (array[i][j] > target) {
        i--;
        continue;
      }
    }
    return false;
  }
}

朋友圈

现在假设有 n 个人,并且有 m 对好友关系,如果甲和乙是朋友,并且甲和丙也是朋友,那么我们可以理解为乙和丙的朋友圈可以合并,也就是属于一个圈子(朋友圈)的人。然后我们需要判断里面到底有多少个朋友圈?一个人自己,不与其他人产生任何联系,也算是一个朋友圈。

这道题可以使用典型的并查集来解,何为并查集,并查集是一种树形结构,用来处理一些不相交集合的合并和查询问题。一般就是相关联的元素,会组成一棵树,而不相关联的表现则是森林。

首先我们开辟出 n + 1 大小的数组,第一个人存储在 r[1](我们不使用 r[0]),全部初始化为 -1。

我们每一个朋友圈都找一个领导者(根源),然后把他们都合并起来,其他的元素里面存储的值是领导者(根源)的索引下标,领导的数值不断累加和它有关联的朋友所在的 -1。

遍历完关系之后,查找数组里面,有多少个小于 0 的值,就有多少个朋友圈。

public class FriendShip {

  public static void main(String[] args) {
    int[][] relationShips = {
      { 1, 2 },
      { 1, 3 },
      { 2, 5 },
      { 3, 5 },
      { 4, 6 },
    };
    int result = getNums(6, relationShips);
    System.out.println(result);
  }

  public static int getNums(int n, int relationShips[][]) {
    int count = 0;
    int[] results = new int[n + 1];
    for (int i = 0; i <= n; i++) {
      results[i] = -1;
    }
    // 合并朋友圈
    for (int i = 0; i < relationShips.length; i++) {
      unionFriends(relationShips[i][0], relationShips[i][1], results);
    }

    // 计算朋友圈个数
    for (int i = 1; i < n + 1; i++) {
      if (results[i] < 0) count++;
    }
    return count;
  }

  public static void unionFriends(int p1, int p2, int[] results) {
    // 获取自己的朋友圈的根源
    int root1 = getRoot(p1, results);
    // 获取自己的朋友圈的根源
    int root2 = getRoot(p2, results);
    // 不相等则合并
    if (root1 != root2) {
      // 圈子的领导者的值不断累加,越来越小
      results[root1] = results[root1] + results[root2];
      // 其他节点存储的值是领导者的索引
      results[root2] = root1;
    }
  }

  public static int getRoot(int p, int[] results) {
    // 最终的根应该小于 0
    while (results[p] >= 0) {
      p = results[p];
    }
    return p;
  }
}

二叉搜索树的查找

class TreeNode {
  // 节点的值
  int val;
  // 左节点的引用
  TreeNode left;
  // 右节点的引用
  TreeNode right;

  // 构造方法
  TreeNode(int x) {
    val = x;
  }
}

public class SearchTree {

  public static void main(String[] args) {
    TreeNode result = searchNode(4, initTree());
    System.out.println(result.val);
  }

  public static TreeNode searchNode(int target, TreeNode node) {
    if (node == null) {
      return null;
    }
    int res = target - node.val;
    if (res == 0) {
      return node;
    } else if (res > 0 && node.right != null) {
      // 查找右子树
      return searchNode(target, node.right);
    } else if (res < 0 && node.left != null) {
      // 查找左子树
      return searchNode(target, node.left);
    } else {
      return null;
    }
  }

  // 构造
  public static TreeNode initTree() {
    TreeNode root = new TreeNode(5);
    root.left = new TreeNode(3);
    root.right = new TreeNode(7);
    root.left.left = new TreeNode(1);
    root.left.right = new TreeNode(4);
    root.right.left = new TreeNode(6);
    return root;
  }
}

在这里插入图片描述

public class KNode {

  public static void main(String[] args) {
    TreeNode root = new TreeNode(5);
    root.left = new TreeNode(3);
    root.right = new TreeNode(7);
    root.left.left = new TreeNode(1);
    root.left.right = new TreeNode(4);
    root.right.left = new TreeNode(6);
    System.out.println(kNode(root, 4).val);
  }

  static TreeNode kNode(TreeNode pRoot, int k) {
    if (pRoot == null) {
      return pRoot;
    } else {
      // 用 k 去减左子树
      int left = getNum(pRoot.left, k);
      // 如果差值为 1,则说明当前节点就是第 k 个
      if (left == 1) {
        return pRoot;
      } else if (left < 1) {
        // 小于 1 说明在左子树,递归左子树
        return kNode(pRoot.left, k);
      } else {
        // 小于 1 说明在右子树,递归右子树
        return kNode(pRoot.right, left - 1);
      }
    }
  }

  static int getNum(TreeNode root, int k) {
    // 为空则直接放回
    if (root == null) {
      return k;
    }
    // 返回减掉剩下的数
    int left = getNum(root.left, k);
    // 减去当前节点 1 个
    left--;
    // 递归右子树
    return getNum(root.right, left);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值