数据结构与算法| 队列| 排序| 查找| 二叉树| 哈希表

 

递归(recursion)的概念

简单的说:递归就是函数/方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。

  递归快速入门

我列举两个小案例,来帮助大家理解递归,递归在讲函数时已经讲过(当时讲的相对比较简单),这里在给大家回顾一下递归调用机制

1)   打印问题

2)   阶乘问题

举一个比较综合的案例,迷宫(maze)问题

说明:

1) 小球得到的路径,和程序员设置的找路策略有关即:找路的上下左右的顺序相关再得到小球路径时,可以先使用(下右上左),再改成(上右下左),看看路径是不是有变化

2) 测试回溯现象

3) 思考: 如何求出最短路径?

4) 分析和代码

 

 

object MiGong {
  def main(args: Array[String]): Unit = {
    //使用二维数组模拟地图 map[8][7]
    val map = Array.ofDim[Int](8, 7)
    //给map初始化,使用1 表示墙

    //上下行设置1
    for (i <- 0 until 7) {
      map(0)(i) = 1
      map(7)(i) = 1
    }

    //左右设置为1
    for (i <- 0 until 8) {
      map(i)(0) = 1
      map(i)(6) = 1
    }
    //设置墙
    map(3)(1) = 1
    map(3)(2) = 1

    //验证看看当前地图是否
    for (row <- map) {
      for (ele <- row) {
        print(ele + " ")
      }
      println()
    }
    println()

    //测试迷宫
    setWay2(map, 1, 1)

    //看看迷宫的情况
    println("小球找路的结果:")
    for (row <- map) {
      for (ele <- row) {
        print(ele + " ")
      }
      println()
    }
    println()
  }

  //编写方法完成探路
  /**
    *
    * @param map 地图
    * @param i   表示当前探测点横坐标
    * @param j   表示当前探测点纵坐标
    * @return
    */
  def setWay(map: Array[Array[Int]], i: Int, j: Int): Boolean = {
    //如果map[6][5] == 2
    if (map(6)(5) == 2) { //找到通路
      return true
    } else {
      //找在地图中,使用1 表示墙,0 表示没有走过, 2 表示通路,3  表示该点已经探测过,但是走不通,死路
      if(map(i)(j) == 0) {
        //假定map(i)(j) 可以走通,实际不一定
        map(i)(j) = 2
        //安我们的策略开始探测
        //先确定一个策略  下->右->上->左
        if(setWay(map, i+1,j)) {
          return  true
        } else if (setWay(map, i,j+1)){ //
          return true
        } else if (setWay(map, i-1,j)){ //
          return true
        } else if (setWay(map, i,j-1)){ //
          return true
        } else {
          //说明你的假定不正确
          map(i)(j) = 3
          return false
        }
      } else { //map(i)(j) != 0, 则map(i)(j) 1 2 3
        return  false
      }
    }
  }

  def setWay2(map: Array[Array[Int]], i: Int, j: Int): Boolean = {
    //如果map[6][5] == 2
    if (map(6)(5) == 2) { //找到通路
      return true
    } else {
      //找在地图中,使用1 表示墙,0 表示没有走过, 2 表示通路,3  表示该点已经探测过,但是走不通,死路
      if(map(i)(j) == 0) {
        //假定map(i)(j) 可以走通,实际不一定
        map(i)(j) = 2
        //安我们的策略开始探测
        //先确定一个策略  上->右->下->左
        if(setWay2(map, i-1,j)) { //
          return  true
        } else if (setWay2(map, i,j+1)){ //
          return true
        } else if (setWay2(map, i+1,j)){ //
          return true
        } else if (setWay2(map, i,j-1)){ //
          return true
        } else {
          //说明你的假定不正确
          map(i)(j) = 3
          return false
        }
      } else { //map(i)(j) != 0, 则map(i)(j) 1 2 3
        return  false
      }
    }
  }
}
View Code

排序

是将一组数据,依指定的顺序进行排列的过程, 常见的排序:

//时间复杂度

//思路=》 推导

1)   冒泡排序

2)   选择排序

3)  插入排序

4)  快速排序

5)   归并排序

1.冒泡排序

冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的排序码,若发现逆序则交换,使排序码较小

的元素逐渐从后部移向前部(从下标较大的单元移向下

  • 标较小的单元),就象水底下的气泡一样逐渐向上冒。

冒泡排序算法的分析

冒泡代码实现:

import java.text.SimpleDateFormat
import java.util.Date
import util.control.Breaks._

object BubbleSort {
  def main(args: Array[String]): Unit = {
    //o(n^2)
    //val arr = Array(3, 9, -1, 10, 2) // 80000
    //创建一个80000个随机数据的数组
    val random = new util.Random()
    val arr = new Array[Int](80000)
    for (i <- 0 until 80000) {
      arr(i) = random.nextInt(8000000) //范围[0,8000000)
    }
    println("排序前")
    val now: Date = new Date()
    val dateFormat: SimpleDateFormat =
      new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    val date = dateFormat.format(now)
    println("排序前时间=" + date) //输出时间

    //测试一下时间

    bubbleSort(arr)

    val now2: Date = new Date()
    val date2 = dateFormat.format(now2)
    println("排序前时间=" + date2) //输出时间


  }

  def bubbleSort(arr: Array[Int]): Unit = {

    //因为1,2,3,4, 轮几乎一样,我们使用for循环嵌套即
    //对冒泡优化
    //思路
    //1. 如果我们发现数组已经是一个有序的了,则可以提前退出排序
    //2. 定义一个标识符 flag ,如果在1轮排序,一次交换都没有发生,说明数组已经有序
    var temp = 0
    var flag = false

    breakable {
      for (i <- 0 until arr.length - 1) {
        for (j <- 0 until arr.length - 1 - i) {
          if (arr(j) > arr(j + 1)) {
            flag = true
            temp = arr(j)
            arr(j) = arr(j + 1)
            arr(j + 1) = temp
          }
        }
        if (!flag) {
          break()
        } else {
          flag = false //下一轮,重写设置false
        }
        //println("第"+(i+1)+"轮的排序" + arr.mkString(","))
      }
    }

    /*
    第1轮 =>         将最大数,移动数组的最后
  (1)  3, 9, -1, 10, 2
  (2)  3, -1,9,10,2
  (3)  3, -1,9,10,2
  (4) 3, -1,9,2,10

     */
    /*
    var temp = 0
    for (j <- 0 until arr.length - 1 - 0) {
      if (arr(j) > arr(j + 1)) {
        temp = arr(j)
        arr(j) = arr(j + 1)
        arr(j + 1) = temp
      }
    }
    println("第l轮的排序" + arr.mkString(","))

    //第二轮
    for (j <- 0 until arr.length - 1 - 1) {
      if (arr(j) > arr(j + 1)) {
        temp = arr(j)
        arr(j) = arr(j + 1)
        arr(j + 1) = temp
      }
    }
    println("第2轮的排序" + arr.mkString(","))

    //第3轮
    for (j <- 0 until arr.length - 1 - 2) {
      if (arr(j) > arr(j + 1)) {
        temp = arr(j)
        arr(j) = arr(j + 1)
        arr(j + 1) = temp
      }
    }
    println("第3轮的排序" + arr.mkString(","))

    //第4轮
    for (j <- 0 until arr.length - 1 - 3) {
      if (arr(j) > arr(j + 1)) {
        temp = arr(j)
        arr(j) = arr(j + 1)
        arr(j + 1) = temp
      }
    }
    println("第4轮的排序" + arr.mkString(","))*/

  }
}
View Code

2. 插入排序法介绍:

插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。

 插入排序法思想:

插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

  插入排序的思路图解

 

代码实现和推导过程

import java.text.SimpleDateFormat
import java.util.Date

object InsertSort {
  def main(args: Array[String]): Unit = {
    //val arr = Array(101, 34, 119, 1)
    val random = new util.Random()
    val arr = new Array[Int](800000)
    for (i <- 0 until 800000) {
      arr(i) = random.nextInt(8000000) //范围[0,8000000)
    }
    println("排序前")
    val now: Date = new Date()
    val dateFormat: SimpleDateFormat =
      new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    val date = dateFormat.format(now)
    println("排序前时间=" + date) //输出时间
    println("插入排序")

    insertSort(arr) //插入排序

    val now2: Date = new Date()
    val date2 = dateFormat.format(now2)
    println("排序前时间=" + date2) //输出时间
  }

