尚硅谷 java数据结构与算法 学习笔记(二)

本文介绍了Java中的几种查找算法,包括二分查找、差值查找和斐波那契查找,并提供了相应的代码实现。此外,详细探讨了哈希表和二叉树的相关概念,如前中后序遍历、二叉树的删除、线索化二叉树及其应用。最后,提到了哈夫曼树在数据压缩和解压中的应用以及二叉排序树的创建、遍历和删除操作。
摘要由CSDN通过智能技术生成

查找算法

在这里插入图片描述

二分查找算法

在这里插入图片描述

在这里插入图片描述

代码实现

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();
		}
	
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值