java知识归纳总结
github: https://a870439570.github.io/interview-docs
public static void main(String[] args) {
int[] number = {1,2,3,4,5,6,7,8,9,10};
int key = 5;
System.out.println("递归查找出的下标:"+binSearch(number,0,number.length-1,key));
System.out.println("查找出的下标:"+halfSort(number, key));
}
public static int halfSort(int[] data,int key){
int left=1, right=data.length;
while(left<=right){
int mid = (left+right)/2;
if(data[mid]>key){
right = mid-1;
}else if(data[mid]<key){
left = mid+1;
}else{
return mid;
}
}
return 0;
}
// 二分查找递归实现
public static int binSearch(int srcArray[], int start, int end, int key){
int mid = (end - start) / 2 + start;
if (srcArray[mid] == key) {
return mid;
}
if (start >= end) {
return -1;
} else if (key > srcArray[mid]) {
return binSearch(srcArray, mid + 1, end, key);
} else if (key < srcArray[mid]) {
return binSearch(srcArray, start, mid - 1, key);
}
return -1;
}
int mid=left+(right-left)*(key-data[left])/(data[right]-data[left]);
上述二分法查找计算中间值部分更换为计算公式即可实现插值查找
public static int halfSort(int[] data,int key){
int left=0, right=data.length-1;
while(left<=right){
int mid=left+(right-left)*(key-data[left])/(data[right]-data[left]); //插值
if(data[mid]>key){
right = mid-1;
}else if(data[mid]<key){
left = mid+1;
}else{
return mid;
}
}
return 0;
}
public class Index {
public int key;
public Object value;
public int size;//分块存储数量
}
public class Student implements Comparable {
public Student(int no, String name) {
this.no = no;
this.name = name;
}
public int no;
public String name;
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Object o) {
Student other = (Student) o;
if (no < other.no) //这里比较的是什么 sort方法实现的就是按照此比较的东西从小到大排列
return -1;
if (no > other.no)
return 1;
return 0;
}
public static void main(String[] args) {
Student[] array=new Student[]{
new Student(2,"2222"),new Student(1,"1111"),
new Student(3,"3333"),new Student(4,"44444"),
new Student(5,"5555"),new Student(6,"7777"),
new Student(7,"7777"),new Student(8,"8888")};
int key=4;
System.out.println(denseIndex(array,8).toString());
}
//创建稠密索引表
public static Student denseIndex(Student[] array, int key) {
if (array != null && array.length > 0) {
//排序 对于稠密索引这个索引表来说,索引项一定是按照关键码有序的排列
Arrays.sort(array);
Index[] list = new Index[array.length];
//建立索引
for (int i = 0; i < array.length; i++) {
Index index = new Index();
index.key = array[i].no;
index.value = array[i];
list[i] = index;
}
//根据索引关键码搜索
int code = binarySearch(list, key);
if (code != -1) {
return (Student) list[code].value;
}
}
return null;
}
public static int binarySearch(Index[] array, int key) {
if (array.length > 0) {
int low, high, mid;
low = 0;
high = array.length - 1;
while (low <= high) {
mid = (low + high) / 2;//折半
if (key < array[mid].key)
high = mid - 1;
else if (key > array[mid].key)
low = mid + 1;
else
return mid;
}
}
return -1;
}
}
分块有序,是把数据集的记录分成了若干块,并且这些块需要满足两个条件:
对于分块有序的数据集,将每块对应一个索引项,这种索引方法叫做分块索引。我们定义的分块索引的索引项结构分三个数据项
在分块索引表中查找,就是分两步进行:
public class BlockIndex {
public int[] index; //索引表
public ArrayList[] list; //块内元素 可以无序
//初始化分块索引表
public BlockIndex(int[] index) {
this.index=index;
this.list=new ArrayList[index.length];
for (int i = 0; i < list.length; i++) {
// 分配元素到各自的块内
list[i]=new ArrayList<>();
}
}
/**
* 分配块内元素
*/
public void insert(int value) {
for (int i = 0; i < index.length; i++) {
if(value<index[i]){
list[i].add(value);
break;
}
}
}
/**
* 打印每块元素
*/
public void printAll() {
for (int i = 0; i < list.length; i++) {
ArrayList l = list[i];
System.out.print("ArrayList" + i + ":");
for (int j = 0; j < l.size(); j++) {
System.out.print(l.get(j) + " ");
}
System.out.println();
}
}
/**
* 二分查找
*/
private int binarysearch(int value) {
int start = 0;
int end = index.length;
int mid = -1;
while (start <= end) {
mid = start + (end - start) / 2;
if (index[mid] > value) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return start;
}
/**
* 查找元素
*/
public boolean search(int data) {
int i = binarysearch(data);
// 先二分查找确定在哪个块
for (int j = 0; j < list[i].size(); j++) {
// 然后顺序查找在该块内哪个位置
if (data == (int) list[i].get(j)) {
System.out.println(String.format("查找元素为 %d 第: %d块 第%d个 元素",data, i + 1, j + 1));
return true;
}
}
return false;
}
public static void main(String[] args) {
int [] data={1,12,22,10,18,23,5,15,27};
//分为三个块
int[] index = {10, 20, 30};
BlockIndex blocksearch=new BlockIndex(index);
for (int i = 0; i < data.length; i++) {
blocksearch.insert(data[i]);
}
blocksearch.printAll();
blocksearch.search(18);
}
}
books
,friends
中的复数 s
以及如 A
这样的大小写差异。我们可以整理出这样一张单词表,如表 8-5-1 所示,并将单词做了排序 , 也就是表格显示了每个不同的单词分别出现在哪篇文章中,比如 good
它在两篇文章中都有出现,而is
只是在文章 2 中才有,book
关键字。系统就先在这张单词表中有序查找 “book”,找到后将它对应的文章编号 1 和 2的文章地址(通常在搜索引擎中就是网页的标题和链接)返回,并告诉你,查找到两条记录,用时 0.0001 秒。由于单词表是有序的,查找效率很高,返回的又只是文章的编号,所以整体速度都非常快 。在这里这张单词表就是索引表, 索引项的通用结构是:
其中记录号表存储具有相同次关键字的所有记录的记录号。这样的索引方法就是倒排索引
优点:
缺点:
package com.example.chazhao;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* 倒排索引
* 倒排索引的优点显然就是查找记录非常快,基本等于生成索引表后,查找时都不 用去读取记录,就可以得到结果
*
* 它的缺点是这个记录号不定长。维护比较困难,插入和删除操作都要做相应的处理
*
* 1.txt:i live in hangzhou where are you
2.txt:i love you i love you
3.txt:i love you today is a good day
*/
public class IntertedIndex {
// 存储单词对应 多个文件列表
private Map<String, ArrayList<String>> map=new HashMap<>();
//文路径列表
private ArrayList<String> list;
//词频统计
private Map<String, Integer> nums=new HashMap<>();
public void CreateIndex(String filepath){
String[] words = null;
try {
File file=new File(filepath);
BufferedReader reader=new BufferedReader(new FileReader(file));
String s=null;
while((s=reader.readLine())!=null){
//获取单词
words=s.split(" ");
}
for (String string : words) {
if (!map.containsKey(string)) {
//集合汇总不不存在当前单词
list=new ArrayList<String>();
list.add(filepath);
map.put(string, list); //单词对应的 文件列表
nums.put(string, 1); //单词出现的次数 首次默认1
}else {
list=map.get(string);
//如果没有包含过此文件名,则把文件名放入
if (!list.contains(filepath)) {
list.add(filepath);
}
//文件总词频数目
int count=nums.get(string)+1;
nums.put(string, count);
}
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
IntertedIndex index=new IntertedIndex();
for(int i=1;i<=2;i++){
String path="C:\\Users\\admin\\Desktop\\"+i+".txt";
index.CreateIndex(path);
}
for (Map.Entry<String, Integer> num : index.nums.entrySet()) {
System.out.println("单词:"+num.getKey()+" "
+ "出现次数:"+num.getValue()+" "+index.map.get(num.getKey()));
}
}
}
二叉排序树 又称为二叉查找树。它或者是一棵空树,或者是具有下列性质的二叉树。
(1)首先将20与根节点进行比较,发现比根节点小,所以继续与根节点的左子树30比较。
(2)发现20比30也要小,所以继续与30的左子树10进行比较。
(3)发现20比10要大,所以就将20插入到10的右子树中。
比如我们要查找节点10,那么思路如下:
(1)还是一样,首先将10与根节点50进行比较,发现比根节点要小,所以继续与根节点的左子树30进行比较。
(2)发现10比左子树30要小,所以继续与30的左子树10进行比较。
(3)发现两值相等,即查找成功,返回10的位置。
删除节点的情况相对复杂,主要分为以下三种情形:
删除的是叶节点(即没有孩子节点的)。比如20,删除它不会破坏原来树的结构,最简单。如图所示
删除的是单孩子节点。比如90,删除它后需要将它的孩子节点与自己的父节点相连。情形比第一种复杂一些。
删除的是有左右孩子的节点。比如根节点50
这里有一个问题就是删除它后,谁将作为根节点?利用二叉树的中序遍历,就是右节点的左子树的最左孩子。
public class Node {
private int value;// 结点值
protected Node leftChild;// 左孩子结点
protected Node rightChild;// 右孩子结点
public Node(int value) {
this.value = value;
this.leftChild = null;
this.rightChild = null;
}
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public class SortTree {
// 递归实现二叉排序树的插入
public void insertBST(Node root, int value) {
if (root == null) {
root = new Node(value);
root.leftChild = null;
root.rightChild = null;
}
//大于根节点则 分配在右子树
if (value > root.getValue()) {
//如果右子树为空则直接赋值给右子树
if (root.rightChild == null) {
root.rightChild = new Node(value);
} else {
//右子树不为空,则递归判断,以右子树为父节点的
insertBST(root.rightChild, value);
}
} else {
//左子树分配原理同上
if (root.leftChild == null) {
root.leftChild = new Node(value);
} else {
insertBST(root.leftChild, value);
}
}
}
// 递归实现二叉排序树查找
public Node searchBSTByRecursion(Node root, int value) {
if (root == null) {
return null;
}
if (root.getValue() == value) {// 步骤1
return root;
} else if (value < root.getValue()) {// 步骤2
return searchBST(root.leftChild, value);
} else if (value > root.getValue()) {// 步骤3
return searchBST(root.rightChild, value);
}
return null;// 如果没找到,就返回null
}
// 非递归实现二叉排序树查找
public Node searchBST(Node root, int value) {
Node temp = root;
while (temp != null) {
if (temp.getValue() == value) {
return temp;
}
if (value < temp.getValue()) {
temp = temp.leftChild;
} else {
temp = temp.rightChild;
}
}
return null;
}
// 二叉排序树的删除
public Node deleteBST(Node root, int value) {
Node cur = root; // 当前结点
Node parent = null; // 待删结点的父结点
Node delNode = null; // 在后面用来引用待删结点
Node temp = null; // 作为一个局域内的根结点
// 查找待删结点p和待删结点的父结点f
while (cur != null) {
if (value == cur.getValue()) {
break;
}
parent = cur;
if (value > cur.getValue()) {
cur = cur.rightChild;
} else {
cur = cur.leftChild;
}
}
// 当前结点为null,即没有找到待删结点。 此时cur指向待删结点
if (cur == null) {
return null;
}
// 待删结点只有右子树
if (cur.leftChild == null) {
// 待删结点的父结点为null,即待删结点为根结点
if (parent == null) {
// 根结点为待删结点的右子树
root = cur.rightChild;
} else if (parent.leftChild == cur) { // 待删结点为父结点的左子树
// 把待删结点的右子树作为待删结点父结点的左子树
parent.leftChild = cur.rightChild;
} else { // 待删结点为父结点的右子树
parent.rightChild = cur.rightChild;
}
} else {// 待删结点有左子树,要找左子树的最右下角的结点
temp = cur;
delNode = cur.leftChild; // 此时s指向待删结点
while (delNode.rightChild != null) {// 查找待删结点的最右下角结点
temp = delNode;
delNode = delNode.rightChild;
}
if (temp == cur) {// 即,待删结点没有右子树,把左子树向上移动
temp.leftChild = delNode.leftChild;
} else {// 即,待删结点有右子树
temp.rightChild = delNode.leftChild;
}
cur.setValue(delNode.getValue());
}
return root;
}
//先序遍历
public void preOrder(Node root) {
if(root != null) {
System.out.print(root.getValue() + " ");
preOrder(root.leftChild);
preOrder(root.rightChild);
}
}
/**
* 先序遍历
* 非递归
*/
public void preOrder1(Node root){
Stack<Node> stack = new Stack<>();
while(root != null || !stack.empty()){
while(root != null) {
System.out.print(root.getValue() + " ");
stack.push(root);
root = root.leftChild;
}
if(!stack.empty()){
root = stack.pop();
root = root.rightChild;
}
}
}
//中序遍历
public void inOrder(Node root) {
if(root != null) {
inOrder(root.leftChild);
System.out.print(root.getValue() + " ");
inOrder(root.rightChild);
}
}
/**
* 中序遍历
* 非递归
*/
public void inOrder1(Node root) {
Stack<Node> stack = new Stack<>();
while(root != null || !stack.empty()){
while (root != null){
stack.push(root);
root = root.leftChild;
}
if(!stack.empty()){
root = stack.pop();
System.out.print(root.getValue() + " ");
root = root.rightChild;
}
}
}
//后序遍历
public void postOrder(Node root) {
if(root != null) {
postOrder(root.leftChild);
postOrder(root.rightChild);
System.out.print(root.getValue() + " ");
}
}
/**
* 后序遍历
* 非递归
*/
public void posOrder1(Node root){
Stack<Node> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
int i = 1;
while(root != null || !stack1.empty()){
while (root != null){
stack1.push(root);
stack2.push(0);
root = root.leftChild;
}
while(!stack1.empty() && stack2.peek() == i){
stack2.pop();
System.out.print(stack1.pop().getValue() + " ");
}
if(!stack1.empty()){
stack2.pop();
stack2.push(1);
root = stack1.peek();
root = root.rightChild;
}
}
}
public static void main(String[] args) {
SortTree t=new SortTree();
Node root=new Node(44); //根节点
t.insertBST(root, 21); //左子树
t.insertBST(root, 65); //右子树
t.insertBST(root, 14);
t.insertBST(root, 72);
t.insertBST(root, 32);
t.insertBST(root, 58);
t.insertBST(root, 80);
t.postOrder(root);
System.out.println("-----------------");
t.posOrder1(root);
// t.deleteBST(root, 44);
System.out.println();
// t.preOrder(t.deleteBST(root, 44));
}
}
距离插入结点最近的,且平衡困子的绝对值大于 1 的结点为根的子树,我们称为最小不平衡子树
public class AVLNode<T extends Comparable> {
public AVLNode<T> left;//左结点
public AVLNode<T> right;//右结点
public T data;
public int height;//当前结点的高度
public AVLNode(T data) {
this(null,null,data);
}
public AVLNode(AVLNode<T> left, AVLNode<T> right, T data) {
this(left,right,data,0);
}
public AVLNode(AVLNode<T> left, AVLNode<T> right, T data, int height) {
this.left=left;
this.right=right;
this.data=data;
this.height = height;
}
}
public class AVLTree<T extends Comparable> {
public AVLNode<T> root;
public boolean isEmpty() {
return root == null;
}
public int size() {
return size(root);
}
public int size(AVLNode<T> subtree) {
if (subtree == null) {
return 0;
} else {
return size(subtree.left) + 1 + size(subtree.right);
}
}
public int height() {
return height(root);
}
/**
* @param p
* @return
*/
public int height(AVLNode<T> p) {
return p == null ? -1 : p.height;
}
public String preOrder() {
String sb = preOrder(root);
if (sb.length() > 0) {
// 去掉尾部","号
sb = sb.substring(0, sb.length() - 1);
}
return sb;
}
/**
* 先根遍历
*
* @param subtree
* @return
*/
public String preOrder(AVLNode<T> subtree) {
StringBuilder sb = new StringBuilder();
if (subtree != null) {
// 先访问根结点
sb.append(subtree.data).append(",");
// 访问左子树
sb.append(preOrder(subtree.left));
// 访问右子树
sb.append(preOrder(subtree.right));
}
return sb.toString();
}
public String inOrder() {
String sb = inOrder(root);
if (sb.length() > 0) {
// 去掉尾部","号
sb = sb.substring(0, sb.length() - 1);
}
return sb;
}
/**
* 中根遍历
*
* @param subtree
* @return
*/
private String inOrder(AVLNode<T> subtree) {
StringBuilder sb = new StringBuilder();
if (subtree != null) {
// 访问左子树
sb.append(inOrder(subtree.left));
// 访问根结点
sb.append(subtree.data).append(",");
// 访问右子树
sb.append(inOrder(subtree.right));
}
return sb.toString();
}
public String postOrder() {
String sb = postOrder(root);
if (sb.length() > 0) {
// 去掉尾部","号
sb = sb.substring(0, sb.length() - 1);
}
return sb;
}
/**
* 后根遍历
*
* @param subtree
* @return
*/
private String postOrder(AVLNode<T> subtree) {
StringBuilder sb = new StringBuilder();
if (subtree != null) {
// 访问左子树
sb.append(postOrder(subtree.left));
// 访问右子树
sb.append(postOrder(subtree.right));
// 访问根结点
sb.append(subtree.data).append(",");
}
return sb.toString();
}
public String levelOrder() {
/**
* @see BinarySearchTree#levelOrder()
* @return
*/
return null;
}
/**
* 插入方法
*
* @param data
*/
public void insert(T data) {
if (data == null) {
throw new RuntimeException("data can\'t not be null ");
}
this.root = insert(data, root);
}
private AVLNode<T> insert(T data, AVLNode<T> p) {
// 说明已没有孩子结点,可以创建新结点插入了.
if (p == null) {
p = new AVLNode<T>(data);
}
int result = data.compareTo(p.data);
if (result < 0) {// 向左子树寻找插入位置
p.left = insert(data, p.left);
// 插入后计算子树的高度,等于2则需要重新恢复平衡,由于是左边插入,左子树的高度肯定大于等于右子树的高度
if (height(p.left) - height(p.right) == 2) {
// 判断data是插入点的左孩子还是右孩子
if (data.compareTo(p.left.data) < 0) {
// 进行LL旋转
p = singleRotateLeft(p);
} else {
// 进行左右旋转
p = doubleRotateWithLeft(p);
}
}
} else if (result > 0) {// 向右子树寻找插入位置
p.right = insert(data, p.right);
if (height(p.right) - height(p.left) == 2) {
if (data.compareTo(p.right.data) < 0) {
// 进行右左旋转
p = doubleRotateWithRight(p);
} else {
p = singleRotateRight(p);
}
}
} else
;// if exist do nothing
// 重新计算各个结点的高度
p.height = Math.max(height(p.left), height(p.right)) + 1;
return p;// 返回根结点
}
/**
* 删除方法
*
* @param data
*/
public void remove(T data) {
if (data == null) {
throw new RuntimeException("data can\'t not be null ");
}
this.root = remove(data, root);
}
/**
* 删除操作
*
* @param data
* @param p
* @return
*/
private AVLNode<T> remove(T data, AVLNode<T> p) {
if (p == null)
return null;
int result = data.compareTo(p.data);
// 从左子树查找需要删除的元素
if (result < 0) {
p.left = remove(data, p.left);
// 检测是否平衡
if (height(p.right) - height(p.left) == 2) {
AVLNode<T> currentNode = p.right;
// 判断需要那种旋转
if (height(currentNode.left) > height(currentNode.right)) {
// RL
p = doubleRotateWithRight(p);
} else {
// RR
p = singleRotateRight(p);
}
}
}
// 从右子树查找需要删除的元素
else if (result > 0) {
p.right = remove(data, p.right);
// 检测是否平衡
if (height(p.left) - height(p.right) == 2) {
AVLNode<T> currentNode = p.left;
// 判断需要那种旋转
if (height(currentNode.right) > height(currentNode.left)) {
// LR
p = doubleRotateWithLeft(p);
} else {
// LL
p = singleRotateLeft(p);
}
}
}
// 已找到需要删除的元素,并且要删除的结点拥有两个子节点
else if (p.right != null && p.left != null) {
// 寻找替换结点
p.data = findMin(p.right).data;
// 移除用于替换的结点
p.right = remove(p.data, p.right);
} else {
// 只有一个孩子结点或者只是叶子结点的情况
p = (p.left != null) ? p.left : p.right;
}
// 更新高度值
if (p != null)
p.height = Math.max(height(p.left), height(p.right)) + 1;
return p;
}
/**
* 查找最小值结点
*
* @param p
* @return
*/
private AVLNode<T> findMin(AVLNode<T> p) {
if (p == null)// 结束条件
return null;
else if (p.left == null)// 如果没有左结点,那么t就是最小的
return p;
return findMin(p.left);
}
public T findMin() {
return findMin(root).data;
}
public T findMax() {
return findMax(root).data;
}
/**
* 查找最大值结点
*
* @param p
* @return
*/
private AVLNode<T> findMax(AVLNode<T> p) {
if (p == null)
return null;
else if (p.right == null)// 如果没有右结点,那么t就是最大的
return p;
return findMax(p.right);
}
public boolean contains(T data) {
return data != null && contain(data, root);
}
public boolean contain(T data, AVLNode<T> subtree) {
if (subtree == null)
return false;
int result = data.compareTo(subtree.data);
if (result < 0) {
return contain(data, subtree.left);
} else if (result > 0) {
return contain(data, subtree.right);
} else {
return true;
}
}
/**
* 左左单旋转(LL旋转) w变为x的根结点, x变为w的右子树
*
* @param x
* @return
*/
private AVLNode<T> singleRotateLeft(AVLNode<T> x) {
// 把w结点旋转为根结点
AVLNode<T> w = x.left;
// 同时w的右子树变为x的左子树
x.left = w.right;
// x变为w的右子树
w.right = x;
// 重新计算x/w的高度
x.height = Math.max(height(x.left), height(x.right)) + 1;
w.height = Math.max(height(w.left), x.height) + 1;
return w;// 返回新的根结点
}
/**
* 右右单旋转(RR旋转) x变为w的根结点, w变为x的左子树
*
* @return
*/
private AVLNode<T> singleRotateRight(AVLNode<T> w) {
AVLNode<T> x = w.right;
w.right = x.left;
x.left = w;
// 重新计算x/w的高度
x.height = Math.max(height(x.left), w.height) + 1;
w.height = Math.max(height(w.left), height(w.right)) + 1;
// 返回新的根结点
return x;
}
/**
* 左右旋转(LR旋转) x(根) w y 结点 把y变成根结点
*
* @return
*/
private AVLNode<T> doubleRotateWithLeft(AVLNode<T> x) {
// w先进行RR旋转
x.left = singleRotateRight(x.left);
// 再进行x的LL旋转
return singleRotateLeft(x);
}
/**
* 右左旋转(RL旋转)
*
* @param w
* @return
*/
private AVLNode<T> doubleRotateWithRight(AVLNode<T> w) {
// 先进行LL旋转
w.right = singleRotateLeft(w.right);
// 再进行RR旋转
return singleRotateRight(w);
}
private void printTree(AVLNode<T> t) {
if (t != null) {
printTree(t.left);
System.out.println(t.data);
printTree(t.right);
}
}
/**
* 测试
*
* @param arg
*/
public static void main(String arg[]) {
AVLTree<Integer> avlTree = new AVLTree<>();
for (int i = 1; i < 18; i++) {
avlTree.insert(i);
}
avlTree.printTree(avlTree.root);
// 删除11,8以触发旋转平衡操作
avlTree.remove(11);
avlTree.remove(8);
System.out.println("================");
avlTree.printTree(avlTree.root);
System.out.println("findMin:" + avlTree.findMin());
System.out.println("findMax:" + avlTree.findMax());
System.out.println("15 exist or not : " + avlTree.contains(15));
System.out.println("先根遍历:" + avlTree.preOrder());
System.out.println("中根遍历:" + avlTree.inOrder());
System.out.println("后根遍历:" + avlTree.postOrder());
}
}
整个散列过程其实就是两步
直接定址法
比如我们要对 0 到100岁的人口数字统计表那么我们对年龄这个关键字就可以直接用年龄的数字作为地址。此时 f (key) =key
f ( key ) =a*key+b
(a,b为常数)
数字分析法
平方取中法
折叠法
987+654+321+0=1962
,再求后 3 位得到散列地址为 962 。除留余敢法
f( key) = key %p (p <= m)
随机数法
f (key)=random (key )
。这里 random 是随机函数。当关键字的长度不等时,采用这个方法构造散列函数是比较合适的。开放定址法
公式:f(key)=(f(key)+d)% m
d等于(1,2,3…m-1 )
f(key)=ke%12
f(37)=(f(37)+1)%12=2
。加入还是重负d就是不断进行累加再散列函数法
链地址法
链地址法对于可能会造成很多冲突的散列函数来说,提供了绝不会出现找不到地址的保障。当然,这也就带来了查找时需要遍历单链装的性能损耗。
公共溢出区法