  //插入排序
  def insertSort(arr: Array[Int]): Unit = {

    var insertVal = 0
    var index = 0
    //使用for循环来完成
    for(i <- 1 until arr.length) {
      insertVal = arr(i)
      index = i - 1
      //说明
      //如果index >=0 比较 insertVal 和  arr(index)关系
      //insertVal < arr(index) 满足,说明还没有找到位置
      while (index >= 0 && (insertVal < arr(index))) {
        //让arr(index) 后移
        arr(index+1) = arr(index)
        index -= 1
      }
      arr(index+1) = insertVal
      //println("arr 第"+i+"轮" + arr.mkString(","))
    }


    //完成第1轮的排序(给 34)找位置
    //思路
    //1. 先 34 保存到一个变量
    //2  用一个变量保存 34的元素的前一个下标

    /*
    var insertVal = arr(1)
    var index = 1 - 1
    //说明
    //如果index >=0 比较 insertVal 和  arr(index)关系
    //insertVal < arr(index) 满足,说明还没有找到位置
    while (index >= 0 && (insertVal < arr(index))) {
      //让arr(index) 后移
      arr(index+1) = arr(index)
      index -= 1
    }
    arr(index+1) = insertVal
    println("arr 第1轮" + arr.mkString(","))

    //第2轮
    insertVal = arr(2)
    index = 2 - 1
    while (index >= 0 && (insertVal < arr(index))) {
      //让arr(index) 后移
      arr(index+1) = arr(index)
      index -= 1
    }
    arr(index+1) = insertVal
    println("arr 第2轮" + arr.mkString(","))

    //第3轮
    insertVal = arr(3)
    index = 3 - 1
    while (index >= 0 && (insertVal < arr(index))) {
      //让arr(index) 后移
      arr(index+1) = arr(index)
      index -= 1
    }
    arr(index+1) = insertVal
    println("arr 第3轮" + arr.mkString(","))*/

  }


}
View Code

 3. 归并排序介绍:

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

 归并排序思想示意图1-基本思想:

可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程。

归并排序思想示意图2-合并相邻有序子序列:

再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤

代码实现

import java.text.SimpleDateFormat
import java.util.Date

object MergeSort {
  def main(args: Array[String]): Unit = {
    //val arr = Array(8, 4, 5, 7, 1, 3, 6, 2,-1)

    val random = new util.Random()
    val arr = new Array[Int](8000000)
    for (i <- 0 until 8000000) {
      arr(i) = random.nextInt(8000000) //范围[0,8000000)
    }
    println("排序前")
    val now: Date = new Date()
    val dateFormat: SimpleDateFormat =
      new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    val date = dateFormat.format(now)
    println("排序前时间=" + date) //输出时间
    println("归并排序")

    val temp = new Array[Int](arr.length)
    mergeSort(arr,0,arr.length-1,temp)

    val now2: Date = new Date()
    val date2 = dateFormat.format(now2)
    println("排序前时间=" + date2) //输出时间

    //println("排序后"+arr.mkString(","))

  }

  def mergeSort(arr: Array[Int],left:Int,right :Int,temp:Array[Int]): Unit = {
    if(left<right){
      val mid = (left+right)/2 //找到中间值
      mergeSort(arr,left,mid,temp) //向左递归,进行分
      mergeSort(arr,mid+1,right,temp) //向右递归进行分
      merge(arr,left,mid,right,temp) //合并
    }
  }


