代码改变世界

随笔分类 -  算法

《算法导论》第15章 动态规划 (1)装配线调度

2012-04-14 08:56 by htc开发, 243 阅读, 收藏,
摘要: 动态规划通常用于有很多种可行解,而找出最优解的问题。具体可分为4个步骤:1)描述最优解的结构。2)递归定义最优解的值。3)自底向上计算最优解的值。4)由最优解的值构造出最优解。下面通过一个具体问题来看究竟如何用动态规划算法来解决问题。Colonel汽车公司在有两条装配线的工厂里生成汽车。每一条装配线上有n个装配站,两条生产线上相同位置的装配站功能相同,但所需时间不同,并且汽车底盘在两条装配线间转移要花费一定的时间。如下图所示两条生产线。这里首先尝试下下一章的贪心算法,在每一步都取最省时间的装配站。首先进入装配线1时间为2 + 7小于装配线2的4 + 8,因此进入装配线1。之后装配站2的时间9大 阅读全文

《算法导论》第14章 数据结构的扩张 (2)

2012-04-09 23:03 by htc开发, 179 阅读, 收藏,
摘要: 在上一节中,我们为树结点添加size域表示每颗子树的大小,即包含的结点个数,扩张了二叉查找树为其增加顺序统计量的查找功能。更为自然的想法是直接添加顺序统计量rank域到每个树结点上。这一节我们就来看下在这样的设计下,如何扩张来完成上一节相同的功能。当我们插入一个结点到二叉树中,假设它的顺序统计量为5,那么之前二叉树中顺序统计量大于5的结点都要更新。也就是说插入一个新结点到对应的位置后,要不断地查找其后继,完成rank域的更新。所以可以结合习题14.2-1,再添加两个指针域prev和next指向前趋和后继,使查找前趋和后继在O(1)内完成。下面来看具体代码。// 添加三个新域 typedef s 阅读全文

《算法导论》第14章 数据结构的扩张 (1)动态顺序统计

2012-04-08 17:57 by htc开发, 230 阅读, 收藏,
摘要: 《数据结构扩张》是《算法导论》第三部分的最后一章。在介绍学习了这么多种数据结构之后,简要介绍了当这些基本数据结构不满足需求时,如何扩张它们来满足需求。这才是学习算法的目的,能够根据需求选择合适的数据结构和算法,并在无法满足需求时能够扩张它。这才是对算法的思想和本质的学习!可以将本章看做深入学习的前奏吧,因为紧接着就要开始进入第四部分《高级设计和分析技术》了。那么赶快来看看如何扩张数据结构,然后就进入高级部分的学习吧!1.如何扩张数据结构?1)选择基础数据结构2)确定要在基础数据结构中添加哪些信息3)验证可用基础数据结构上的基本操作来维护新添加的信息4)设计新的操作下面来看一个简单的数据扩张的例 阅读全文

关于算法学习的总结和感悟(原创)

2012-03-26 22:43 by htc开发, 411 阅读, 收藏,
摘要: 时隔一年重读《算法导论》,去年读到了二叉查找树就搁浅了,今年从头捡起,希望能走的更远一些。算上大学时的数据结构与算法课,今年可以算是第三波学习攻势了。随着学习的深入,对算法的学习渐渐有了些自己的看法和感悟。一.为什么学习算法?记得初学算法时不明白为什么费力分析程序的执行步骤后,还要用公式表达出来并求极值。一遍遍的学习渐渐有了领悟:算法研究是用来做大事的!之所以分析效率还求极限,是因为各种算法和数据结构都要研究其在海量输入数据或最坏最不利的情况下的效率表现。如果只是我们每天乏味工作中那三两个数的排序,几十个数值对的哈希,那其实怎样实现都无所谓了。所以个人觉得,学习算法能提高分析代码、洞察效率的能 阅读全文

《算法导论》第12章 二叉查找树 (3)基数树

