经典算法揭秘
文章平均质量分 88
深入算法的背后逻辑,揭秘潜在意义。
苏叔叔
Isolation is not good for me!
展开
-
二分查找的改进--差值查找
差值查找 在二分查找中,我们每次比较都可以排除一半的数据量,这个已经是很高效了。如果利用关键字本身的信息,每次排除的数据量充分依赖于关键字的大小,则查找会更高效,这就是差值查找的思想。下面通过示例代码,比较二分查找和差值查找的不同,在不同中领略差值查找的改良之处。原创 2014-09-21 00:31:09 · 4382 阅读 · 8 评论 -
数据结构:外排序-多路归并
外排序 外排序问题的出现,主要是因为内存不够。当需要排序的数据量过多,以至于无法一次性把所有的数据都放入内存,这导致了外排序问题的出现。解决大数据量排序的方法是:先分块排序,后进行块合并。外排序步骤把原数据分成几段读入内存,以至于每一块都可以完整的在内存中进行排序,排序好后,写入外部存储设备。归并已排序好的数据块。这就是归并排序在外排序中的应用。 对每块数据进行排序,可以使用各种内排序方法:快速排序、归并排序、堆排序等。这个比较简单,下面模拟一个对排序好的数据块进行归并的过程。原创 2014-08-23 17:56:24 · 3774 阅读 · 1 评论 -
数据结构:最小生成树--Kruskal算法
Kruskal算法 求解最小生成树的另一种常见算法是Kruskal算法,它比Prim算法更直观。从直观上看,Kruskal算法的做法是:每次都从剩余边中选取权值最小的,当然,这条边不能使已有的边产生回路。手动求解会发现Kruskal算法异常简单,下面是一个例子算法说明 为了判断环的出现,我们换个角度来理解Kruskal算法的做法:初始时,把图中的n个顶点看成是独立的n个连通分量,从树的角度看,也是n个根节点。我们选边的标准是这样的:若边上的两个顶点从属于两个不同的连通分量,则此边可取原创 2014-08-07 11:45:37 · 10032 阅读 · 1 评论 -
数据结构:最小生成树--Prim算法
最小生成树 给定一无向带权图,顶点数是n,要使图连通只需n-1条边,若这n-1条边的权值和最小,则称有这n个顶点和n-1条边构成了图的最小生成树(minimum-cost spanning tree)。Prim算法 Prim算法是解决最小生成树的常用算法。它采取贪心策略,从指定的顶点开始寻找最小权值的邻接点。图G=<V,E>,初始时S={V0},把与V0相邻接,且边的权值最小的顶点加入到S。不断地把S中的顶点与V-S中顶点的最小权值边加入,直到所有顶点都已加入到S中。原创 2014-08-05 00:30:48 · 17105 阅读 · 3 评论 -
数据结构:点对之间最短距离--Floyd算法
Floyd算法 Dijkstra算法是用于解决单源最短路径问题的,Floyd算法则是解决点对之间最短路径问题的。Floyd算法的设计策略是动态规划,而Dijkstra采取的是贪心策略。当然,贪心算法就是动态规划的特例。算法思想 点对之间的最短路径只会有两种情况:两点之间有边相连,weight(Vi,Vj)即是最小的。通过另一点:中介点,两点相连,使weight(Vi,Vv)+weight(Vv,Vj)最小。Min_Distance(Vi,Vj)=min{weight(Vi,Vj)原创 2014-08-04 13:06:24 · 4835 阅读 · 0 评论 -
数据结构:单源最短路径--Dijkstra算法
单源最短路径 给定一带权图,图中每条边的权值是非负的,代表着两顶点之间的距离。指定图中的一顶点为源点,找出源点到其它顶点的最短路径和其长度的问题,即是单源最短路径问题。Dijkstra算法 求解单源最短路径问题的常用方法是Dijkstra(迪杰斯特拉)算法。该算法使用的是贪心策略:每次都找出剩余顶点中与源点距离最近的一个顶点。算法思想 带权图G=<V,E>,令S为已确定了最短路径顶点的集合,则可用V-S表示剩余未确定最短路径顶点的集合。假设V0是源点,则初始 S={V0}。原创 2014-08-03 21:28:06 · 10082 阅读 · 0 评论 -
数据结构:图--拓扑排序
拓扑排序 在实际应用中,有向图的边可以看做是顶点之间制约关系的描述。把顶点看作是一个个任务,则对于有向边<Vi,Vj>表明任务Vj的完成需等到任务Vi完成之后,也就是说任务Vi先于任务Vj完成。对于一个有向图,找出一个顶点序列,且序列满足:若顶点Vi和Vj之间有一条边<Vi,Vj>,则在此序列中顶点Vi必在顶点Vj之前。这样的一个序列就称为有向图的拓扑序列(topological order)。步骤从有向图中选取一个没有前驱(入度为0)的顶点输出。删除图中所有以它为起点的弧。原创 2014-08-03 01:10:21 · 8182 阅读 · 0 评论 -
数据结构:图的遍历--深度优先、广度优先
图的遍历是指从图中的某一顶点出发,按照一定的策略访问图中的每一个顶点。当然,每个顶点有且只能被访问一次。在图的遍历中,深度优先和广度优先是最常使用的两种遍历方式。这两种遍历方式对无向图和有向图都是适用的,并且都是从指定的顶点开始遍历的。先看下两种遍历方式的遍历规则:深度优先深度优先遍历也叫深度优先搜索(Depth First Search)。它的遍历规则:不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。原创 2014-08-02 11:20:15 · 66549 阅读 · 11 评论 -
串的匹配:朴素匹配&KMP算法
引言字符串的模式匹配是一种常用的操作。模式匹配(pattern matching),简单讲就是在文本(text,或者说母串str)中寻找一给定的模式(pattern)。通常文本都很大,而模式则比较短小。典型的例子如文本编辑和DNA分析。在进行文本编辑时,文本通常是一段话或一篇文章,而模式则常常是一个单词。若是对某个指定单词进行替换操作,则要在整篇文章中进行匹配,效率要求肯定是很高的。模式匹配的朴素算法最简单也最容易想到的是朴素匹配。何为朴素匹配,简单讲就是把模式串跟母串从左向右或从右向左一点一点比较:原创 2014-07-23 22:30:13 · 4386 阅读 · 1 评论 -
索引排序
索引排序在排序时,若是数据很复杂,对数据的移动显然是费时的。若把数据移动改为指针移动,则减少了操作复杂度。索引排序,也叫地址排序,就是这种排序思想。索引含义根据索引的含义不同,索引排序的算法上也主要分为两种。一、index[i]为array[i]最终在有序序列中的位置。二、index[i]为位置i上最终应存放元素的下标。即最终元素按array[index[0]]、array[index[1]]……有序。原创 2014-07-17 00:30:50 · 10443 阅读 · 0 评论 -
二分检索
二分检索二分检索(Binary Search)也叫二分查找,是应用于有序表上的一种检索方法。二分检索的思想是:由于序列已经有序,故不需要顺序遍历,每次只需和序列中间位置的元素进行比较即可,以此确定下次查找的位置。显然每次都可以排除一半的元素,很高效。原创 2014-07-13 15:20:32 · 3574 阅读 · 0 评论 -
桶排序
桶排序桶排序(Bucket Sort)是对基数排序的一个变种。在排序过程中没有用到计数数组,而是用不同的桶来暂时存储关键字。看一个示意图:整个过程就是在不断地分配、收集。在实际应用中,根据桶的创建策略,桶排序也有不同的写法。下面给出两种不同的桶创建方式。一、使用静态队列创建桶。二、使用二维数组模拟桶。原创 2014-07-11 01:07:57 · 2618 阅读 · 2 评论 -
基数排序
前言当序列中元素范围比较大时,就不适合使用计数排序。针对这种情况,就有了基数排序,这是一种按位排序。它仍然是以计数排序为基础。基数排序基数排序的基数:十进制数的基数自然是10,二进制的基数自然是2。通常有两种按位排序策略:1.高位优先法(most significant digit first,MSD):简单讲就是从高位排起。2.低位优先法(least significant digit first,LSD):它与高位优先相反,从低位排起。从排序效果上看,高位优先比较直观,但却涉及到递归的过程,故原创 2014-07-10 21:39:29 · 3417 阅读 · 0 评论 -
计数排序
前言一般的排序都是需要进行关键字的比较的。有没有不需要比较的的呢?有的,计数排序就是其中一种。计数排序假设输入序列都是0到k之间的整数,则可使用计数排序。具体操作是这样的:创建一个同类型同等大小的临时数组temp,用于备份输入序列。创建一个整型大小为k的数组count,用于统计序列中各元素出现的次数。接下来只需把备份序列从大到小放回原数组即可。一个示例图:原创 2014-07-09 22:09:15 · 2341 阅读 · 0 评论 -
归并排序:二路归并
归并排序(Merge Sort)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列。归并排序的具体做法:把原序列不断地递归等分,直至每等份只有一个元素,此时每等份都是有序的。相邻等份合并,不断合并,直至合并完全。二路归并归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序最常用的是二路归并,即把两个小的有序的序列和并成一个大的有序序列:合二为一原创 2014-06-26 17:40:33 · 9745 阅读 · 0 评论 -
交换排序:快速排序
快速排序(Quick Sort)也是一种交换排序,它在排序中采取了分治策略。快速排序的主要思想是:从待排序列中选取一元素作为轴值(也叫主元)。将序列中的剩余元素以该轴值为基准,分为左右两部分。左部分元素不大于轴值,右部分元素不小于轴值。轴值最终位于两部分的分割处。对左右两部分重复进行这样的分割,直至无可分割。原创 2014-06-14 13:47:47 · 4615 阅读 · 0 评论 -
交换排序:冒泡排序
交换排序:两两比较待排序记录的关键码,若是逆置,则交换,直到无逆置。其中最简单的交换排序是:冒泡排序。冒泡排序(Bubble Sort,也叫起泡排序):不断地比较相邻的记录,若是不满足排序要求,则交换。交换时,可从前向后,也可从后向前。看一个从前向后的排序过程:原创 2014-06-12 18:24:08 · 1934 阅读 · 0 评论 -
选择排序:堆排序
堆排序(Heap Sort):使用堆这种数据结构来实现排序。先看下堆的定义:最小堆(Min-Heap)是关键码序列{k0,k1,…,kn-1},它具有如下特性:ki<=k2i+1,ki<=k2i+2(i=0,1,…)简单讲:孩子的关键码值大于双亲的。同理可得,最大堆(Max-Heap)的定义:ki>=k2i+1,ki>=k2i+2(i=0,1,…)简单讲:孩子的关键码值大于双亲的。同理可得,最大堆(Max-Heap)的定义:ki>=k2i+1,ki>=k2i+2(i=0,1,…)同样的:对于最大堆,双亲的关原创 2014-06-11 20:56:42 · 2640 阅读 · 0 评论 -
选择排序:简单选择、树形选择
选择排序(Selection Sort):经过一趟排序,可以从n-i+1(i=1,2...)个记录中选取关键字最小的记录作为有序序列中第i个记录。也就是说,每一趟排序,都会排好一个元素的最终位置。其中最简单的是简单选择排序(Simple Selection Sort)简单选择排序的思想是 在每一趟排序中,通过n-i次关键字的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录原创 2014-06-10 17:21:33 · 7189 阅读 · 2 评论 -
插入排序:希尔排序
我们知道当一个序列基本有序时,直接插入会变得很高效。因为此时只需少量的移动元素,操作集中在元素的比较上。基于这种想法,我们就试图把一个序列在进行直接插入前调整得尽量有序。这就是希尔排序(Shell Sort)的核心思路。(Shell只是算法发明者的名字,无特殊含义)那到底该怎么做呢? 希尔排序一反以前的做法,插入为何只在相邻的元素之间进行,不相邻的同样可以进行。于是,希尔排序也被形象原创 2014-06-07 21:15:28 · 1970 阅读 · 1 评论 -
插入排序:表折半插入
在前一篇插入排序:表插入中,我们用静态链表的存储方式,直接插入的策略,构建了一种新的插入排序算法:表插入。有人可能会想到:同样是静态链表的形式,为什么不使用更高效的折半插入的策略呢?这种想法真的很好,如果做到了,显然是极大的优化。 我在网上还真看到了相关的内容,大家可看下《表插入方法的改进》,里面有此想法的介绍。这篇博客就是介绍表插入的另一种实现:表折半插入。看完一定让你彻底理解它。原创 2014-06-05 17:26:57 · 2215 阅读 · 0 评论 -
插入排序:表插入
所谓插入排序之表排序,是利用静态链表的形式,分两步完成排序。一,对一个有序的循环链表,插入一新的元素,修改每个节点的后继指针的指向,使顺着这个指针的指向,元素是有序的。在这个过程中,我们不移动或交换元素,只是修改指针的指向。二,顺着指针的指向调整元素的位置,使其在链表中真正做到物理有序。思路:构建一新的结构体类型,使其封装了值域和指针域。并增加一节点,当做头节点,为循环终止原创 2014-06-02 20:44:44 · 3960 阅读 · 1 评论 -
插入排序:二路插入
在上一篇博客中:插入排序:直接插入、交换插入、折半插入。提到了三种插入排序的具体实现。不过仍有改进的地方。例如序列 2 1 3,当把1往前插入时,由于1<2,则应当把1插入到2的前面。在上述三种插入排序方法的实现中,都是把1、2位置交换。于是,我们想有没有可能不进行交换,因为交换总是相当耗时的。但是1必须要排到2的前面,可2的前面没有位置了啊?嗯,初看是这样的。试想这是一个循环的数组呢?这就是二路插入最核心的想法。原创 2014-06-01 15:47:26 · 6734 阅读 · 6 评论 -
插入排序:直接插入、交换插入、折半插入
插入排序:把一个数插入到一个有序的序列中,并要求插入后此数据序列仍然有序。这种排序思想就是插入排序。 那么对于一个原始无序的序列,哪里找有序的部分呢?这个很简单,可以把序列分为两个部分,第一个元素是第一部分,其余元素是第二部分。第一个部分只有一个元素,当然是有序的!对于如何把其余元素插入到第一部分中去,按插入策略,可分为如下几种插入方法:原创 2014-05-28 21:13:38 · 17260 阅读 · 2 评论