查找算法
二分查找算法
代码实现
package com.luyi.search;
import java.util.ArrayList;
import java.util.Arrays;
/**
* 二分查找法
* 注意 使用二分查找法的前提是 数组时有序的
* @author 卢意
* @create 2020-12-09 15:50
*/
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {
1,8,10,89,1000,1000,1000,1234};
System.out.println(binarySearch(arr, 0, arr.length-1, 1000));
System.out.println(binarySearchUpgrade(arr, 0, arr.length-1, 1000));
}
// 二分查找算法
/**
*
* @param arr 数组
* @param left 查找时 左边的索引
* @param right 查找时 右边的索引
* @param findVal 要查找的值
* @return 如果找到就返回下标 没找到返回 -1
*/
public static int binarySearch(int[] arr, int left, int right, int findVal){
int mid = (left + right) / 2;
int midVal = arr[mid];
if (left > right) return -1;// 没有找到
if(findVal > midVal){
// 向右递归
return binarySearch(arr, mid+1, right, findVal);
}else if(findVal < midVal){
// 向右递归
return binarySearch(arr, left, mid, findVal);
}else {
return mid;
}
}
// 对多个相同值的查找
// 思路分析 在找到mid的时候不要马上返回 向mid的索引的左/右边扫描 将所有满足的查找值1000的所有下标 放入数组中
public static ArrayList<Integer> binarySearchUpgrade(int[] arr, int left, int right, int findVal){
ArrayList<Integer> resIndexList = new ArrayList<>();
int mid = (left + right) / 2;
int midVal = arr[mid];
if (left > right) {
resIndexList.add(-1);// 没有找到
return resIndexList;
}
if(findVal > midVal){
// 向右递归
return binarySearchUpgrade(arr, mid+1, right, findVal);
}else if(findVal < midVal){
// 向右递归
return binarySearchUpgrade(arr, left, mid, findVal);
}else {
int temp = mid;
// 向左扫描
while (arr[--temp] == midVal){
resIndexList.add(temp);
}
resIndexList.add(mid);
temp = mid;
// 向右扫描
while (arr[++temp] == midVal){
resIndexList.add(temp);
}
return resIndexList;
}
}
}
差值查找法
代码实现
package com.luyi.search;
import com.luyi.Main;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @author 卢意
* @create 2020-12-09 18:28
*/
public class InsertValueSearch {
public static void main(String[] args) {
int[] arr = new int[100];
for(int i = 1; i <= 100;i++){
arr[i-1] = i;
}
System.out.println(Arrays.toString(arr));
System.out.println(insertValueSearch(arr, 0, arr.length - 1, 100));
}
// 编写差值查找算法
/**
* 差值查找算法的前提要求也是数组要有序
* @param arr 传入的数组
* @param left 左边的索引
* @param right 右边的索引
* @param findVal 要查找的值
* @return
*/
public static int insertValueSearch(int[] arr, int left, int right, int findVal){
if(left > right || findVal < arr[0] || findVal > arr[arr.length - 1]){
// 优化查找 且防止越界
return -1;
}
// 求出mid
int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);
int midVal = arr[mid];
if(findVal > midVal) {
// 向右递归
return insertValueSearch(arr, mid + 1, right, findVal);
}else if (findVal < midVal){
// 向左递归
return insertValueSearch(arr, 0, mid-1, findVal);
}else {
return mid;
}
}
}
使用场景
斐波那契(黄金分割)查找算法
代码实现
package com.luyi.DataStructures.recursion;
import java.lang.reflect.Array;
import java.util.Arrays;
/**
* 斐波那契查找
* @author 卢意
* @create 2020-12-09 19:30
*/
public class FibonacciSearch {
public static int maxSize = 20;
public static void main(String[] args) {
int[] arr = {
1,8,10,89,1000,1234};
System.out.println(fibSearch(arr, 1234));
}
// 因为后面我们mid = low+F(k - 1)-1. 需要使用斐波那契数列, 因此我们需要先获取到斐波那契数列
// 用非递归的方法得到一个大小为20的斐波那契数列
public static int[] fbn(){
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < maxSize; i++){
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
// 非递归的方式编写斐波那契数列查找算法
/**
*
* @param a
* @param key 需要查找的值
* @return 返回对应的下标 没有则返回-1
*/
public static int fibSearch(int[] a, int key){
int low = 0;
int high = a.length -1;
int k = 0; // 表示斐波那契分割数值的下标 mid = low+F(k - 1)-1
int mid = 0;
int[] f = fbn(); // 获取斐波那契数列
// 获取斐波那契分割数值的下标
while (high > f[k]-1){
k++;
}
// 因为 f[k] 可能大于数组a 的长度 需要一个Array类 构造一个新的数组 并指向temp[]
int[] temp = Arrays.copyOf(a, f[k]); // 多出来的部分用0补起来
// 实际上需要使用a最后的数填充temp
// 举例 :
// temp:{1,8,10,89,1000,1234,0,0} =>temp:{1,8,10,89,1000,1234,1234,1234}
for (int i = high + 1;i < temp.length; i++){
temp[i] = a[high];
}
// 使用while循环查找我们的key
while (low <= high) {
mid = low + f[k-1]-1;
if (key < temp[mid]){
//说明应该向数组的前一部分查找(左边)
high = mid -1;
// 为什么是k--
// 1 全部元素 = 前面的元素 + 后边的元素
// 2 f[k] = f[k-1] + f[k-2];
// 因为前面有f[k-1]个元素,所以我们继续拆分 f[k-1] = f[k-2] + f[k-3];
// 即在f[k-1] 的前面继续查找 k--
// 即下次在循环的时候 mid = f[k-1-1] -1
k--;
}else if ( key > temp[mid]){
// 我们继续向数组的右边查找
low = mid + 1;
// 为什么是k-2
// 1 全部元素 = 前面的元素 + 后边的元素
// 2 f[k] = f[k-1] + f[k-2];
// 3 因为后面有f[k-2] 所以加足拆分 f[k -2] = f[k-3] + f[k-4];
// 4 即在f[k-2] 的前面继续查找
// 下次循环mid = f[k-1-2] -1
k -= 2;
}else {
// 找到
// 需要确定返回的是哪个下标
if (mid <= high){
return mid;
}else {
return high;
}
}
}
return -1; // 没有找到
}
}
哈希表
代码实现
package com.luyi.hashTable;
import java.util.Scanner;
/**
* @author 卢意
* @create 2020-12-10 14:52
*/
public class HashTable {
public static void main(String[] args) {
// 创建一个哈希表
HashTab hashTab = new HashTab(7);
//写一个简单的菜单
String key = "";
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.println("add: 添加雇员");
System.out.println("list: 显示雇员");
System.out.println("find: 查找雇员");
System.out.println("exit: 退出系统");
key = scanner.next();
switch (key) {
case "add":
System.out.println("输入 id");
int id = scanner.nextInt();
System.out.println("输入名字");
String name = scanner.next();
//创建 雇员
Emp emp = new Emp(id, name);
hashTab.add(emp);
break;
case "list":
hashTab.list();
break;
case "find":
System.out.println("请输入要查找的 id");
id = scanner.nextInt();
hashTab.findEmpById(id);
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}
// 定义hash表
class HashTab{
public EmployLinkedList[] employLinkedListArray;
private int size; //有多少条链表
public HashTab(int size) {
// 初始化链表
this.size = size;
this.employLinkedListArray = new EmployLinkedList[size];
// 不要忘了 分别初始化每个链表
for (int i = 0; i < size; i++){
employLinkedListArray[i] = new EmployLinkedList();
}
}
// 添加顾员
public void add (Emp emp){
// 通过员工的Id 得到该员工应该添加到哪一个链表
int empLinkedListNo = hashFun(emp.id);
// 将emp 添加到链表自己中
employLinkedListArray[empLinkedListNo].add(emp);
}
//遍历所有的链表
public void list(){
int index = 0;
for (EmployLinkedList e :employLinkedListArray){
e.list(index);
index++;
}
}
// 添加顾员
public void findEmpById(int id){
int empLinkedListNo = hashFun(id);
System.out.println(employLinkedListArray[empLinkedListNo].findEmpById(id)==null?"没有该节点哦":
employLinkedListArray[empLinkedListNo].findEmpById(id));
}
// 编写一个散列函数 使用简单的取模法
public int hashFun(int id){
return id % size;
}
}
// 表示顾员
class Emp{
public int id;
public String name;
public Emp next = null;
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
// 创建一个EmployLinkedList
class EmployLinkedList{
// 头指针 指向第一个Emp ,这个头指针是直接指向第一个Emp
private Emp head = null;
// 添加顾员到链表
// 假定添加顾员的时候 id自增 将该雇员插入链表的最后一个
public void add(Emp emp){
if (head == null){
head = emp;
return;
}
Emp temp = head;
while (temp.next != null){
temp = temp.next;
}
// 当前的temp为最后一个
temp.next = emp;
}
// 遍历连表的顾员信息
public void list(int i){
if (head == null){
System.out.println("第"+i+"条链表为空");
System.out.println();
return;
}
Emp temp = head;
System.out.print("第"+i+"条链表内的数据为: ");
while (temp != null){
System.out.print(temp+" ");
temp = temp.next;
}
System.out.println();
}
// 根据Id查找顾员
// 如过找不到返回空
public Emp findEmpById(int id) {
if (head == null){
return null;
}
Emp temp = head;
while (temp != null){
if (temp.id == id){
return temp;
}
temp = temp.next;
}
return temp;
}
}
二叉树
对数组 链表 树的比较
二叉树的概念
前中后序遍历图解
前中后序遍历实例
代码实现
package com.luyi.tree;
/**
* @author 卢意
* @create 2020-12-10 18:31
*/
public class BinaryTreeDemo {
public static void main(String[] args) {
// 先创建一棵二叉树
BinaryTree binaryTree = new BinaryTree();
// 创建需要的节点
HeroNode root = new HeroNode(1,"宋江");
HeroNode node2 = new HeroNode(2,"吴用");
HeroNode node3 = new HeroNode(3,"卢俊义");
HeroNode node4 = new HeroNode(4,"林冲");
HeroNode node5 = new HeroNode(5,"关胜");
// 我们先手动创建该二叉树 后面学习递归的方式创建二叉树
root.setLeft(node2);
root.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
binaryTree.setRoot(root);
// 测试
System.out.println("前序遍历: "); // 1 2 3 5 4
binaryTree.preOrder();
System.out.println("中序遍历: "); // 2 1 5 3 4
binaryTree.infixOrder();
System.out.println("后序遍历: "); // 2 5 4 3 1
binaryTree.postOrder();
}
}
// 定义一个二叉树 BinaryTree
class BinaryTree {
private HeroNode root;
public void setRoot(HeroNode root) {
this.root = root;
}
// 前序遍历
public void preOrder(){
if(this.root != null){
root.preOrder();
}else {
System.out.println("二叉树为空 无法遍历");
}
}
// 中序遍历
public void infixOrder(){
if(this.root != null){
root.infixOrder();
}else {
System.out.println("二叉树为空 无法遍历");
}
}
// 前序遍历
public void postOrder(){
if(this.root != null){
root.postOrder();
}else {
System.out.println("二叉树为空 无法遍历");
}
}
}
//先创建HeroNode 结点
class HeroNode {
private int no;
private String name;
private HeroNode left; // 左子树
private HeroNode right; // 右子树
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
// 前序遍历的方法
public void preOrder() {
System.out.println(this); //先输出父节点
// 递归遍历左子树
if (this.left != null){
this.left.preOrder();
}
// 递归遍历右子树
if (this.right != null){
this.right.preOrder();
}
}
// 中序遍历的方法
public void infixOrder() {
// 递归遍历左子树
if (this.left != null){
this.left.infixOrder();
}
System.out.println(this); //输出父节点
// 递归遍历右子树
if (this.right != null){
this.right.infixOrder();
}