2012-03-25 13:38 by htc开发, 245 阅读, 收藏,
摘要: 基数树与二叉查找树和Trie树很相似。它像BST一样是二叉的,向左表示0而不是BST的小于,而向右则表示1而不是大于。它像Trie一样共享相同的结点来保存字符串中相同的前缀,从而节省了空间,但它不像Trie那样每个结点有很多孩子(可以是26个,表示a到z),它用来处理只包含0和1的字符串。基数树和Trie都用来保存和排列字符串,那么现在来看看字典序,关于字典序有两条规则:1. 字符串长度相同时,从左向右逐个字符比较。如011 < 100。2. 字符串长度不同时,长度长的在字典序中值更大。如100 < 1011。因此,根结点 < 左子树结点 < 右子树结点。#includ 阅读全文

《算法导论》第12章 二叉查找树 (2)查找、插入与删除

2012-03-25 11:48 by htc开发, 202 阅读, 收藏,
摘要: 1. 查找利用二叉查找树左小右大的性质,可以很容易实现查找任意值和最大/小值。BSTNode * bst_search(BSTNode *node, int key) { while (node && key != node->key) { if (key < node->key) node = node->left; else node = node->right; } return node; } BSTNode * bst_minimum(BSTNode *... 阅读全文

《算法导论》第12章 二叉查找树 (1)遍历

2012-03-24 22:49 by htc开发, 130 阅读, 收藏,
摘要: 二叉查找树的效率在二叉查找树中执行的基本操作的时间与树的高度成正比。最坏情况,树的高度是N,像链表一样,而较好情况高度是lgN。因此,树的高度是关键。下一章将要学习的红黑树是对二叉查找树的改进,通过保持树的高度从而保证红黑树上的操作有很好的效率。各种遍历算法中序遍历算法:子树根的关键字在输出时介于左子树和右子树的关键字之间。即按排列顺序输出树中的所有关键字。相应的,前序遍历就是子树根的关键字在左右子树之前输出。在后面的基数树中,前序遍历(而非中序遍历)是二进制串的排序输出。用递归方式可以很轻松地实现二叉树的遍历。// 包含卫星数据的二叉树结点定义 struct _BSTNode { ... 阅读全文

《算法导论》第11章 散列表 (3)开放寻址

2012-03-12 21:34 by htc开发, 177 阅读, 收藏,
摘要: 前一节介绍是最简单的冲突解决方法-链接法。开放寻址与链接法不同,所有元素都放在散列表内。在这种方法中,散列表可能会被填满。开放寻址不需要指针,只需要计算出要存取的各个槽。由于不用存储指针而节省的空间可以提供更多的槽。有三种技术常用来计算开放寻址法中的探查序列:线性探查、二次探查和双重探查。下面的实现中,三种方法的差别只在计算探查序列的那一行代码。#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 20 typedef struct _Entry { char *key 阅读全文

《算法导论》第11章 散列表 (2)散列表

2012-03-10 23:05 by htc开发, 180 阅读, 收藏,
摘要: 用散列表来解决直接寻址表的那两个问题。但由此带来的散列值的碰撞问题。最简单的解决方法是链接法,以及下一节介绍的开放寻址法。链接法,即把散列到同一槽中的所有元素都放在一个链表中。链表是无序的,在查找一个元素时需要遍历链表。对于删除函数,假如参数是要删除的结点,那么如果链表是双向的,删除操作可以O(1)内完成。在下面的删除函数中,参数是关键字,这样更为方便。#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 20 // 链表结点的定义 typedef struct _Li 阅读全文

《算法导论》第11章 散列表 (1)直接寻址表

2012-03-07 22:54 by htc开发, 193 阅读, 收藏,
摘要: (一)直接寻址表关键字集合U = { 0, 1, ..., m - 1 },实际的关键字集合K。用一个数组T[0..m - 1],其中每个位置对应U中的一个关键字。直接寻址表的问题:(1)如果U很大,要保存|U|大小的一张表T有点不实际。(2)实际存储的关键字集合K相对U来说可能很小,因而分配给T的大部分空间都要浪费掉。(二)位向量位向量 (bit vector)是一种仅包含0和1的数组,所占空间比包含指针的数组少得多。一个32位的整型,每一位用0和1表示key是否存在,这样一个整数就可以表示32个key。key / 32表示key应保持在数组哪个下标的整数中,而key % 32则表示key应 阅读全文

《算法导论》第10章 基本数据结构 (2)链表

2012-03-05 22:21 by htc开发, 114 阅读, 收藏,
摘要: 《算法导论》里实现的是无序双向链表。源文件 list.h:链表的接口,定义链表和结点的类型,以及链表支持的操作。typedef int DATA_TYPE; // 注意将结构声明为新类型的语法 struct tagNode { DATA_TYPE data; struct tagNode *prev, *next; }; typedef struct tagNode Node; typedef struct { Node *head; } List; List * list_create(void); Node * node_create(DATA... 阅读全文

《算法导论》第9章 顺序统计学 (2)随机选择

2012-02-25 23:00 by htc开发, 202 阅读, 收藏,
摘要: randomized_select使用划分方法randomized_partition(),返回主元位置q(第k小元素)。要查找的是第 i 小元素,若恰好等于k,那么直接返回。如果 i < k,则继续在[p, q - 1]中搜索第 i 小元素。如果 i > k,则继续在[q + 1, r]中搜索第 i - k 小元素。int randomized_select(int A[], int p, int r, int i) { if (p == r) return A[p]; int q = randomized_partition(A, p, r); ... 阅读全文

《算法导论》第9章 顺序统计学 (1)最小值和最大值

2012-02-25 22:58 by htc开发, 340 阅读, 收藏,
摘要: 1. 查找最小值逻辑比较简单,用第一个元素来初始化最小值min,然后与数组中其余元素比较,找出最小值。时间复杂度是O(n),已经最优化了。int minimum(int A[], int len) { int min = A[0]; int i; for (i = 2; i < len; i++) if (A[i] < min) min = A[i]; return min; }2. 同时找最大和最小值如果依然采用前面的方法,用第一个元素初始化min和max。然后将min和ma... 阅读全文

《算法导论》第7章 快速排序 (四种变形)

2012-02-24 23:19 by htc开发, 260 阅读, 收藏,
摘要: 这一章的正文及思考题部分讲到了快速排序及其几种变形,包括:Hoare快排,普通快排,随机快排,三数取中快排。这些快排的区别主要是划分方法PARTITION算法的不同:如何选取主元,划分出的两部分范围是什么。根据划分出的范围不同,各变形的QUICKSORT有微小的差别。1. Hoare快排Hoare划分是最初的版本,与《算法导论》中的划分版本相比,它选取第一个元素A[p]为主元。划分后的两部分是:A[p..j]和A[j+1..r],主元可能放入某一个之中。int hoare_partition(int A[], int p, int r) { int x = A[p]; in... 阅读全文

《算法导论》第6章 堆排序 (4)Young氏矩阵

2012-02-22 22:08 by htc开发, 192 阅读, 收藏,
摘要: 1. 什么是Young氏矩阵?一个 m * n 的矩阵,其中每一行的数据都从左到右排序,每一列的数据都从上到下排序。其中用 ∞ 代表不存在的元素,一共可以存放 r ≤ mn 个有限的数。如下面程序实现中用来测试的Young氏矩阵:{ 1, 3, 5, 7, 8, 11 }{ 4, 6, 9, 14, 15, 19 }{ 10, 21, 23, 33, 56, 57 }{ 34, 37, 45, 55,∞, ∞}2. 相关问题及思路2.1实现EXTRACT-MIN的算法,使其运行时间为O(m + n)。参考最大堆的方法MAX-HEAPIFY,两者的相似性是:一个元素 i 的改变,破坏了堆... 阅读全文