数据结构与算法weeks03

       查找算法、哈希表、树
       在这里插入图片描述

开始~~~



一、查找算法

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.leftthis.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);
       leftTyperightType(默认为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~~,堆排序,赫夫曼树,赫夫曼编码

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值