数据结构面试整理(2)
来源:互联网 发布:仓库管理源码php 编辑:程序博客网 时间:2024/05/21 17:18
4. Hash表的hash函数,冲突解决方法有哪些
Hash表的基本思想:首先在元素的关键字k和元素的存储位置p之间建立一个对应关系f,使得p=f(k),f称为哈希函数。创建哈希表时,把关键字为k的元素直接存入地址为f(k)的单元;以后当查找关键字为k的元素时,再利用哈希函数计算出该元素的存储位置p=f(k),从而达到按关键字直接存取元素的目的。
散列函数:
1) 直接地址
取关键字或关键字的某个线性函数值为哈希地址:H(key)=key 或 H(key)=a·key+b,其中a和b为常数,这种哈希函数叫做自身函数。
2)乘法
该方法包括两个步骤:首先用关键字key乘上某个常数
该方法最大的优点是m的选取比除余法要求更低。
3)除法
取关键字被数p除后所得余数为哈希址:
4)分段叠加法
将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法(folding)。
5)伪随机
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即
6)查表法
有一个关键字表,通过关键字可以在该表查询hash值
7)平方取中法
取关键字平方后的中间几位为哈希地址。
解决冲突
1)开放地址法
a)线性探测法
插入元素时,如果发生冲突,算法会简单的从该槽位置向后循环遍历hash表,直到找到表中的下一个空槽,并将该元素放入该槽中,这会使得相同hash值的元素紧挨在一起导致其他hash值的槽被占用。查找元素时,首先散列值所指向的槽,如果没有找到匹配,则继续从该槽遍历hash表,直到:(1)找到相应的元素;(2)找到一个空槽,指示查找的元素不存在,(所以不能随便删除元素);(3)整个hash表遍历完毕(指示该元素不存在并且hash表是满的)
缺点:
① 处理溢出需另编程序。一般可另外设立一个溢出表,专门用来存放上述哈希表中放不下的记录。此溢出表最简单的结构是顺序表,查找方法可用顺序查找。
② 删除工作非常困难。如果将此元素删除,查找的时会发现空槽,则会认为要找的元素不存在。只能标上已被删除的标记,否则,将会影响以后的查找。
③ 容易产生堆聚现象。所谓堆聚现象,就是存入哈希表的记录在表中连成一片。按照线性探测法处理冲突,如果生成哈希地址的连续序列愈长 ( 即不同关键字值的哈希地址相邻在一起愈长 ) ,则当新的记录加入该表时,与这个序列发生冲突的可能性愈大。因此,哈希地址的较长连续序列比较短连续序列生长得快,这就意味着,一旦出现堆聚 ( 伴随着冲突 ) ,就将引起进一步的堆聚。
b)线性补偿探测法
基本思想是:将线性探测的步长从1改为Q,即将上述算法中的
c)伪随机探测
基本思想是:将线性探测的步长从常数改为随机数,即令:
2)拉链法
基本思想:将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数 组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于 1,但一般均取α≤1。
优点:
①拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
②由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
③开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;
④删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。
缺点:
指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度。
5. 各种排序:冒泡、选择、插入、希尔、归并、快排、堆排、桶排、基数的原理、平均时间复杂度、最坏时间复杂度、空间复杂度、是否稳定
5.1冒泡法
/** * 冒泡排序 * 思路:内部循环每走一趟排好一位,依次向后排序 */private static void bubbleSort(int[] data) { int temp; for (int i = 0; i < data.length; i++) {//每一次确定第i小的数,第i位数据与i之后的数据 for (int j = i+1; j < data.length; j++) { if (data[i]>data[j]) { temp =data[i]; data[i]=data[j]; data[j] = temp; } } }}
5.2 选择法
/** * 选择排序 * 思路:每次循环得到最小值的下标,然后交换数据。 * 如果交换的位置不等于原来的位置,则不交换。 */public static void selectSort(int[] data){ int index=0; for (int i = 0; i < data.length; i++) { index = i;//保存最小值下标 for (int j = i; j < data.length; j++) { if (data[index]>data[j]) { index = j; } } if (index != i) { swap(data,index,i); } }}
5.3 插入排序
/** * 插入排序 * 思路:将数据插入到已排序的数组中。 */ public static void InsertSort(int[] data) { int temp; for (int i = 1; i < data.length; i++) { temp = data[i];//保存待插入的数值 int j = i; for (; j>0 && temp<data[j-1]; j--) { data[j] = data[j-1]; //如果带插入的数值前面的元素比该值大,就向后移动一位 } //内部循环结束,找到插入的位置赋值即可。 data[j]=temp; } }
5.4 希尔排序
参考:白话经典算法系列之三 希尔排序的实现
希尔排序的实质就是分组插入排序,该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。基本思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的。
/** * 希尔排序(缩减增量排序) * 想想也不难。 * 思路:三层循环 * 第一层循环:控制增量-增量随着程序的进行依次递减一半 * 第二层循环:遍历数组 * 第三层循环:比较元素,交换元素。 * 这里需要注意的是:比较的两个元素和交换的两个元素是不同的。 */public static void shellSort(int[] data) { int k; for (int div = data.length/2; div>0; div/=2) { for (int j = div; j < data.length; j++) { int temp = data[j]; for (k=j; k>=div && temp<data[k-div] ; k-=div) { data[k] = data[k-div]; } data[k] = temp; } }}
5.5 归并排序
基本思路:将两个已经排好序的数组插入到第三个数组当中。分割并排序
/** *data 待排序数组 *temp 存储排序数组 *left 左边数组起始位置 *right 右边数组起始位置 *center 左右数组断点位置 */public static void mergeSort(int[] data,int[] temp,int left,int center,int right){ int leftEnd = center; int rightStar = center+1; int len = right-left+1; int tempPos = left;//排序数组下标 //将两个已经排序的数组进行比较,将元素添加到temp数组中保存。 while (left<=leftEnd&&rightStar<=right) { if (data[left]<=data[rightStar]) { temp[tempPos++] = data[left++]; }else { temp[tempPos++] = data[rightStar++]; } } //右数组空,左数组未空 while (left<=leftEnd) { temp[tempPos++]=data[left++]; } //左数组空,右数组未空 while (rightStar<=right) { temp[tempPos++]=data[rightStar++]; } //将排序结果拷贝回原来的数组 for (int i = 0; i < len; i++,right--) { data[right]=temp[right]; }}
5.6 快速排序
基本思想是:1)先从数列中取出一个数作为基准数;2)分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边;3)再对左右区间重复第二步,直到各区间只有一个数。
参考:白话经典算法系列之六 快速排序 快速搞定
5.7 堆排序
白话经典算法系列之七 堆与堆排序
5.8 桶排序
- 数据结构面试整理(2)
- 数据结构面试整理(1)
- 数据结构面试整理(3)
- 数据结构面试整理(4)
- 整理:数据结构面试大全
- 整理:数据结构面试大全
- 数据结构面试知识点整理
- 面试整理(2)
- 转:整理:数据结构面试大全
- 数据结构学习之二叉树(面试易考题整理)
- java面试知识点整理之数据结构算法(待完善)
- 面试复习重点 算法 数据结构 【山科大牛陈磊整理】
- 面试复习重点 算法 数据结构 【山科大牛陈磊整理】
- C++面试宝典(整理版)2
- 面试整理2
- 面试整理(一)
- 面试整理(二)
- 面试小结三:关于算法数据结构的面试题整理(待续)
- Maven工程如何将本地jar包引入到工程中
- CSS之两栏固定布局(一)
- C语言图书管理系统
- web.xml配置详解
- <NOIP> 14 . P1047 校门外的树
- 数据结构面试整理(2)
- matlab 偏最小二乘法 函数plsregress
- 当 Direct3D 遇上中国古诗词
- crond和crontab调研
- Android EventBus简单使用
- 实验测试admin_move_table的机制(1)
- 儿童编程微课堂-Scratch准备篇:一、Scratch简介
- oracle获取的执行计划区别及获取真实执行计划
- 列写三角函数表(C语言实现)