二分查找
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);
}
}