查找算法、哈希表、树
开始~~~
目录
一、查找算法
1.线性查找
简单直接代码,从第一个数组开始找。
public void orderSearch(int value) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] != value) {
} else {
System.out.println("索引为:" + i);
return;
}
}
System.out.println("没有找到数据~~");
}
2.二分查找
2.1思路分析
前提是数组是升序排列的,这里考虑到数组中可能有多个重复值。
主要是通过递归:
1)获得数组arr中间值的索引mid = (left + right) / 2,并与查找值findValue做比较。
2)如果arr[mid] = findValue,则结束循环,并且比较mid左右两端的数据看是否等于findValue
3)如果arr[mid] < findValue,需要向右递归。将mid + 1作为left
4)如果arr[mid] > findValue,需要向左递归。将mid - 1作为right
5)否则没有找到
2.2代码
public void binarySearch(int value, int left, int right) {
int mid = (left + right) / 2;
if (arr[mid] == value) {
ArrayList list = new ArrayList();
list.add(mid);
int temp = mid + 1;
while (temp < arr.length) {
if (arr[temp] == value) {
list.add(temp);
}
temp += 1;
}
temp = mid - 1;
while (temp >= 0) {
if (arr[temp] == value) {
list.add(temp);
}
temp -= 1;
}
System.out.println("索引为:");
list.forEach(System.out::println);
} else if (arr[mid] < value) {
binarySearch(value, mid + 1, right);
} else if (arr[mid] > value) {
binarySearch(value, left, mid - 1);
} else {
System.out.println("没有找到~~~");
}
}
3.插值查找
3.1插值查找原理的介绍
插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找。
公式:
mid = left + (right – left) * (findVal – arr[left]) / (arr[right] – arr[left])
3.2代码
public void insertValSearch(int value, int low, int high) {
int mid = low + (high - low) * (value - arr[low]) / (arr[high] - arr[low]);
if (value > arr[arr.length - 1] || value < arr[0]){
System.out.println("没有找到~~");
}else{
if (arr[mid] == value){
System.out.println("索引为:" + mid);
}else if (value > arr[mid]){
insertValSearch(value,mid + 1,high);
}else if (value < arr[mid]){
insertValSearch(value,low,mid - 1);
}else{
System.out.println("没有找到~~");
}
}
}
4.斐波那契查找
~~
5.哈希表
一个实际的需求:
有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,住址…),当输入该员工的id时,要求查找到该员工的 所有信息.
要求: 不使用数据库,尽量节省内存,速度越快越好=>哈希表(散列)
5.1哈希表的基本介绍
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
5.2图解
5.3代码
1)添加成员、查找成员、遍历成员信息
class HashTable{
private int size;
private Manage[] manage;
public HashTable(int size){
this.size = size;
this.manage = new Manage[size];
for (int i = 0; i < size; i++) {
manage[i] = new Manage();
}
}
//添加
public void add(Employees emp){
int index = emp.getId() % size;
manage[index].add(emp);
}
//遍历
public void show(){
for (int i = 0; i < size; i++) {
System.out.println("第" + (i + 1) + "条链表:");
manage[i].show();
}
}
//查找
public void find(int id){
int index = id % size;
Employees emp = manage[index].find(id);
if (emp == null){
System.out.println("没有查到该员工信息");
}else{
System.out.println("查询的员工信息在第" + (index + 1) + "条链表中:");
System.out.println(emp);
}
}
}
//创建员工信息类
class Employees{
private int id;
private String name;
private int age;
public Employees next;
public Employees(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "employees{" +
"id=" + id +
", name=" + name +
", age=" + age +
'}';
}
}
//管理员工信息
class Manage{
private Employees head;
//添加成员信息
public void add(Employees emp){
if(head == null){
head = emp;
}else{
head.next = emp;
}
System.out.println("添加成功");
}
//显示成员信息
public void show(){
Employees temp = head;
if (head == null){
System.out.println("无员工信息");
}else{
while (temp != null){
System.out.println(temp);
temp = temp.next;
}
}
}
//查找成员信息
public Employees find(int num){
Employees temp = head;
if (head == null){
return null;
}else{
while (temp != null){
if (temp.getId() == num){
return temp;
}
temp = temp.next;
}
return null;
}
}
}
2)操作界面
HashTable list = new HashTable(7);
boolean loop = true;
Scanner scan = new Scanner(System.in);
char key;
while (loop){
System.out.println("-----------雇员操作界面-----------");
System.out.println(" a(add):添加雇员数据");
System.out.println(" s(show):显示雇员信息");
System.out.println(" f(find):查找雇员信息");
System.out.println(" e(exit):退出程序");
System.out.print("请输入操作类型:");
key = scan.next().charAt(0);
switch (key){
case 'a':
System.out.println("-----------添加员工信息-----------");
System.out.print("请输入员工编号:");
int id = scan.nextInt();
System.out.print("请输入员工姓名:");
String name = scan.next();
System.out.print("请输入员工年龄:");
int age = scan.nextInt();
Employees emp = new Employees(id, name, age);
list.add(emp);
break;
case 's':
System.out.println("-----------显示员工信息-----------");
list.show();
break;
case 'f':
System.out.println("-----------查找员工信息-----------");
System.out.print("输入要查找的员工id:");
int no = scan.nextInt();
list.find(no);
break;
case 'e':
loop = false;
System.out.println("退出成功~~~");
break;
}
}
二、二叉树
1.二叉树介绍
1.1为什么需要树这种数据结构
简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。
1)数组存储方式的分析
优点:通过下标方式访问元素,速度快。对于有序数组,还可使用二分查找提高检索速度。
缺点:如果要检索具体某个值,或者插入值(按一定顺序)会整体移动,效率较低。
2)链式存储方式的分析
优点:在一定程度上对数组存储方式有优化(比如:插入一个数值节点,只需要将插入节点,链接到链表中即可, 删除效率也很好)。
缺点:在进行检索时,效率仍然较低,比如(检索某个值,需要从头节点开始遍历) 。
3)树存储方式的分析
能提高数据存储,读取的效率, 比如利用 二叉排序树(Binary Sort Tree),既可以保证数据的检索速度,同时也可以保证数据的插入,删除,修改的速度。
1.2树的示意图
1.3二叉树的概念
1)树有很多种,每个节点最多只能有两个子节点的一种形式称为二叉树。
2)二叉树的子节点分为左节点和右节点。
3)如果该二叉树的所有叶子节点都在最后一层,并且结点总数= 2^n -1 , n 为层数,则我们称为满二叉树。
4)如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。
2.二叉树遍历
2.1遍历的说明
前序遍历: 先输出父节点,再遍历左子树和右子树
中序遍历: 先遍历左子树,再输出父节点,再遍历右子树
后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点
小结: 看输出父节点的顺序,就确定是前序,中序还是后序
2.2代码
1)前序遍历
public void preOrder(){
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
2)中序遍历
public void midOrder(){
if (this.left != null) {
this.left.midOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.midOrder();
}
}
3)后序遍历
public void lasOrder(){
if (this.left != null) {
this.left.lasOrder();
}
if (this.right != null) {
this.right.lasOrder();
}
System.out.println(this);
}
3.二叉树查找
同样也是分为前序遍历查找,中序遍历查找,后序遍历查找。
3.1代码
1)前序遍历查找
public HeroNode preSearch(int no){
if (this.no == no){
return this;
}
HeroNode node = null;
if (this.left != null){
node = this.left.preSearch(no);
}
if (node != null){
return node;
}
if (this.right != null){
node = this.right.preSearch(no);
}
return node;
}
2)中序遍历查找
public HeroNode midSearch(int no){
HeroNode node = null;
if (this.left != null){
node = this.left.midSearch(no);
}
if (this.no == no){
return this;
}
if (node != null){
return node;
}
if (this.right != null){
node = this.right.midSearch(no);
}
return node;
}
3)后序遍历查找
public HeroNode postSearch(int no){
HeroNode node = null;
if (this.left != null){
node = this.left.postSearch(no);
}
if (node != null){
return node;
}
if (this.right != null){
node = this.right.postSearch(no);
}
if (node != null){
return node;
}
if (this.no == no){
return this;
}
return node;
}
4.二叉树的删除
4.1思路分析
规定:
1)如果删除的是叶子结点,则删除该叶子结点。
2)如果删除的是非叶子结点,则删除该子树。
3)如果要删除的节点是root节点,则将整个二叉树置空即可。
4)需要通过this.left和this.right找到需要删除的节点位置。
步骤:
1)如果this.left.no = findNo或
this.right.no = findNo,则找到了要删除的节点。
2)否则依次进行左递归和右递归,遍历整个子树找到对应的no值,删除节点。
4.2代码
public void delete(int no){
if (this.left != null && this.left.no == no){
this.left = null;
return;
}
if (this.right != null && this.right.no == no){
this.right = null;
return;
}
if (this.left != null){
this.left.delete(no);
}
if (this.right != null){
this.right.delete(no);
}
}
5.顺序存储二叉树
5.1说明
数组存储方式和树的存储方式可以相互转换,即数组可以转换成树,树也可以转换成数组。
5.2特点
1)顺序二叉树通常只考虑完全二叉树
2)第n个元素的左子节点为2 * n + 1
3)第n个元素的右子节点为 2 * n + 2
4)第n个元素的父节点为 (n-1) / 2
5.3代码
//前序遍历
public void preOrder(int index) {
System.out.println(arr[index]);
if (2 * index + 1 < arr.length) {//相当于this.left != null
preOrder(2 * index + 1);//preOrder(this.left)
}
if (2 * index + 2 < arr.length) {
preOrder(2 * index + 2);
}
}
6.线索化二叉树
难难~~,通过画图实际分析可以解决
6.1问题分析
上述的二叉树中有几个节点的左右指针,并没有完全的利用上。于是引入线索二叉树。
6.2基本介绍
1)n个结点的二叉链表中含有**n+1 [公式 2n-(n-1)=n+1]**个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")。
2)线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
3)一个结点的前一个结点,称为前驱结点
4)一个结点的后一个结点,称为后继结点
6.3思路分析
1)线索化二叉树的思路分析
无论是前序、中序、还是后序线索化二叉树,都需要:
pre:表示当前结点node的前驱节点(如果node.left == null),node:表示当前节点,同时也是pre的后继结点(如果pre.right == null);
leftType 与 rightType(默认为0)表示该节点是否进行了线索化。
2)遍历线索化二叉树的思路分析
线索化后结点之间已经不是单向联系了,所以不能用递归的方式。
6.4代码
1)前序线索化及前序遍历
//前序遍历
public void preOrder(){
HeroNode node = root;
while (node != null){
System.out.println(node);
while (node.getLeftType() == 0){
node = node.getLeft();
System.out.println(node);
}
node = node.getRight();
}
}
//前序线索化
public void preThreadedTree(HeroNode node){
if (node == null){
return;
}
if (node.getLeft() == null){
node.setLeft(pre);
node.setLeftType(1);
}
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setRightType(1);
}
pre = node;
if(node.getLeftType() != 1){
preThreadedTree(node.getLeft());
}
if (node.getRightType() != 1){
preThreadedTree(node.getRight());
}
}
2)中序线索化及中序遍历
//中序遍历
public void midOrder(){
HeroNode node = root;
while (node != null){
while (node.getLeftType() == 0){
node = node.getLeft();
}
System.out.println(node);
while (node.getRightType() == 1){
node = node.getRight();
System.out.println(node);
}
node = node.getRight();
}
}
//中序线索化
public void midThreadedTree(HeroNode node){
if (node == null){
return;
}
//左线索化
midThreadedTree(node.getLeft());
//前驱节点
if (node.getLeft() == null){
node.setLeft(pre);
node.setLeftType(1);
}
//后继节点
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setRightType(1);
}
pre = node;
//右线索化
midThreadedTree(node.getRight());
}
3)后序线索化及后序遍历
//后续遍历:代码有问题!!!!
public void postOrder(){
HeroNode node = root;
while (node.getLeftType() == 0){
node = node.getLeft();
}
System.out.println(node);
while (node.getRightType() == 1){
node = node.getRight();
System.out.println(node);
}
HeroNode node1 = root.getRight();
while (node1.getLeftType() == 0){
node1 = node1.getLeft();
}
System.out.println(node1);
while (node1.getRightType() == 1){
node1 = node1.getRight();
System.out.println(node1);
}
}
//后序线索化
public void postThreadedTree(HeroNode node){
if (node == null){
return;
}
postThreadedTree(node.getLeft());
postThreadedTree(node.getRight());
if (node.getLeft() == null){
node.setLeft(pre);
node.setLeftType(1);
}
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setRightType(1);
}
pre = node;
}
总结
To be continued~~,堆排序,赫夫曼树,赫夫曼编码