  /**
    *
    * @param arr 排序的原始数组
    * @param left 左边的有序数列初始索引
    * @param mid  //中间初始索引
    * @param right //右边的索引
    * @param temp  //做中转的数组
    */
  def merge(arr: Array[Int], left: Int, mid: Int, right: Int, temp: Array[Int]) {
    var i = left //初始化i
    var j = mid + 1 //初始j
    var t = 0 // 指向temp数组的当前索引

    //将左右两边有序数列安,从小到大的顺序拷贝到temp数组中
    while (i <= mid && j <= right) {
      //如果左边的有序数列的当前元素,小于右边的有序数列的当前元素
      //则将arr(i) 拷贝temp
      if (arr(i) <= arr(j)) {
        temp(t) = arr(i)
        t += 1
        i += 1
      } else { //将 arr(j) 拷贝到temp
        temp(t) = arr(j)
        t += 1
        j += 1
      }
    }

    //如果左边的有序数列有剩余,的依次拷贝到temp
    while (i <= mid) {
      temp(t) = arr(i)
      t += 1
      i += 1
    }
    //如果右边的有序数列有剩余,的依次拷贝到temp
    while (j <= right) {
      temp(t) = arr(j)
      t += 1
      j += 1
    }
    t = 0
    var tempLeft = left
    //是把temp 数组的元素拷贝到 arr
    while (tempLeft <= right) {
      arr(tempLeft) = temp(t)
      t += 1
      tempLeft += 1
    }
  }

}
View Code

4. 快排

                  

代码实现

def quickSort(left: Int, right: Int, arr: Array[Int]): Unit = {

    var l: Int = left
    var r: Int = right
    var pivot = arr((left + right) / 2)
    var temp = 0

    //Array(10, 11, 2, -1, 3)
    breakable {
      while (l < r) {

//从左点向右遍历,直到找到比中间值大的
        while (arr(l) < pivot) {
          l += 1
        }

//从右点向左遍历,直到找到比中间值小的
        while (arr(r) > pivot) {
          r -= 1
        }

//判断是否已经越过中间值
        if (l >= r) {
          break()
        }

         //交换数据
        temp = arr(l)
        arr(l) = arr(r)
        arr(r) = temp
      }
    }

    if (l == r) {
      l += 1
      r -= 1
    }

    //向左递归
    if (left < r) {
      quickSort(left, r, arr)
    }

    //向右递归
    if (right > l) {
      quickSort(l, right, arr)
    }

}
View Code

查找

介绍:

       在java中,我们常用的查找有两种:

       1) 顺序(线性)查找

       2) 二分查找

线性查找

   有一个数列: {1,8, 10, 89, 1000, 1234} ,判断数列中是否包含此名称【顺序查找】 要求: 如果找到了,就提示找到,并给出下标值。

object OrderSearch {
  def main(args: Array[String]): Unit = {
    val arr = Array(1,8, 10, 89, 89,1000, 1234)
    val index = orderFind(arr, 89)
    if (index == -1) {
      println("没有查找到~")
    } else {
      println("找到,下标为=" + index)
    }
  }
  def orderFind(arr: Array[Int], value: Int): Int = {
    for (i <- 0 until arr.length) {
      if (arr(i) == value) {
        return i
      }
    }
    -1
  }
}
View Code

二分查找:

   请对一个有序数组进行二分查找 {1,8, 10, 89, 1000, 1234} ,输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"。

       课后思考题1: {1,8, 10, 89, 1000, 1000,1234} 当一个有序数组中,有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000.

import scala.collection.mutable.ArrayBuffer
import util.control.Breaks._

object BinarySearch {
  def main(args: Array[String]): Unit = {

    val arr = Array(1, 8, 10, 89, 1000, 1000, 1000,1000,1000)
    //binarySearch(arr,0,arr.length -1, 1000)
    val resIndexBuffer = binarySearch2(arr, 0, arr.length - 1, 1000)
    println("结果是=" + resIndexBuffer)

    //要求:
    /*
    课后思考题1: {1,8, 10, 89, 1000, 1000,1234}
    当一个有序数组中,有多个相同的数值时,如何将所有的数值对应下标都查找到,比如这里的 1000.

     */


  }

