1. 查找
a) 概念:给定某个值,在给定的数据结构中寻找指定数据元素的过程
b) 分类
i. 若在查找的同时需要进行插入和删除叫动态查找,否则叫静态查找
ii. 从逻辑上说,对应不同数据结构分为线性表、树表、哈希表三种查找技术
c) 效率:通常把查找过程中对关键字执行的平均比较次数ASL作为标准
i. ASL=,其中n为节点个数,c为每个节点需要比较的次数
2. 线性查找
a) 顺序查找
i. 基本思想:从表的一段开始扫描,若扫描到元素与给定值key相等,结束扫描,返回下标,若扫描完表也没找到,返回-1
ii. 代码实现:略
iii. 效率:ASL=N
iv. 使用范围:在以下两种情况只能采用顺序查找
[1] 顺序表为无序表
[2] 链表
b) 二分查找
i. 思想:设置start、end、mid,mid等于中间值,比较mid的值与给定的key值大小,如果mid值大于key,则令end=mid-1,mid=(start+end)/2,如果mid值小于key,则start=mid+1,mid=(start+end)/2,继续查找,找到则返回下标,如果start==end还是没找到返回-1
ii. 代码实现:略
iii. 效率:ASL=log(n+1)-1
iv. 使用范围:有序的顺序表
c) 分块查找:索引顺序查找
i. 思想:将待排序数组分为几块,每块内求出块内的最大值,然后用key与这些最大值比较,确定key所在的块,然后再在块内顺序查找
ii. 代码实现:略
iii. 效率:ASL=n^(0.5)
3. 树表查找
a) 提高动态查找表的效率
b) 二叉排序树(二叉查找树)
c) 代码实现:见第五章
d) 查找效率:ASL=logn
4. 哈希表查找
a) 概念:将键值转换为偏移地址来检索记录,键值转换地址通过哈希函数完成
b) 基本思想:假设一个有n个记录的集合设置一个长为m的表,将n个记录的关键字尽可能的转换为0~m-1之间的数值
c) 哈希冲突:不同记录的键值通过哈希函数转换得到的地址相同
d) 构建哈希函数
i. 平方取中法,求关键字的平方,然后根据表长度取中间几位数,如关键字为0100、0110,平方后为0010000、0012100,假设表长为1000,则取中间三位数字100、121
ii. 除余法:地址=关键字%p,p为小于表长m且最接近m的质数
iii. 折叠偏移法:将关键字分为几段,将这几段的值相加,根据表长舍去最高几位得到的地址,如47669,将其拆为1位,3位,1位向加为4+766+9=779,假如表长尾100,则舍去7,地址尾79;
e) 解决哈希冲突
i. 开放定址法
[1] 首先取一部分数据放入对应地址,当表中i,i+1,…i+k已经存在元素时,如果有后续具有哈希冲突的元素将要放入I,i+1,…i+k的地址时,逐渐向后搜索直到有位置没有插入过元素放入该元素。
ii. 链表法
[1] 若选择的表长尾m,则创建一个长度为m由头指针组成的单链表数组,将具有相同哈希值的元素按顺序添加入对应的单链表中
f) 代码实现:以除余法和链表法构建哈希表
//构建哈希节点类
class HashNode{
privateinte;
private HashNode next;
public HashNode() {
this.e=0;
this.next=null;
}
public HashNode(inte) {
super();
this.e = e;
this.next = null;
}
publicint getE() {
returne;
}
publicvoid setE(inte) {
this.e = e;
}
public HashNode getNext() {
returnnext;
}
publicvoid setNext(HashNode next) {
this.next = next;
}
}
//哈希表查找
publicstaticboolean hashtableSearch(intdata[],intkey) {
HashNode[] nodes=createHashTable(data);
inthash=key%getPrimes(data.length);//获取待查找元素的hash值
HashNode temp=nodes[hash]; //从hash表中找出待查元素
while(temp!=null) {
if(temp.getE()==key)
returntrue;
else
temp=temp.getNext();
}
returnfalse;
}
//构建哈希表
publicstatic HashNode[] createHashTable(intdata[]) {
intlen=data.length;
inthash;
intprimes=getPrimes(len); //找到被除数(小于表长且最接近表长的质数)
HashNode temp;
HashNode[] nodes=new HashNode[len];
for(inti=0;i<len;i++) {
hash=data[i]%primes;
temp=nodes[hash];
HashNode node=new HashNode(data[i]);
if(temp==null) //如果这是插入该链表的第一个元素,这个节点作为首节点
nodes[hash]=node;
else {
while(temp.getNext()!=null) {
temp=temp.getNext();
}
temp.setNext(node);
}
}
returnnodes;
}
//求取余法的被除数(小于表长最接近表长的质数)
publicstaticint getPrimes(intlength) {
for(inti=length-1;i>1;i--) {
booleanflag=true;
for(intj=2;j<i;j++) {
if(i%j==0) {
flag=false;
break;
}
}
if(flag==true)
returni;
}
return -1;
}
g) 性能:在没有哈希冲突的情况下ASL=O(1),随着哈希冲突的增多,会使效率变低
1. 查找
a) 概念:给定某个值,在给定的数据结构中寻找指定数据元素的过程
b) 分类
i. 若在查找的同时需要进行插入和删除叫动态查找,否则叫静态查找
ii. 从逻辑上说,对应不同数据结构分为线性表、树表、哈希表三种查找技术
c) 效率:通常把查找过程中对关键字执行的平均比较次数ASL作为标准
i. ASL=,其中n为节点个数,c为每个节点需要比较的次数
2. 线性查找
a) 顺序查找
i. 基本思想:从表的一段开始扫描,若扫描到元素与给定值key相等,结束扫描,返回下标,若扫描完表也没找到,返回-1
ii. 代码实现:略
iii. 效率:ASL=N
iv. 使用范围:在以下两种情况只能采用顺序查找
[1] 顺序表为无序表
[2] 链表
b) 二分查找
i. 思想:设置start、end、mid,mid等于中间值,比较mid的值与给定的key值大小,如果mid值大于key,则令end=mid-1,mid=(start+end)/2,如果mid值小于key,则start=mid+1,mid=(start+end)/2,继续查找,找到则返回下标,如果start==end还是没找到返回-1
ii. 代码实现:略
iii. 效率:ASL=log(n+1)-1
iv. 使用范围:有序的顺序表
c) 分块查找:索引顺序查找
i. 思想:将待排序数组分为几块,每块内求出块内的最大值,然后用key与这些最大值比较,确定key所在的块,然后再在块内顺序查找
ii. 代码实现:略
iii. 效率:ASL=n^(0.5)
3. 树表查找
a) 提高动态查找表的效率
b) 二叉排序树(二叉查找树)
c) 代码实现:见第五章
d) 查找效率:ASL=logn
4. 哈希表查找
a) 概念:将键值转换为偏移地址来检索记录,键值转换地址通过哈希函数完成
b) 基本思想:假设一个有n个记录的集合设置一个长为m的表,将n个记录的关键字尽可能的转换为0~m-1之间的数值
c) 哈希冲突:不同记录的键值通过哈希函数转换得到的地址相同
d) 构建哈希函数
i. 平方取中法,求关键字的平方,然后根据表长度取中间几位数,如关键字为0100、0110,平方后为0010000、0012100,假设表长为1000,则取中间三位数字100、121
ii. 除余法:地址=关键字%p,p为小于表长m且最接近m的质数
iii. 折叠偏移法:将关键字分为几段,将这几段的值相加,根据表长舍去最高几位得到的地址,如47669,将其拆为1位,3位,1位向加为4+766+9=779,假如表长尾100,则舍去7,地址尾79;
e) 解决哈希冲突
i. 开放定址法
[1] 首先取一部分数据放入对应地址,当表中i,i+1,…i+k已经存在元素时,如果有后续具有哈希冲突的元素将要放入I,i+1,…i+k的地址时,逐渐向后搜索直到有位置没有插入过元素放入该元素。
ii. 链表法
[1] 若选择的表长尾m,则创建一个长度为m由头指针组成的单链表数组,将具有相同哈希值的元素按顺序添加入对应的单链表中
f) 代码实现:以除余法和链表法构建哈希表
//构建哈希节点类
class HashNode{
privateinte;
private HashNode next;
public HashNode() {
this.e=0;
this.next=null;
}
public HashNode(inte) {
super();
this.e = e;
this.next = null;
}
publicint getE() {
returne;
}
publicvoid setE(inte) {
this.e = e;
}
public HashNode getNext() {
returnnext;
}
publicvoid setNext(HashNode next) {
this.next = next;
}
}
//哈希表查找
publicstaticboolean hashtableSearch(intdata[],intkey) {
HashNode[] nodes=createHashTable(data);
inthash=key%getPrimes(data.length);//获取待查找元素的hash值
HashNode temp=nodes[hash]; //从hash表中找出待查元素
while(temp!=null) {
if(temp.getE()==key)
returntrue;
else
temp=temp.getNext();
}
returnfalse;
}
//构建哈希表
publicstatic HashNode[] createHashTable(intdata[]) {
intlen=data.length;
inthash;
intprimes=getPrimes(len); //找到被除数(小于表长且最接近表长的质数)
HashNode temp;
HashNode[] nodes=new HashNode[len];
for(inti=0;i<len;i++) {
hash=data[i]%primes;
temp=nodes[hash];
HashNode node=new HashNode(data[i]);
if(temp==null)//如果这是插入该链表的第一个元素,这个节点作为首节点
nodes[hash]=node;
else {
while(temp.getNext()!=null) {
temp=temp.getNext();
}
temp.setNext(node);
}
}
returnnodes;
}
//求取余法的被除数(小于表长最接近表长的质数)
publicstaticint getPrimes(intlength) {
for(inti=length-1;i>1;i--) {
booleanflag=true;
for(intj=2;j<i;j++) {
if(i%j==0) {
flag=false;
break;
}
}
if(flag==true)
returni;
}
return -1;
}
g) 性能:在没有哈希冲突的情况下ASL=O(1),随着哈希冲突的增多,会使效率变低