基本算法设计

主要介绍七种基本的算法基础设计
1.枚举
2.递推
3.递归
3.贪心算法
5.分治算法
6.哈希
7.二分法

1.枚举

枚举算法是指从可能的解集中一一列举出各个元素,用题目给定的约束条件判断哪些是满足题目条件的,哪些是不满足条件的,满足条件的即为可行解。
枚举算法分为两步:1)确定解的范围,并逐一列举其中的元素;2)对列举每一个可能的解进行检验。

2.递推

  • 谈到递推,我们很容易联想到高中数学里的数列的等差公式、等比公式,这些递推公式按一定的规律来计算序列中的每个项。
  • 递推有两种顺序,顺推法和逆推法。顺推法是从已知条件出发,逐步推算出要解决的问题,如斐波拉契数列,已知f(1)=1,f(2)=1,f(n)=f(n-2)+f(n-1)(n>2)······;逆推法则是从已知问题的结果出发,用迭代表达式逐步推算出问题的开始条件。

3.递归

编程中常用的递归调用函数即用了递归的算法思想。

  • 递归是指由一种(或多种)简单的基本情况定义的一类对象或方法,并规定其他所有情况都能被还原成基本情况。递归的本质是自己调用自己的过程。例如:
  • 小明的父母是小明的祖先(基本情况);
    小明祖先的父母同样是小明的祖先(递推情况)。
  • 递归算法的步骤体现了一种过程的“重复”或者叫“自相似”,它有三个要求:1)每次调用在规模上有所缩小,最后可保证问题达到直接求解的规模,这样原问题便能得到结果,否则程序将无限陷入递归调用之中;2)相邻两次重复之间有紧密的练习;3)在问题规模极小时,必须直接给出解答而不再进行递归调用,因此每次递归调用都是有条件的。
  • 递归和递推:递归是将问题规模为n的问题,降解成若干规模为n-1的问题,依次降解,直到问题规模可求,求出低阶规模的解。代入高阶问题中,直至求出规模为n的问题的解。递推是构造低阶的规模(如规模为i)的问题,并求出解推导出问题规模为i+1的问题及解,依次递推到规模为n的问题。

4.贪心算法

贪心算法能够对某些求最优解问题给出更简单、更迅速的实现,对大多数优化问题能够产生最优解,是一种应用广泛的算法。

  • 贪心算法通过作出一系列选择来求出问题的最优解,在每个决策点,做出在当前看来最好的选择,自上而下在每个步骤都选择局部最优解来构造全局最优解。

5.分治算法

  • 分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可以得到原问题的解。
  • 分治一般伴随着递归。
  • 利用分治求解时,所需时间取决于分解后子问题的个数,子问题的规模大小等因素,二分法是一种常采用的有效方法,如二分法检索。

6.哈希

在程序设计中,常常需要使用数据结构来维护一个动态集合,并且支持在该动态集合中进行查询、插入和删除元素等操作。

  • 直接寻址表:当所维护集合元素关键字为自然数,且全域较小时,利用数组的下标直接映射整个集合的关键字全域。查询、插入、删除的时间复杂度均为O(1)。实际应用中,可以在直接寻址表中直接存储集合中的元素,而不是用指针数组记录对应关键字数据所存储的地址,从而节省这部分指针数组的额外空间开销。
  • 哈希表:令关键字全域为K,元素集合为S,且|K|>|S|,为了使得最后的空间消耗为O(|S|),对关键字做一遍哈希处理:将所有元素存储在长度|S|的表中,需要对关键字k用哈希函数h做哈希处理得到k的哈希值h(k),0 1.哈希函数的设计**:除法哈希函数是通过取关键字k除以|S|的余数作为关键字的哈希值。
    除法哈希函数为h(k)=k mod |S|。该函数只需使用一次取模运算,时间复杂度为O(1)。
    哈希表长度大小m一般选择的并不是|S|,而是附近的一个素数,且满足该素数离最近的2的幂比较远。对于选定好的m的值,改写后的除法哈希函数为:h(k)=k mod m。
    2.冲突
    冲突,是指多个关键字在同一个哈希函数作用下,得到了相同的哈希值。显然,如果用数组来存储集合中的元素,同一个位置是无法存储多个元素的。
    接下来介绍两种解决冲突的方法:

3.链接法:
将所有冲突的元素放入一个链表,整个哈希表的结构类似一个邻接表结构。表中的每个位置存放的不是指向元素地址的指针或者元素本身,而是对应哈希值所在链表的头指针。

4.开放寻址法
必须满足m>=|S|。开放寻址法将对哈希函数进行扩充,使得探查函数作为第二个参数传入函数,即h(k,i)。h(k,i)对于某个关键字k可以生成一个探查序列,并且满足探查序列为(0,1,······,m-1)的一个序列。有三种计算探查序列的方法;线性探查、二次探查和双重探查。

7.二分法

二分查找:
当集合是静态的,且有序存储在数组中时,可以使用二分查找来优化查找操作,使得每次查找的最坏时间复杂度为O(log2 n)。
假设所有元素有序递增存储在数组A[1·····n],待查数key,二分查找思路如下:
伪代码:
1)查找区间为[low, high],如果low>high,可以确定key不存在与数组中,否则确定当前区间重点为mid=(low+high)/2;
2)将A[mid]与key比较,两值相等则返回当前中点位置;若A[mid]>key,则key位于数组A的左边,更新查找区间为[low,mid-1];反之更新查找区间为[mid+1,high]。

二分逼近:
对于一个给定数key,求取序列中key的前驱或后继,即询问集合中所有不小于Key的数中最小的那个数,或者集合中所欲不大于key的最大的那个数。

常见题型:
1.求一个字符串中出现至少k次的最长重复子串的长度。
分析:用二分和哈希表。如果在字符串中存在长度为l且出现至少k次的子串,则必然也存在长度为l-1且出现至少K次的子串。可以通过二分长度后验证满足条件的最大长度。当二分长度为l后,只需遍历字符串中所有长度为l的子串。可以使用字符串哈希的方法将所有长度为l的子串用整数代替,存入哈希表内维护。如果在哈希表中有一个字符串出现次数超过k,则代表存在长度为l的子串出现了至少K次。

你可能感兴趣的:(数据结构算法基础,二分法,哈希表,递归与递推,贪心算法)