  //使用二分查找,需要将所有数都找到
  def binarySearch2(arr: Array[Int], leftIndex: Int, rightIndex: Int, findVal: Int): ArrayBuffer[Int] = {
    //判断什么时候是找不到
    if (rightIndex < leftIndex) {
      return ArrayBuffer() //如果没有找到,就返回ArrayBuffer大小=0
    }


    //得到中间的mid
    val midIndex = (leftIndex + rightIndex) / 2
    val midVal = arr(midIndex)

    //比较
    if (midVal > findVal) { //向左递归查找
      return binarySearch2(arr, leftIndex, midIndex - 1, findVal)
    } else if (midVal < findVal) { //向右递归查找
      return binarySearch2(arr, midIndex + 1, rightIndex, findVal)
    } else {
      //思路,当我们找到midIndex时,需要在midIndex 左右两边扫描,将满足条件的下标
      //全部放入到ArrayBuffer
      val resIndexBuffer = new ArrayBuffer[Int]()
      //向左
      var temp = midIndex - 1
      breakable {
        while (true) {
          //如果 temp < 0 已经把左边全部扫描完毕
          // arr(temp) != findVal: 在向左扫描过程中,一旦发现有一个元素不等于 findVal,就退出
          if (temp < 0 || arr(temp) != findVal) {
            break()
          }
          resIndexBuffer.append(temp)
          temp -= 1
        }
      }
      resIndexBuffer.append(midIndex) //加入中间索引
      //向右
      temp = midIndex + 1
      breakable {
        while (true) {
          //如果 temp > arr.length - 1 已经把右边全部扫描完毕
          // arr(temp) != findVal: 在向左扫描过程中,一旦发现有一个元素不等于 findVal,就退出
          if (temp > arr.length - 1 || arr(temp) != findVal) {
            break()
          }
          resIndexBuffer.append(temp)
          temp += 1
        }
      }
      return resIndexBuffer

    }
  }

  //使用递归的方式,编写二分查找
  /**
    *
    * @param arr        待查找的数组
    * @param leftIndex  数组左边的索引
    * @param rightIndex 数组右边的索引
    * @param findVal    你要查找的数
    */
  def binarySearch(arr: Array[Int], leftIndex: Int, rightIndex: Int, findVal: Int): Unit = {

    //判断什么时候是找不到
    if (rightIndex < leftIndex) {
      println("找不到")
      return
    }


    //得到中间的mid
    val midIndex = (leftIndex + rightIndex) / 2
    val midVal = arr(midIndex)

    //比较
    if (midVal > findVal) { //向左递归查找
      binarySearch(arr, leftIndex, midIndex - 1, findVal)
    } else if (midVal < findVal) { //向右递归查找
      binarySearch(arr, midIndex + 1, rightIndex, findVal)
    } else {
      println("找到了 索引为=" + midIndex)


    }

  }
}
View Code

二叉树概念

二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点:它可以像数组一样快速查找,也可以像链表一样快速添加。但是他也有自己的缺点:删除操作复杂。

二叉树:是每个节点最多有两个子树的有序树,在使用二叉树的时候,数据并不是随便插入到节点中的,一个节点的左子节点的关键值必须小于此节点,右子节点的关键值必须大于或者是等于此节点,所以又称二叉查找树、二叉排序树、二叉搜索树。

完全二叉树:若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。

满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。

深度——二叉树的层数,就是深度。

二叉树的特点

1)树执行查找、删除、插入的时间复杂度都是O(logN)

2)遍历二叉树的方法包括前序、中序、后序

3)非平衡树指的是根的左右两边的子节点的数量不一致

4) 在非空二叉树中,第i层的结点总数不超过 , i>=1;

5)深度为h的二叉树最多有个结点(h>=1),最少有h个结点;

6)对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;

 二叉树的Java代码实现

首先是树的节点的定义,下面的代码中使用的是最简单的int基本数据类型作为节点的数据,如果要使用节点带有更加复杂的数据类型,换成对应的对象即可。

public class TreeNode {

 // 左节点
 private TreeNode lefTreeNode;
 // 右节点
 private TreeNode rightNode;
 // 数据
 private int value;

 private boolean isDelete;

 public TreeNode getLefTreeNode() {
  return lefTreeNode;
 }

 public void setLefTreeNode(TreeNode lefTreeNode) {
  this.lefTreeNode = lefTreeNode;
 }

 public TreeNode getRightNode() {
  return rightNode;
 }

 public void setRightNode(TreeNode rightNode) {
  this.rightNode = rightNode;
 }

