![](https://img-blog.csdnimg.cn/20201014180756930.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
Algorithm
文章平均质量分 62
taesimple
这个作者很懒,什么都没留下…
展开
-
子集和问题
问题描述:对给定集合A和整数K,求出A的所有和等于K的子集(可以包括其本身),如A = {10, 20, 30, 40, 50, 60}且K = 60,则满足条件的子集有{10, 20, 30}, {10, 50}, {20, 40}, {60}思路1:利用二进制法枚举子集,再找出满足条件的子集。此方法优点是思想简单,缺点是枚举本身效率不高代码:void process(int原创 2012-05-25 16:23:11 · 405 阅读 · 0 评论 -
选择排序(selection sort)
选择排序的时间复杂度为O(n^2)算法思想:每次遍历当前未排序部分,记录其中最大元素的下标,遍历完成后,将该元素与未排序部分的最后一个元素交换;每次遍历后未排序部分的长度减1代码如下:void swap(int * a, int * b){ assert(a != NULL); assert(b != NULL); if(a != b){ *a ^= *b; *原创 2012-05-21 22:56:33 · 204 阅读 · 0 评论 -
快速排序(quick sort)
基本概念快速排序的最差时间性能为O(n^2),期望时间性能为O(n*logn)快速排序和归并排序一样,都属于分治法,其三个步骤如下分解(关键步骤):将序列(A[p], ..., A[r])划分成两个(可能空)的子序列(A[p], ..., A[q-1])和(A[q+1], ..., A[r]),使得(A[p], ..., A[q-1])中的任意元素均小于等于A[q],(A[q+1]原创 2011-07-26 19:50:54 · 254 阅读 · 0 评论 -
分治法+一趟排序求数组中第K大的元素
算法思想见题库代码如下:void swap(int * a, int * b){ assert(a != NULL); assert(b != NULL); if(a != b){ *a ^= *b; *b ^= *a; *a ^= *b; }}int partition(int a[], int left, int right){ assert(a !=原创 2012-05-08 16:43:11 · 739 阅读 · 0 评论 -
求和为K的所有连续正整数序列
思路:维护两个指针p和q,分别指向当前序列和sum所对应序列的起点和终点。若sum N则p右移且sum减小;若sum == N则输出p、q所指序列,然后q继续右移,如此循环。程序中止条件为q > (N+1)/2,原因是:若序列存在一个或多个大于等于(N+1)/2的元素,则其和必然大于N代码:void process(int n){ assert(n > 0); int p原创 2012-05-19 19:52:42 · 413 阅读 · 0 评论 -
最优二叉查找树问题
问题描述(详见算法导论P212-P213)对于给定关键字序列,构造一颗最优的二叉查找树T,使得在T内的一次搜索的期望代价最小前提概念一颗最优二叉树不一定是一颗整体高度最小的树;也不一定总把具有最大概率的关键字作为根节点二叉查找树的子树必定包含连续范围内的关键字当一颗树成为一个节点的子树时,它的期望代价增加值为该树中所有概率的总和最优子结构设包含有序关键字原创 2011-07-06 15:09:08 · 591 阅读 · 0 评论 -
单链表快排
在单链表上实现快排,需要考虑以下几个因素:单链表存取随机结点需要从头遍历指针单向,取前驱结点需要从头遍历不能像数组那样以下标判断越界因此,我们使用一趟排序中的策略三,其好处是:只要维护足够多(实际上也并不多)的指针,就能只遍历一次即完成一趟排序但和数组快排不同的是:为了方便,不使用三数中值分割法选取pivot(需要遍历一次链表),而直接使用当前子链表的第一个结点作为原创 2012-05-09 17:10:36 · 1099 阅读 · 0 评论 -
判断一个数是不是某个自然数的平方
基本思路是二分查找,只是判断标准变为x^2 ?= key改进思路是缩小二分查找的起始区间,步骤如下:设被查找数为N,令A = 1,B = N每次令 A *= 2, B /= 2,直到A >= B停止此时,若A == B,则N是A的平方;若A > B,则二分查找的初始区间为[B, A]若二分查找失败,则N不是某个自然数的平方改进思路的证明:假设存在K,使得2^K >= N/(2^原创 2012-05-14 20:58:17 · 914 阅读 · 0 评论 -
利用bit数组进行桶排序
本机器上int类型大小为4字节,即32bit,因此使用bit数组能使桶排序的空间开销缩小为原来的1/32bit数组可用于hash法去除重复元素或统计出现次数代码如下:#define SHIFT sizeof(int)*8 //32bitint main(){ int bit_array[100] = {0}; //此bit数组支持的整数范围为1~3199 int原创 2012-05-15 15:42:47 · 318 阅读 · 0 评论 -
最短长度的珠子
题目要求和分析见题库这儿只讲一点:p1和p2停下,得到序列,和当前最短序列比较完毕后,class为k-1(k为颜色数)。此时要保持class和color[],p1和p2重复那两个步骤,直至class再次等于k,会再次得到序列,再去和当前最短序列比较,如此循环。程序中止条件是p2越过最后一个元素循环至头一个元素(珠子是首尾相接的)代码如下:void shortest_chai原创 2012-05-15 20:40:07 · 207 阅读 · 0 评论 -
shell排序(shell sort)
基本概念shell排序又叫缩小增量排序,其基本思路是:使用一个增量序列(h1, h2, ..., hn),其中h1 = 1。对应地有n趟对子序列(子序列由增量决定)的插入排序,第一趟增量为hn,第二趟为hn-1,……,依次递减。当增量为hk时排序结束后应确保对每一个i我们有a[i] ≤ a[i+hk]shell的时间复杂度与增量序列选取有关,目前最优增量序列情况下,时间复杂度约为O(n^1原创 2011-08-30 19:45:12 · 1676 阅读 · 0 评论 -
归并排序(merge sort)
归并排序时间复杂度为O(n*logn),需要和待排序列相同大小的辅助空间,是一种稳定排序非递归实现的归并排序效率要高得多归并排序属于分治法的一种,分治法定义为:将原问题划分为n个规模较小而结构与原问题相似的子问题,递归解决这些子问题,然后合并结果,得出原问题的解。因此分治法有以下三个步骤:分解:将原问题分解为一系列结构相似的子问题递归求解:对子问题递归求解合并(非必须,如二分查找、原创 2011-07-01 13:45:00 · 218 阅读 · 0 评论 -
八皇后问题
问题描述:在8×8的棋盘上放置8个皇后,确保她们互不攻击(皇后攻击范围为同行、同列以及同对角线)思路:首先可以确定在此问题的任意一个正确解里,8个皇后必然各占一行(不能同行),因此问题可以简化为:为每一行的皇后找到正确的对应列(列必然也是互异的)。若用穷举法(全排列)的话,共有8! = 40320种可能。在此基础上,我们使用回溯法减少不必要的枚举,具体为:每次放置一个皇后,若该皇后与之前放置的原创 2012-05-25 19:43:12 · 292 阅读 · 0 评论 -
字符串全排列
字符串全排列的两个例子:1.abc的全排列有:abcacbbacbcacbacab2.aab的全排列有:aabababaa思路:使用回溯法。对于abc,a和a交换仍是abc;a和b交换可得bac;a和c交换可得cba,至此第一个字母的遍历结束。从上述结果中进行第二个字母的遍历:对于abc,b和b交换仍是abc,b和c交换可得acb;对于bac原创 2012-05-23 21:21:46 · 426 阅读 · 0 评论 -
枚举子集问题
问题描述:枚举给定集合的所有子集,包括只含一个元素的集合以及给定集合本身。当集合中出现值相同的元素时,根据不同性质可将子集分为:允许重复包含相同值的元素不允许重复包含相同值的元素思路:利用二进制的“开关”特性枚举,具体为:假设给定集合A大小为n,则想象A = {a[0], a[1], ..., a[n-1]}的每个元素对应一个开关位(0或1),0表示不出现,1表示出现。原创 2012-05-25 15:45:07 · 674 阅读 · 0 评论 -
单链表逆输出
实际上递归实现单链表逆输出不一定优于辅助堆栈或辅助数组,只是可读性更强代码:typedef struct node{ int data; struct node * next;} Node;void print(Node * p){ assert(p != NULL); if(p->next != NULL) //递归,先输出后继结点 print(p->n原创 2012-05-23 14:36:52 · 259 阅读 · 0 评论 -
任意多次交换两数组元素使得两数组之和的差值最小
思想:注意两数组长度不一定相同。现只能想到穷举法(时间复杂度大约为O(n^3)):记当前两数组之和的差值为abs(diff),其中diff=sum_A-sum_B,遍历所有组合(a[i], b[j]),察看abs(diff-2*(a[i]-b[j]))与abs(diff)的大小,若前者更小,则更新diff -= 2*(a[i]-b[j])并swap(a[i], b[j]);若找不到这样的组合,则当原创 2012-05-23 09:46:32 · 552 阅读 · 0 评论 -
常用排序算法总结
排序方法平均时间最坏情况辅助存储稳定性备注冒泡排序O(n^2)O(n^2)O(1)稳定 插入排序O(n^2)O(n^2)O(1)稳定 选择排序O(n^2)O(n^2)O(1)不稳定 快速排序O(n*logn)O(n^2)O(logn) ~ O(n)不稳定原创 2012-05-22 10:20:40 · 279 阅读 · 0 评论 -
冒泡排序(bubble sort)
冒泡排序的时间复杂度为O(n^2),是一种原地排序,是一种稳定排序冒泡排序的思想是:每趟冒泡将序列中未排序部分的相邻两元素比较交换,确保将其中的最大元素“沉底”,未排序部分的长度每趟减1,第K趟冒泡可将原序列的第K大元素“沉底”冒泡排序算法思想简单,但效率很低。相比于直接插入排序,虽然时间复杂度均为O(n^2),但冒泡排序的“交换”次数要多很多,因此直接插入排序要优于冒泡排序代原创 2012-05-21 22:35:49 · 200 阅读 · 0 评论 -
插入排序(insertion sort)
插入排序的平均时间复杂度和最差时间复杂度均为O(n^2)插入排序是一种原地排序方法基本思想:将序列分为“已排序”和“未排序”的左右两部分,每次对“未排序”部分的首元素排序,具体做法是:从“已排序”部分的尾部向头部扫描,找到合适位置将该元素插入(实际代码实现时,寻找插入位置和后移同时进行),“已排序”部分长度加1,“未排序”部分长度减1。初始化状态为:“已排序”部分长度为1(只包原创 2011-07-27 09:16:45 · 163 阅读 · 0 评论 -
基数排序(radix sort)
讨论基数排序之前,先来看桶式排序桶式排序桶式排序的原理是:利用待排序序列元素的值作为新序列的索引进行插入,完成新序列的建立后,只需对新序列进行遍历即可完成排序(具体遍历过程和新序列元素的值有关)假设有N个整数的序列,元素的值的范围是0到M-1。现建立一个名为Count的桶序列,长度为M,并初始化为0。于是,Count有M个桶,开始时都是空的。对待排序序列进行遍历,当遍历至原序列原创 2011-07-18 20:41:55 · 688 阅读 · 0 评论 -
找单链表的倒数第K个结点
思路:只需遍历一次单链表,维护两个指针p和q,初始时均指向头结点。p先移动k-1步,然后p和q同时移动直至p指向尾结点,此时q所指的结点即倒数第K个结点代码:typedef struct node{ int data; struct node * next;} Node;Node * process(Node * L, int k){ assert(L != NUL原创 2012-05-18 19:18:50 · 934 阅读 · 0 评论 -
判断单链表存储的回文
思路:维护两个指针slow和fast,其中fast每次步进2,slow每次步进1,并且slow在步进过程中对单链表逆置。当fast遍历至尾结点时,slow恰好在链表中点位置,而链表前半段已全部被逆置。此时维护两个指针从中心出发向两头遍历判断回文。需要注意的一点:回文有奇偶两种长度,在fast步进过程中计数判断是哪种,据此设置中心指针的初始位置代码:typedef struct原创 2012-05-18 21:56:55 · 505 阅读 · 0 评论 -
KMP算法
算法概述KMP算法的基本思想是:通过分析模式串P,得出一个next[]数组,以指示:当源串字符S[i]与P[j]不匹配时,下一步应比较S[i]和P[next[j]],若仍不匹配,则比较S[i]和P[next[next[j]]]...如此循环。因此在整个过程中,指向S的指针i是始终不回溯的,指向P的指针j会根据当前匹配状况按next[]数组的值回溯。假设S和P的长度分别为n和m,则KMP算法的时原创 2012-05-16 18:12:32 · 172 阅读 · 0 评论 -
栈和队列的相互模拟
1.用两个栈实现队列记两个栈为s1和s2,操作要求:入队时,若s2不为空,则将s2中的所有元素逐个出栈并压入s1中,然后再将需要入队的元素压入s1出队时,若s1不为空,则将s1中的所有元素逐个出栈并压入s2中,然后将s2的栈顶元素出栈需要注意的是,在入队/出队操作前,需要保证已入队的所有元素均在其中一个栈内,而不能同时分布在两个栈上2.用两个队列实现栈记两个队列原创 2012-05-29 09:03:12 · 262 阅读 · 0 评论 -
找前K大的所有数
问题描述:注意和“找第K大的数”相区别方法1:可利用“找第K大的数”问题的解法,经过多次一趟排序,找出第K大的数,那么该数以及该数左边的所有数即所求的“前K大的所有数”(一趟排序的性质)方法2:利用一个大小为K的小顶堆进行辅助操作,具体方法为:将数组前K个元素建成一个小顶堆扫描数组剩下的N-K个元素,每次扫描一个并进行判断:若该元素大于(不包括等于)小顶堆的堆顶元素,则用该元素原创 2012-05-28 21:06:25 · 1609 阅读 · 0 评论 -
将数组分成和相等的若干子数组
问题描述:对一个整数数组,将其分为k个子数组,使得各数组的和相等。求k的最大值思路:1.记数组a中的最大元素为max,a本身的和为sum,则m的值不会超过ceil(sum/max)。因为若m > ceil(sum, max),则max不可能被放到任何一个子数组中2.在1的基础上,记k为当前分割份数,k从ceil(sum, max)遍历至1,判断能否分成k个相等和子数组,具体判断方法为:原创 2012-05-27 18:39:30 · 1424 阅读 · 1 评论 -
高精度运算总结
目前在oj上遇到的高精度运算有以下几类高精度整数+高精度整数(poj 2413)高精度整数×普通整数(poj 1001、poj 1306)高精度整数×高精度整数(poj 1405)普通整数÷普通整数(商为高精度浮点数)(poj 1131)高精度数÷普通整数(poj 1220、poj 1306)高精度整数+高精度整数:简单。用两个整数数组分别保存加数(a[0]保存最低位),然后逐原创 2011-08-07 16:20:51 · 313 阅读 · 0 评论 -
质数环问题
问题描述:给定正整数n,要求将n个正整数1, 2, ..., n组成一个环,确保环中任意两个相邻元素之和为质数思路:回溯法。在枚举过程中进行回溯剪枝。另外,为确保不出现重复的环,令第一个元素为1代码:int isp(int k){ //判断k是否为质数 int ok = 1; for(int i = 2; i < sqrt(k); i++){ if(k%i == 0){原创 2012-05-27 15:12:55 · 1035 阅读 · 1 评论 -
堆排序(heap sort)
堆和堆排序堆排序结合了合并排序和插入排序的优点,其时间复杂度为O(n*logn),且是一种原地排序算法(在任何时候,数组中只有常数个元素存储在输入数组以外)堆排序采用了名为“堆”的数据结构,分为大顶堆和小顶堆。堆排序采用了大顶堆,其定义是:一颗完全二叉树,且除根结点外的任意结点的值至多和其父结点的值一样大。小顶堆组织方式刚好相反,其通常用在构造优先队列时使用为方面起见,实际代码中用数组原创 2011-07-28 10:57:46 · 244 阅读 · 0 评论 -
特殊矩阵运算
问题描述:假设矩阵存在某种特殊运算,要求对矩阵任意一个元素减1时,必须对其邻居(上下左右)中的一个同时减1。现给定一个矩阵,要求判断能否通过有限次特殊运算得到零矩阵思路:回溯法。枚举所有可进行特殊运算的位置并以此递归,若出现元素小于0的情况则进行回溯。不用每次判断零矩阵,只需要维护一个计数变量cnt,统计当前矩阵非零元素个数。每次进行特殊运算时,更新cnt的值代码(效率较低,应该可以进一步原创 2012-05-27 19:59:33 · 356 阅读 · 0 评论 -
利用表实现一元多项式的加法和乘法
这里采用顺序表即数组,因为进行的随机访问操作较多,而插入、删除操作较少关键点:对保存一元多项式的数据结构进行置0的初始化,这便于减少对系数为0的判断以及乘法合并同类项加法很简单,对应幂系数相加即可,加和的幂是两个加数的幂的最大值乘法:乘积的幂是两个乘数的幂的和代码:#define原创 2011-07-16 17:14:51 · 416 阅读 · 1 评论 -
大数乘法
这里的大数是指:超过计算机变量(包括long long)表示范围的数因此我们考虑用字符串保存两个乘数以及结果,按位相乘需要注意两点:记两乘数分别为X和Y,其中X有m位,Y有n位,则Z=X*Y可能有m+n位或m+n-1位。如10*10=100, 99*99=9801。因此计算结果可能存在1位前置0,需要判断处理每一轮相乘完毕后,最后的进位(cf)记得加到被乘数的对应位上原创 2012-05-10 15:19:07 · 614 阅读 · 0 评论 -
欧几里德算法(辗转相除法)求两数的最大公因数
欧几里德原理a,b为两个正整数,记gcd(a, b)表示a和b的最大公因数(a>b),则必然有:gcd(a, b) = gcd(b, a%b),其中a%b不为0证明如下:1.a可以表示为a = k*b+r,即r = a%b = a-k*b2.记d = gcd(a, b),即有:a%d = 0且b%d = 03.根据步骤2,必然有:r%d = (a-k*b)%d = a%d -原创 2012-05-08 16:36:00 · 300 阅读 · 0 评论 -
装配线调度问题及DP的一点总结
DP的一点总结DP拥有以下两点特征(必要条件):最优子结构:问题的最优解包含了子问题的最优解。贪心算法和分治法同样具有此特征重叠子问题:解原问题的递归算法可反复地解同样的子问题,而不是总在产生新的子问题。分治法不同,它在递归的每一步都产生全新的子问题Tips:DP通常应用于最优化问题,即此类问题可能有很多种可行解,而我们希望找出一个具有最优值的解DP大致有四个步原创 2011-07-03 09:09:26 · 535 阅读 · 0 评论 -
矩阵链乘法问题
问题描述(详见算法导论P197-P198):已知:给定n个矩阵构成的一个矩阵链(A1, A2, ..., An),矩阵Ai的维数为pi-1×pi求:决定该矩阵链的乘法结合顺序(即加括号),使得矩阵链乘法的运行时间最短几个前提概念矩阵链乘法的运行时间将以标量乘法(单行×单列)的次数来衡量A是p×q矩阵,B是q×r矩阵,则A×B的运行时间为pqr矩阵乘法满足结合律原创 2011-07-03 15:56:35 · 1350 阅读 · 0 评论 -
最长公共子序列(LCS)问题
问题描述:见算法导论P208-P209前提概念给定一个序列X = (x1, x2, ..., xm),对i = 0, 1, ..., m,记X的第i个前缀为Xi = (x1, x2, ..., xi),故Xm = X,而X0是个空序列一个给定序列的子序列就是该序列去掉0个或多个元素(不一定连续),如BCDB是ABCBDAB的一个子序列基于以上定义,最长公共子序列(LCS)有原创 2011-07-04 10:29:19 · 293 阅读 · 0 评论 -
最长上升子序列(LIS)问题
问题描述给定一个序列(a1, a2, ..., an),求它的一个子序列(ak1, ak2, ..., aki) (不一定连续),使得该子序列满足ak1 递归表达式作如下定义e(i):表示以ai为最后一个元素的最长子序列的长度由以上定义可得如下递归表达式则原问题最优解为max{e(1), e(2), ..., e(n)原创 2011-07-08 23:34:31 · 515 阅读 · 0 评论 -
最大子序列和问题
问题描述:给定一个整数序列(可能有负数),求一子序列(记为L‘)使得该子序列所有元素之和最大。例:给定序列-2,11,-4,13,-5,-2,则最大子序列和为20(11,-4,13)方法一:遍历穷举——O(n^2)略方法二:分治递归——O(n*logn)思路:将输入序列L分为左右两个子序列L1和L2,则L’只可能以下面三种情况出现:L‘完全在L1中,则L原创 2012-05-08 10:52:32 · 146 阅读 · 0 评论 -
判断数组中是否存在两数,其和等于给定值
思路:若数组无序,则先将数组排为升序。设给定值为sum,维护两个指针p和q,p指向数组头部,q指向数组尾部。若*p+*q sum,则q--;若*p+*q == sum,则存在这两个数;程序中止条件为p == q(因此避免了*p+*q == sum且p == q的情况)代码如下:int process(int a[], int n, int sum){ assert(a !=原创 2012-05-15 16:12:38 · 556 阅读 · 0 评论