 public int getValue() {
  return value;
 }

 public void setValue(int value) {
  this.value = value;
 }

 public boolean isDelete() {
  return isDelete;
 }

 public void setDelete(boolean isDelete) {
  this.isDelete = isDelete;
 }

 public TreeNode() {
  super();
 }

 public TreeNode(int value) {
  this(null, null, value, false);
 }

 public TreeNode(TreeNode lefTreeNode, TreeNode rightNode, int value,
   boolean isDelete) {
  super();
  this.lefTreeNode = lefTreeNode;
  this.rightNode = rightNode;
  this.value = value;
  this.isDelete = isDelete;
 }

 @Override
 public String toString() {
  return "TreeNode [lefTreeNode=" + lefTreeNode + ", rightNode="
    + rightNode + ", value=" + value + ", isDelete=" + isDelete
    + "]";
 }

}
下面给出二叉树的代码实现。由于在二叉树中进行节点的删除非常繁琐,因此,下面的代码使用的是利用节点的isDelete字段对节点的状态进行标识
public class BinaryTree {

 // 根节点
 private TreeNode root;

 public TreeNode getRoot() {
  return root;
 }

 /**
  * 插入操作
  * 
  * @param value
  */
 public void insert(int value) {

  TreeNode newNode = new TreeNode(value);

  if (root == null) {
   root = newNode;
   root.setLefTreeNode(null);
   root.setRightNode(null);
  } else {

   TreeNode currentNode = root;
   TreeNode parentNode;

   while (true) {

    parentNode = currentNode;
    // 往右放
if (newNode.getValue() > currentNode.getValue()) {

     currentNode = currentNode.getRightNode();

     if (currentNode == null) {
      parentNode.setRightNode(newNode);
      return;
     }
    } else {
     // 往左放
     currentNode = currentNode.getLefTreeNode();

     if (currentNode == null) {
      parentNode.setLefTreeNode(newNode);
      return;
     }
    }
   }
  }
 }

 /**
  * 查找
  * 
  * @param key
  * @return
  */
 public TreeNode find(int key) {

  TreeNode currentNode = root;

  if (currentNode != null) {

   while (currentNode.getValue() != key) {

    if (currentNode.getValue() > key) {
     currentNode = currentNode.getLefTreeNode();
    } else {
     currentNode = currentNode.getRightNode();
    }

    if (currentNode == null) {
     return null;
}

   }

   if (currentNode.isDelete()) {
    return null;
   } else {
    return currentNode;
   }

  } else {
   return null;
  }
 }

 /**
  * 中序遍历
  * 
  * @param treeNode
  */
 public void inOrder(TreeNode treeNode) {

  if (treeNode != null && treeNode.isDelete() == false) {

   inOrder(treeNode.getLefTreeNode());

   System.out.println("--" + treeNode.getValue());

   inOrder(treeNode.getRightNode());
  }
 }
}
View Code

在上面对二叉树的遍历操作中,使用的是中序遍历,这样遍历出来的数据是增序的。

下面是测试代码:

public class Main {

 public static void main(String[] args) {

  BinaryTree tree = new BinaryTree();

  // 添加数据测试
  tree.insert(10);
  tree.insert(40);
  tree.insert(20);
  tree.insert(3);
  tree.insert(49);
  tree.insert(13);
  tree.insert(123);

  System.out.println("root=" + tree.getRoot().getValue());

  // 排序测试
  tree.inOrder(tree.getRoot());

  // 查找测试
  if (tree.find(10) != null) {
   System.out.println("找到了");
  } else {
   System.out.println("没找到");
  }

  // 删除测试
  tree.find(40).setDelete(true);

  if (tree.find(40) != null) {
   System.out.println("找到了");
  } else {
   System.out.println("没找到");
  }
 }
}
View Code

哈希表(散列)

哈希表的基本介绍

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

          

 应用实例

  • google公司的一个上机题:

有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,住址..),当输入该员工的id时,要求查找到该员工的 所有信息.

  • 要求:

1) 不使用数据库,,速度越快越好=>哈希表(散列)

2) 添加时,保证按照id从低到高插入  [课后思考:如果id不是从低到高插入,但要求各条链表仍是从低到高,怎么解决?]

3) 使用链表来实现哈希表, 该链表不带表头
       [即: 链表的第一个结点就存放雇员信息]

4) 思路分析并画出示意图

1) 代码实现[增删改查(显示所有员工,按id查询)]

import util.control.Breaks._
object HashTableDemo {
  def main(args: Array[String]): Unit = {
    val emp1 = new Emp(1,"tom")
    val emp2 = new Emp(2,"mary")
    val emp3 = new Emp(3,"smith")
    val emp4 = new Emp(4,"terry")
    val emp5 = new Emp(5,"tomcat")
    val emp6 = new Emp(11,"king")

    //创建HashTable
    val hashTable = new HashTable(7)
    hashTable.addEmp(emp1)
    hashTable.addEmp(emp2)
    hashTable.addEmp(emp3)
    hashTable.addEmp(emp4)
    hashTable.addEmp(emp5)
    hashTable.addEmp(emp6)

    //显示
    hashTable.list()

    //测试查找
    val emp = hashTable.findByNo(111)
    if(emp != null) {
      printf("找到了" + emp.no + " " + emp.name )
    } else {
      println("没有找到")
    }
  }
}


//创建HashTable , 管理7条链表
class HashTable(size:Int) {
  val linkedArr: Array[EmpLinkedList] = new Array[EmpLinkedList](size)
  //将 linkedArr 数组中的每一个链表对象,再new
  for(i <- 0 until linkedArr.length ) {
    linkedArr(i) = new EmpLinkedList()
  }

  //查找
  def findByNo(no:Int): Emp = {
    //先确定到哪条链表去查找
    println("在" + hash(no) + "去找雇员")
    return linkedArr(hash(no)).findByNO(no)
  }

  //添加方法, 由 HashTable 来通过哈希得到应该加入的链表
  def addEmp(emp: Emp): Unit = {
    val linkedListNO = hash(emp.no)//散列
    linkedArr(linkedListNO).add(emp)
  }
  //遍历整个哈希表
  def list(): Unit = {
    for(i <- 0 until linkedArr.length) {
      linkedArr(i).list(hash(i))
      println()
    }
  }

  /**
    *
    * @param no 传入雇员的no
    * @return 返回该雇员应该加入到哪个链表中
    */
  def hash(no:Int): Int = {
    no % size
  }
}

//创建Emp 类,表示雇员
class Emp(eNo: Int, eName: String) {
  val no = eNo
  var name = eName
  var next: Emp = null
}

//创建EmpLinkedList 存放Emp的单链表
class EmpLinkedList {
  var head: Emp = null//链表头
  //添加
  def add(emp: Emp): Unit = {
    if(head == null) { //添加的第一个Emp
      head = emp
    } else {
      //使用辅助指针完成添加, 简单处理直接加入到链表的最后
      var curEmp = head
      while (curEmp.next != null) {
        curEmp = curEmp.next
      }
      //退出while后curEmp就指向最后
      curEmp.next = emp
    }
  }

  //查找
  //如果找到返回 Emp对象,没有则返回null
  def findByNO(no:Int): Emp = {
    if(head==null) {
      println("空链表")
      return null
    }
    var curEmp = head

    breakable {
      while (true) {
        if (curEmp.no == no) {
          //找到
          break() //这时curEmp就是你要找到Emp
        }
        if (curEmp.next == null) {
          //这时curEmp就是是最后
          curEmp = null
        }

        curEmp = curEmp.next
      }
    }

    return curEmp
  }

  //遍历
  def list(linkedListNO:Int): Unit = {
    //判断链表是否为空
    if(head == null) {
      println("第"+linkedListNO+"链表为空")
      return
    }
    var curEmp = head
    print("第"+linkedListNO+"链表雇员 : ")
    while(curEmp != null) {
      //输出curEmp信息
      printf("雇员的信息 no=%d name = %s => ", curEmp.no, curEmp.name)
      curEmp = curEmp.next
    }
    println()

  }
}
View Code

 

 

 

转载于:https://www.cnblogs.com/shengyang17/p/10854549.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值