├── 排序算法 ├── 排序.jpg ├── 冒泡排序初级版-{9,1,5,8,3,7,4,6,2}.jpg ├── 99-排序练习.cpp ├── 08-桶排序.cpp ├── 03-直接插入排序.cpp ├── README.md ├── 06-归并排序.cpp ├── 02-简单选择排序.cpp ├── 07-快速排序.cpp ├── 00-排序标准.cpp ├── 05-堆排序.cpp ├── 04-希尔排序.cpp └── 01-冒泡排序.cpp ├── BOOK ├── 网上推荐.txt ├── 《Cracking the Coding Interview》 │ ├── Q101.class │ ├── Q103.class │ ├── 3.5.cpp │ ├── 3.6.cpp │ ├── 3.7.cpp │ ├── 3.1.cpp │ ├── README.md │ ├── 2.7.cpp │ ├── 3.3.cpp │ ├── 3.2.cpp │ ├── 1.8旋转字符串.cpp │ ├── 3.4.cpp │ ├── Q101.java │ ├── 2.4.cpp │ ├── 2.6返回有环链表中环路开始结点.cpp │ ├── 2.1链表去重复结点.cpp │ ├── Q103.java │ ├── 1.2翻转字符串.cpp │ ├── 2.2倒数第k个结点.cpp │ ├── 1.6旋转图像.cpp │ ├── 2.3删除链表结点.cpp │ ├── 1.5字符串压缩.cpp │ ├── 1.1字符是否唯一.cpp │ ├── 1.7行列置0.cpp │ ├── 2.5链表求和.cpp │ ├── 1.3相同字符串.cpp │ └── 1.4替换空格.cpp ├── 《Alogrithms(Fourth Edition)》 │ └── 00SetupEnvironment │ │ ├── 使用说明.txt │ │ └── algs4.exe ├── 《Data Structures For Game Programmers》 │ └── Running Time Comparisons.png ├── 《剑指offer》 │ ├── 第2章-面试需要的基础知识.cpp │ └── 第1章-面试的流程.cpp ├── 《算法竞赛入门经典》 │ ├── Chapter04-函数和递归.cpp │ ├── Chapter05-基础题目选解.cpp │ ├── Chapter07-暴力求解法.cpp │ ├── Chapter02-循环结构程序设计.cpp │ ├── Chapter01-程序设计入门.cpp │ ├── Chapter03-数组和字符串.cpp │ └── Chapter06-栈和队列.cpp ├── 《Introduction to Algorithms —— A Creative Approach》 │ ├── 第3章 算法分析.md │ ├── 第2章 数学归纳法.md │ └── 第1章 引 论.md ├── README.md ├── 《挑战程序设计竞赛》 │ ├── 02-初出茅庐——初级篇.cpp │ └── 01-蓄势待发——准备篇.cpp ├── 《Alogrithms IN C(part1-4)》 │ └── 1.1.cpp └── assess.md ├── 常见算法在实际项目中的应用 ├── 网址.txt └── 工程常用算法.jpg ├── Computational Geometry └── [Code( )]ConvexHull │ ├── text1 │ ├── stdafx.h │ ├── text1.cpp │ ├── stdafx.cpp │ ├── targetver.h │ ├── ReadMe.txt │ ├── text1.vcxproj.filters │ └── text1.vcxproj │ ├── Template │ ├── stdafx.h │ ├── stdafx.cpp │ ├── targetver.h │ ├── Template.cpp │ ├── Template.vcxproj.filters │ └── Template.vcxproj │ ├── .vs │ └── [Code( )]ConvexHull │ │ └── v14 │ │ └── .suo │ ├── ConvexHullStudy │ ├── proj.win32 │ │ ├── main.cpp │ │ ├── stdafx.h │ │ ├── stdafx.cpp │ │ ├── targetver.h │ │ ├── ConvexHullStudy.vcxproj.filters │ │ └── ConvexHullStudy.vcxproj │ └── Classes │ │ ├── 01-windowsInit.h │ │ ├── 00-DataDefinition.h │ │ ├── 01-windowsInit.cpp │ │ └── 01-SimpleGraphics.h │ ├── 进程.md │ └── [Code( )]ConvexHull.sln ├── 阶段-状态转移 ├── 显式栈或循环代替递归调用 │ ├── README.md │ └── 02-如何利用循环代替递归以防止栈溢出.cpp ├── 动态规划 │ ├── 动态规划:从新手到专家.md │ ├── README.md │ └── 什么是动态规划.md └── 递归 │ └── 尾递归.md ├── 随机算法 └── 模拟退火算法.md ├── 寻路算法 ├── 01-Distance.cpp └── README.md ├── TitleLabel.md ├── 字符串比较算法 ├── KMP.md └── READ.md ├── 查找算法 ├── README.md ├── 02-折半查找.cpp ├── 00-查找标准.cpp ├── 01-顺序查找.cpp └── Randomize select.md ├── 位运算 ├── 位运算的应用.md └── 位运算实现加法.md ├── 哈希算法-散列算法 └── Hash表.md └── README.md /排序算法/排序.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/排序算法/排序.jpg -------------------------------------------------------------------------------- /BOOK/网上推荐.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/BOOK/网上推荐.txt -------------------------------------------------------------------------------- /常见算法在实际项目中的应用/网址.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/常见算法在实际项目中的应用/网址.txt -------------------------------------------------------------------------------- /常见算法在实际项目中的应用/工程常用算法.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/常见算法在实际项目中的应用/工程常用算法.jpg -------------------------------------------------------------------------------- /排序算法/冒泡排序初级版-{9,1,5,8,3,7,4,6,2}.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/排序算法/冒泡排序初级版-{9,1,5,8,3,7,4,6,2}.jpg -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/Q101.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/BOOK/《Cracking the Coding Interview》/Q101.class -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/Q103.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/BOOK/《Cracking the Coding Interview》/Q103.class -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/text1/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/text1/stdafx.h -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/text1/text1.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/text1/text1.cpp -------------------------------------------------------------------------------- /BOOK/《Alogrithms(Fourth Edition)》/00SetupEnvironment/使用说明.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/BOOK/《Alogrithms(Fourth Edition)》/00SetupEnvironment/使用说明.txt -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/Template/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/Template/stdafx.h -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/text1/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/text1/stdafx.cpp -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/text1/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/text1/targetver.h -------------------------------------------------------------------------------- /BOOK/《Alogrithms(Fourth Edition)》/00SetupEnvironment/algs4.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/BOOK/《Alogrithms(Fourth Edition)》/00SetupEnvironment/algs4.exe -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/Template/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/Template/stdafx.cpp -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/Template/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/Template/targetver.h -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/Template/Template.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/Template/Template.cpp -------------------------------------------------------------------------------- /阶段-状态转移/显式栈或循环代替递归调用/README.md: -------------------------------------------------------------------------------- 1 | # 显式栈或循环代替递归调用 2 | 3 | ## 01-基础知识 4 | 5 | 进程栈/线程栈 6 | 7 | ## 02-如何利用循环代替递归以防止栈溢出 8 | 9 | ## 03-替代过程的几个简单例子 10 | 11 | ## 实际代码 12 | 13 | ## 练习题目 14 | -------------------------------------------------------------------------------- /BOOK/《Data Structures For Game Programmers》/Running Time Comparisons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/BOOK/《Data Structures For Game Programmers》/Running Time Comparisons.png -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/.vs/[Code( )]ConvexHull/v14/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/.vs/[Code( )]ConvexHull/v14/.suo -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/main.cpp -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/stdafx.h -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/stdafx.cpp -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vo01github/Algorithm/HEAD/Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/targetver.h -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/3.5.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.3 栈与队列 4 | 5 | 6 | // __________________________ 3.5 __________________________ 7 | /* 8 | 实现一个MyQueue类,该类用两个栈实现一个队列。 9 | */ 10 | 11 | 12 | -------------------------------------------------------------------------------- /BOOK/《剑指offer》/第2章-面试需要的基础知识.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 第2章-面试需要的基础知识 4 | 5 | 6 | // __________________________ 第2章-面试需要的基础知识 __________________________ 7 | /* 8 | 9 | 10 | */ 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /随机算法/模拟退火算法.md: -------------------------------------------------------------------------------- 1 | 2 | # 模拟退火算法 3 | 4 | 讲解的不错的一篇文章: 5 | 6 | http://4691359.blog.51cto.com/4681359/963063 7 | 8 | 9 | ## 总结 10 | 11 | 模拟退火算法是一种随机算法,并不一定能找到全局的最优解,可以比较快的找到问题的近似最优解。 如果参数设置得当,模拟退火算法搜索效率比穷举法要高。 12 | 13 | 14 | -------------------------------------------------------------------------------- /寻路算法/01-Distance.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 01-寻路算法 4 | /* 5 | Distance, Simple Heuristic, Complex Heuristic, A Star分别是4种寻路算法。 6 | */ 7 | 8 | 9 | // __________________________1 距离优先 __________________________ 10 | /* 11 | 12 | */ 13 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/3.6.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.3 栈与队列 4 | 5 | 6 | // __________________________ 3.6 __________________________ 7 | /* 8 | 写程序将一个栈按升序排序(即最大元素位于栈顶)。 9 | 最多只能使用一个额外的的栈存放临时数据,但不得将元素复制到别的数据结构中(如数组) 10 | 该栈 支持的 操作有:push | pop | peek 【返回位于 Stack 顶部的对象但不将其移除。】 | isEmpty。 11 | */ 12 | 13 | 14 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/3.7.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.3 栈与队列 4 | 5 | 6 | // __________________________ 3.7 __________________________ 7 | /* 8 | 有家收容所只收容狗与猫,且严格遵守“先进先出”的原则。在收养该收容所的动物时,收养人只能收养所有动物中“最老的动物”, 9 | 或者同时收养此类动物中“最老”的。 10 | 11 | 实现的操作:enqueue, dequeueAny,dequeueDog,dequeueCat. 12 | */ 13 | 14 | 15 | -------------------------------------------------------------------------------- /BOOK/《算法竞赛入门经典》/Chapter04-函数和递归.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | //#include "Chapter04.h" 4 | 5 | 6 | // 第04章 函数和递归 7 | 8 | 9 | // __________________________4.1 数学函数__________________________ 10 | 11 | // 4.1.1 简单函数的编写 12 | 13 | 14 | 15 | 16 | // __________________________4.2 地址和指针__________________________ 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /TitleLabel.md: -------------------------------------------------------------------------------- 1 | # 书签 2 | 3 | 4 | 5 | ## 算法 6 | 7 | **动态规划** 8 | 9 | http://www.hawstein.com/posts/dp-novice-to-advanced.html 10 | http://www.hawstein.com/posts/dp-knapsack.html 11 | 12 | **程序员面试金典(第5版)-网络读书笔记** 13 | http://www.hawstein.com/categories.html 14 | 15 | ## 书籍 16 | 17 | **那些年,我们捧读过的C和C++经典** 18 | http://bbs.csdn.net/topics/390092013 19 | 20 | 21 | **其他书籍** 22 | 12 -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/3.1.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.3 栈与队列 4 | 5 | 6 | // __________________________ 3.1 __________________________ 7 | /* 8 | 描述如何只用一个数组来实现三个栈 9 | */ 10 | 11 | // 前面6个元素 是栈指针。 12 | // a[0]->top1 13 | // a[1]->bottom1 14 | // a[2]->top2 15 | // a[3]->bottom2 16 | // a[4]->top2 17 | // a[5]->bottom2 18 | 19 | 20 | // a[6] 开始就是 栈的内容了 21 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/README.md: -------------------------------------------------------------------------------- 1 | # 《Cracking the Coding Interview》读书笔记 2 | 3 | 4 | ## 说明 5 | 6 | 自己先做一遍题目,用【C++】代码,然后边看书对答案,边再敲一遍【JAVA】代码。 7 | 8 | --- 9 | 10 | 买到手的实体书是 【第5版】的。书上的代码大部分用【Java】,少部分用【C】 11 | 12 | --- 13 | 网上别人做的读书笔记 好像是看的【第4版】的。主要用的是【C++代码】 14 | 15 | http://www.hawstein.com/categories.html 16 | 17 | --- 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/2.7.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.2 链表 4 | 5 | 6 | // __________________________ 2.7 __________________________ 7 | /* 8 | 编写一个函数,检查链表是否为回文。 9 | */ 10 | 11 | // __________________________ 解决思路 __________________________ 12 | // 思路1:用栈,遍历链表前半部分,把数据都压入栈中,到中间时,开始出栈和链表后半部分进行对比。 13 | 14 | // 思路2:反转链表,然后与原链表进行对比,只要前半部分相同,即可。 15 | 16 | 17 | 18 | // __________________________ 实现细节 __________________________ 19 | // 20 | -------------------------------------------------------------------------------- /寻路算法/README.md: -------------------------------------------------------------------------------- 1 | # 寻路算法 2 | 3 | 参考文章来源: 4 | 5 | 6 | 距离优先 7 | http://cocos2d.9tech.cn/news/2013/1205/39103.html 8 | 9 | 离目的地的距离优先 10 | http://cocos2d.9tech.cn/news/2013/1209/39137.html 11 | 12 | A星寻路-算法 13 | http://www.360doc.com/content/10/0222/09/46383_16414504.shtml 14 | 15 | http://www.yxkfw.com/?p=10111 16 | 17 | A星寻路算法 : 18 | 有一点问题,那就是在2点之间是可以直线走的时候, 19 | 它会左拐一下,右拐一下,奔着目的地去。就是本来可以直线走的,它会变成曲线前进。 20 | 21 | 需要对这个算法进行改进,比如利用概率论里的知识 根据离散点 画一条相关很近的一条直线。 22 | 23 | ## 24 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/3.3.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.3 栈与队列 4 | 5 | 6 | // __________________________ 3.3 __________________________ 7 | /* 8 | 栈就像叠盘子,当盘子叠得太高时,就会倾斜倒下。因此,在真实的世界中,当一叠盘子 (栈)超过了一定的高度时, 9 | 我们就会另起一堆,再从头叠起。实现数据结构SetOfStacks 来模拟这种情况。 10 | SetOfStacks由几个栈组成,当前一栈超出容量时,需要创建一个新的栈 来存放数据。 11 | SetOfStacks.push()和SetOfStacks.pop()的行为应当和只有一个栈时 表现的一样。 12 | 13 | 进阶: 14 | 15 | 实现函数popAt(int index)在指定的子栈,执行pop操作。 16 | */ 17 | 18 | 19 | /* 20 | 思路: 21 | 22 | 23 | */ -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/进程.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## 要实现的函数 5 | 6 | 1. 随机生成一堆点的函数,压入 vector 7 | 2. 能从 vector 中取出点画在画布上面。 8 | 3. 根据 vector 算出凸包的点,并依据排序【顺时针/逆时针】压入 ConvexHull_01_vector 9 | 4. 从 一个vector【比如ConvexHull_01_vector】 取出点 用 OpenGL的画多边形的函数画凸包 10 | 11 | ## 点筛选 12 | 多点重叠 13 | 14 | 多点共线 15 | 16 | 凸包呈现一条线段,一个点,没有点 17 | 18 | 03-算凸包的点,先找最低的点,再在最低的点中寻找最左边的点。以这个点为起始点,遍历其他点与这个起始点连接起来的直线的斜率,寻找斜率最小【不能小于0,下次这个条件就会变化】的那个点,然后新找到的那个点为新的起始点,直到返回到最开始的那个起始点。 19 | 20 | 21 | -------------------------------------------------------------------------------- /字符串比较算法/KMP.md: -------------------------------------------------------------------------------- 1 | # KMP算法 2 | 3 | ## KMP算法 4 | 5 | 参考文章来源: 6 | 7 | 阮一峰的网络日志 【图画的不错,容量理解】 8 | > 9 | http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 10 | 11 | 图灵社区 【算法】KMP算法解析 【讲的比较好,从减少冗余上讲了下】 12 | > 13 | http://www.ituring.com.cn/article/59881 14 | 15 | KMP算法详解 16 | > 17 | http://www.matrix67.com/blog/archives/115 18 | 19 | 20 | http://www.cnblogs.com/yjiyjige/p/3263858.html 21 | 22 | http://blog.csdn.net/v_july_v/article/details/7041827 23 | 24 | ## AC自动机 25 | 26 | 27 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/3.2.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.3 栈与队列 4 | 5 | 6 | // __________________________ 3.2 __________________________ 7 | /* 8 | 实现一个栈,除了push和pop操作,还要实现min函数以返回栈中的最小值。 push,pop和min函数的时间复杂度都为O(1)。 9 | 10 | 11 | */ 12 | 13 | 14 | // __________________________ 解决思路 __________________________ 15 | // 思路1:用一条链表记录每个压入的节点,按从小到大排序。【好像不是很好】 16 | 17 | // 思路2: 18 | 19 | 20 | 21 | // __________________________ 实现细节 __________________________ 22 | // 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/1.8旋转字符串.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 数组与字符串 4 | 5 | 6 | // __________________________ 1.8 __________________________ 7 | /* 8 | 假定有一个方法 isSubstring,可检查一个单词是否为其他字符串的子串。 9 | 给定两个字符串 s1 和 s2,请编写代码检查 s2 是否为 s1 旋转而成,要求只能调用一次 isSubstring。 10 | (比如:waterbottle 是 erbottlewat 旋转后的字符串) 11 | 12 | */ 13 | 14 | 15 | // __________________________ 解决思路 __________________________ 16 | /* 17 | 18 | 19 | */ 20 | 21 | 22 | // __________________________ 实现细节 __________________________ 23 | // 24 | -------------------------------------------------------------------------------- /BOOK/《Introduction to Algorithms —— A Creative Approach》/第3章 算法分析.md: -------------------------------------------------------------------------------- 1 | 2 | ## 第3章 算法分析 3 | 4 | ### 3.1 引言 5 | 6 | 算法分析的目的是要预测算法的行为,特别是运行时间,这应与运行算法的特定计算机无关。 7 | 8 | 本章将描述一种用于预测算法近似运行时间以及进行算法比较的方法论。这种方法的主要特征是:忽略参数因子并关注输入大小无限增长后算法的行为。 9 | 10 | 幸运的是,大多数算法的运行时间表达式只含较小的数。因此,即使这种近似方法会带来误导,但它在实践过程中很有效。 11 | 12 | 我们为输入加上一个测度,称之为输入的规模,从而把算法的分析与输入的规模联系起来。 规模通常被定义为存储输入所需要的空间大小。 13 | 14 | 在所有规模相同的输入中进行选择,找到所需要的一个作为指标。最通常的选择是最坏的输入。本书使用的都是最坏情况分析技术。 15 | 16 | 总之:渐近分析和最坏情况分析,仅仅是对特定算法在特定输入作用下运行时间的近似。 17 | 18 | ### 3.2 符合O 19 | 20 | 符号O允许我们忽略常数项,因此在符号O中包括常数项是毫无意义的。 21 | 可以写出O(logn),而不必注明对数的底数,这是因为不同底数的对数间只有常数倍的差别。 22 | 可用O(1)来标明一个常数。 -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/Classes/01-windowsInit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | 7 | class OpenGLwindows 8 | { 9 | public: 10 | static int windowsInit(int argc,_TCHAR* argv[]); 11 | void static myDisplay(void); 12 | 13 | static OpenGLwindows* sharedSceneManager(); // 单例模式 14 | 15 | static void TimerFunction(int value); // 给定时器调用的。 16 | 17 | 18 | 19 | //static 20 | private: 21 | 22 | static OpenGLwindows* m_pSceneManager; 23 | 24 | static float* m_corners; 25 | static int frameCount; 26 | }; 27 | 28 | 29 | class OpenGLwindowsSon : public OpenGLwindows 30 | { 31 | 32 | }; 33 | 34 | 35 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/3.4.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.3 栈与队列 4 | 5 | 6 | // __________________________ 3.4 __________________________ 7 | /* 8 | 原文: 9 | 10 | In the classic problem of the Towers of Hanoi, you have 3 rods and N disks of different sizes which can slide onto any tower. The puzzle starts with disks sorted in ascending order of size from top to bottom (e.g., each disk sits on top of an even larger one). You have the following constraints: 11 | 12 | Only one disk can be moved at a time. 13 | A disk is slid off the top of one rod onto the next rod. 14 | A disk can only be placed on top of a larger disk. 15 | Write a program to move the disks from the first rod to the last using Stacks 16 | 17 | 译文: 18 | 19 | 编程解决汉诺塔问题,使用数据结构栈 20 | */ 21 | 22 | 23 | -------------------------------------------------------------------------------- /阶段-状态转移/动态规划/动态规划:从新手到专家.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 动态规划:从新手到专家 4 | 5 | 网址来源: 6 | > 7 | http://www.hawstein.com/posts/dp-novice-to-advanced.html 8 | 9 | 10 | 11 | **什么是动态规划,我们要如何描述它?** 12 | 13 | 动态规划算法通常基于一个递推公式及一个或多个初始状态。 当前子问题的解将由上一次子问题的解推出。 14 | 15 | 首先,我们要找到某个状态的最优解,然后在它的帮助下,找到下一个状态的最优解。 16 | 17 | **“状态”代表什么及如何找到它?** 18 | 19 | “状态"用来描述该问题的子问题的解。 20 | 21 | 这个规模变小后的问题和原来的问题是同质的,除了规模变小,其它的都是一样的, 本质上它还是同一个问题(规模变小后的问题其实是原问题的子问题)。 22 | 23 | 面对一个问题,我们首先要定义一个“状态”来代表它的子问题, 并且找到它的解。注意,大部分情况下,某个状态只与它前面出现的状态有关, 而独立于后面的状态。 24 | 25 | DP的关键:欲求问题的解,先要去求子问题的解 26 | 27 | **状态和状态转移方程** 28 | 29 | 动态规划的本质,是对问题**状态的定义**和**状态转移方程**的定义。 30 | 31 | 动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。 32 | 33 | 如何拆分问题,才是动态规划的核心。 34 | 35 | 而拆分问题,靠的就是状态的定义和状态转移方程的定义。 36 | 37 | **1. 什么是状态的定义?** -------------------------------------------------------------------------------- /BOOK/《剑指offer》/第1章-面试的流程.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 第1章-面试的流程 4 | 5 | 6 | // __________________________ 第1章-面试的流程 __________________________ 7 | /* 8 | 习惯01: 9 | 做完题后,习惯的写下 单元测试。相当于 证明了自己有这专业的软件开发经验。 10 | 11 | 但是如果是 先写单元测试用例,再写解决问题的函数。面试官绝对我刮目相看, 12 | 因为能做到测试在前,开发往后的程序实在是太稀缺了。 13 | 14 | 【那得多熟悉那个问题才能先写单元测试啊,如果是完全没做过的新题目, 15 | 要边写边想如何解决的,肯定就无法做到 先写单元测试用例了。】 16 | 17 | 面试小提示:__________________________ 18 | 平时写代码时,要注意是否考虑了: 19 | 01-边界条件 20 | 02-特殊输入(比如 NULL 指针,空字符串等)及错误处理。 21 | 22 | 面试小提示:__________________________ 23 | 我们平时有3种办法分析,解决复杂的问题: 24 | 01-画图能使抽象问题形象化 25 | 02-举例使抽象问题具体化 26 | 03-分解使复杂问题简单化 27 | 28 | */ 29 | 30 | 31 | /* 32 | 33 | 34 | 作者:何海涛。 35 | 任职经历: 36 | 01-在Autodesk公司做的是 面向土木的软件。 37 | 02-微软做维护 Winforms,2.5年 38 | 03-思科做网络。 39 | */ 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/Q101.java: -------------------------------------------------------------------------------- 1 | public class Q101 { 2 | public static void main(String[] args) { 3 | boolean isUnique = isUniqueChar2("Helo,Wrd"); 4 | if( isUnique ){ 5 | System.out.println("no equal"); 6 | }else{ 7 | System.out.println("equal"); 8 | } 9 | 10 | } 11 | 12 | public static boolean isUniqueChar2(String str){ 13 | if(str.length()>256)return false; 14 | 15 | boolean[] char_set = new boolean[256]; 16 | for(int i = 0;i 买的,图书馆借的,待买或者借的 10 | 11 | ## 图书馆借的 12 | 《算法:C语言实现(第1~4部分,基础知识,数据结构,排序及搜索)》【原书第3版】 13 | 14 | ## 待借OR买的 15 | 《算法:C语言实现(第5部分)图算法》 16 | 17 | 《数据结构与算法分析:C++ 描述(第3版)》 18 | 19 | *** 20 | 21 | ## 已买的 22 | 23 | 《编程之美》 24 | 25 | 《编程珠玑》 26 | 27 | 《Cracking the Coding Interview》 《程序员面试金典》 28 | 29 | 《算法竞赛入门经典》 30 | 31 | 《算法》【第4版】 32 | > 33 | 习题答案:https://github.com/BrambleXu/AlgorithmsSedgewick 34 | > 35 | 作者的mooc视屏:http://mooc.guokr.com/course/404/Algorithms--Part-I/ 36 | > 37 | 只读书略微枯燥,建议结合 coursera(视频教程)+ booksite(code/data/lecture/errata) 38 | > 39 | 编程环境建议使用booksite上面提供的Windows installer,一键安装,自动下载jdk,自带Dr.java轻量级IDE,让你恨不得马上卷起袖子开始撸代码 40 | http://algs4.cs.princeton.edu/windows/ 41 | > 42 | 43 | 44 | ## 其他人推荐的 45 | 46 | 47 | 48 | > 学习算法导论 时可以看的视屏: 49 | http://mooc.nthu.edu.tw/sharecourse/course/view/courseInfo/50 50 | 51 | 52 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/2.6返回有环链表中环路开始结点.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.2 链表 4 | 5 | 6 | // __________________________ 2.6 __________________________ 7 | /* 8 | 给定一个循环链表,实现一个算法返回这个环的开始结点。 9 | 10 | 定义: 11 | 12 | 循环链表:链表中一个结点的指针指向先前已经出现的结点,导致链表中出现环。 13 | 14 | 例子: 15 | 16 | 输入:A -> B -> C -> D -> E -> C [结点C在之前已经出现过] 17 | 18 | 输出:结点C 19 | */ 20 | 21 | // __________________________ 解决思路 __________________________ 22 | /* 23 | 方案1:改变结构体 24 | 在结点结构体里,加一个标志位在里面,初始为0 25 | 然后从头结点开始走起,每经过一个结点,标志位就置为1 26 | 直到走到一个 结点的 标志位 本来就是1的。 27 | 说明 找到了 这个环的 开始结点。 28 | 29 | */ 30 | 31 | /* 32 | 方案2:使用快慢指针【FastRunner/SlowRunner法】 33 | 用2个指针,A指针每次前进1个节点,B指针每次前进2个节点。 34 | 如果B指针首先走到尾部,说明没有环,如果AB指针走到一起,说明有环。 35 | 【只要有环,AB指针早晚要挨到一起。】 36 | 说明有环后,再次让AB开始走,统计A走的次数,再次撞到一起时,就可以推算出 环的长度。 37 | 然后就好推出 环在哪里了。 38 | 39 | // 书上给的 快慢指针 用了很巧妙的方法 来找到开始结点。 40 | */ 41 | 42 | 43 | // __________________________ 实现细节 __________________________ 44 | // 45 | -------------------------------------------------------------------------------- /查找算法/00-查找标准.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 00-查找标准 4 | 5 | 6 | // __________________________ __________________________ 参考代码 __________________________ __________________________ 7 | /// 自己练习后 与 参考代码 进行对比 。 8 | 9 | 10 | 11 | // __________________________1 顺序查找 __________________________ 12 | // 默写练习次数: 13 | 14 | 15 | // __________________________2 折半查找 __________________________ 16 | // 默写练习次数: 17 | 18 | 19 | // __________________________3 二叉树查找 __________________________ 20 | // 默写练习次数: 21 | 22 | 23 | 24 | // __________________________4 索引查找 __________________________ 25 | // 默写练习次数: 26 | 27 | 28 | 29 | // __________________________5 开地址哈希查找 __________________________ 30 | // 默写练习次数: 31 | 32 | 33 | // __________________________6 拉链法哈希查找 __________________________ 34 | 35 | 36 | // __________________________7 __________________________ 37 | // 默写练习次数: 38 | 39 | 40 | // __________________________8 __________________________ 41 | 42 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/2.1链表去重复结点.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.2 链表 4 | 5 | 6 | // __________________________ 2.1 __________________________ 7 | /* 8 | 编写代码,移除未排序链表中的重复结点。 9 | 进阶:如果不得使用临时缓冲区,,该怎么解决? 10 | */ 11 | 12 | // __________________________ 解决思路 __________________________ 13 | // 首先要看允不允许对 单链表 按照大小重新排序。 14 | 15 | /* 16 | 【方案1---不允许重新进行排序】: 17 | 使用“双指针”技巧: 18 | 01-A指针指向1结点,B指针从A指针下一个结点移动到尾结点。 19 | 02-遇到与A指针指向的1结点相同的结点就删除该节点。 20 | 03-然后A指针指向第2个节点,B指针又从第2个结点开始移动到尾。 21 | 重复上面的步骤。就可以删除所有重复元素。 22 | 23 | 时间复杂度分析: 24 | n + (n-1) + 。。。 +2 +1 = n(n-1)/2 = n^2 25 | 26 | 空间复杂度分析: 27 | 没有使用 临时缓冲区,0. 28 | */ 29 | 30 | /* 31 | 【方案2---允许重新进行排序】: 32 | 01-先对链表重新排序 33 | 02-从头遍历一次链表,遇到相邻的2个节点的值相同,就删除其中一个结点。 34 | 35 | 时间复杂度分析: 36 | n + 链表排序(?) = 37 | 空间复杂度分析: 38 | 单链表排序(?) 可能要用到临时缓冲区。得去看 单链表排序 的实现细节。 39 | */ 40 | 41 | 42 | /* 43 | 【方案3---借助临时缓冲区---散列表】: 44 | 01-先对链表遍历1次,每个节点值放入散列表中,若加的途中,发现有重复元素,则将该节点从链表中移除,然后继续迭代。 45 | */ 46 | 47 | // 48 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/Q103.java: -------------------------------------------------------------------------------- 1 | public class Q103 { 2 | public static void main(String[] args) { 3 | boolean isEqual = permutation("helowrd","wrdhloe"); 4 | if( isEqual ){ 5 | System.out.println("equal"); 6 | }else{ 7 | System.out.println("no equal"); 8 | } 9 | 10 | } 11 | 12 | public static boolean permutation(String s,String t){ 13 | if( s.length() != t.length() ){ 14 | return false; 15 | } 16 | 17 | int[] letters = new int[256]; 18 | 19 | char[] s_array = s.toCharArray(); 20 | for( char c : s_array ){ 21 | letters[c]++; 22 | } 23 | 24 | for( int i = 0; i< t.length(); i++ ){ 25 | int c = (int)t.charAt(i); 26 | if( --letters[c] < 0 ){ 27 | return false; 28 | } 29 | } 30 | 31 | return true; 32 | } 33 | } -------------------------------------------------------------------------------- /查找算法/01-顺序查找.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 01-顺序查找 4 | /* 5 | 从表的一端开始,顺序扫描表,依次将扫描到的结点关键字和给定值(假定为a)相比较,若当前结点关键字与a相等,则查找成功; 6 | 若扫描结束后,仍未找到关键字等于a的结点,则查找失败。 7 | 8 | 说白了就是,从头到尾,一个一个地比,找着相同的就成功,找不到就失败。很明显的缺点就是查找效率低。 9 | 10 | 适用于线性表的顺序存储结构和链式存储结构。 11 | */ 12 | 13 | 14 | // __________________________0 基础函数 __________________________ 15 | // 16 | 17 | 18 | // __________________________1 __________________________ 19 | /* 20 | 21 | */ 22 | 23 | 24 | int sequenceSearch(int goal,int array[],int length) 25 | { 26 | for(int i = 0; i1),若对任意小于n的自然数P成立能推出对n命题P也成立;那么对任意自然数,P都成立。 12 | 13 | 另一种变形: 14 | 如果对于带有参数n的命题P,当 n=1 和 n=2 时P都成立;并且如果对每一个n(n>2),若n-2时P成立能推出n时P也成立;那么对任意自然数,P都成立。 15 | 16 | ### 2.2 三个简单的例子 17 | 18 | **定理2.2** 19 | 20 | 前n个自然数之和为n(n+1)/2 21 | 22 | 假设要计算和T(n) = 8 + 13 + 18 + 23 + ... + (3+5n) 23 | 24 | **定理2.3** 25 | 26 | 级数 8 + 13 + 18 + 23 + ... + (3+5n) 之和是 2.5n^2 + 5.5n 27 | 28 | **定理2.4** 29 | 30 | 若n是自然数,且 1 + x > 0, 则 (1+x)^n >= 1+nx 31 | 32 | ### 2.3 平面内区域的计数 33 | 34 | [几何方面的问题,看不下去了,找个早上 脑洞大开的时候 来看吧] 35 | 36 | ### 2.4 简单的着色问题 37 | 38 | [同上] 39 | 40 | ### 2.5 复杂一些的加法题 41 | 42 | 要证明第i行的和的确是i^3,只须证明第(i+1)行 和 第i行的差是 (i+1)^3 - i^3 43 | 44 | 然后推理到:嵌套的归纳假设:第(i+1)行的最后一个数是 i^2+3i+1 45 | 46 | ### 2.6 一个简单的不等式 47 | 48 | 49 | ### 2.9 格雷码 50 | 51 | 用循环队列来排列这个名字,使得每个名字都可以通过只改变其前一个名字中的1位(bit)而生成。这样的命名方案称为 格雷码。 52 | -------------------------------------------------------------------------------- /位运算/位运算的应用.md: -------------------------------------------------------------------------------- 1 | 2 | ## 其他位运算的 应用 3 | 4 | 把最低位的1变成0: 5 | 6 | num = num & (num - 1); // 把最低位的1变成0了。 7 | 8 | 输出 num 的低位中的第一个 1 的位置: 9 | 10 | int firstOneBit = num & ~(num - 1); 11 | 12 | 13 | 14 | # 异或运算符 15 | 16 | 感受异或的神奇 17 | > 18 | http://www.lijinma.com/blog/2014/05/29/amazing-xor/ 19 | 20 | ## 恒等律 21 | 22 | x^0=x 23 | 24 | ## 归零律 25 | 26 | 对于任何数x,都有 27 | 28 | x^x=0 29 | 30 | ## 应用 31 | 32 | **(1)快速比较两个值** 33 | 34 | 判断两个int数字a,b是否相等, 35 | 36 | a - b == 0, 37 | 38 | 但是如果判断a ^ b == 0效率将会更高 [为啥不用 a == b ??] 39 | 40 | **(6)经典题目:不使用其他空间,交换两个值** 41 | 42 | 利用了 异或运算的 交换律 43 | 44 | a ^ b = c 45 | a ^ c = b 46 | 47 | a = a ^ b; 48 | b = a ^ b; 49 | a = a ^ b; 50 | 51 | --- 52 | 53 | ## ^ 异或运算的 交换律证明 54 | 55 | 只要把 1位的 交换证明即可 56 | 57 | 0^0^0 ,不用交换。 58 | 1^1^1 ,不用交换。 59 | 60 | 只含1个1的情况: 61 | 62 | 1^0^0 = 1 63 | 0^1^0 = 1 64 | 0^0^1 = 1 65 | 66 | 含2个1的情况: 67 | 68 | 0^1^1 = 0 69 | 1^0^1 = 0 70 | 1^1^0 = 0 71 | 72 | 8种情况全部,举例完毕,异或运算的 交换律 用 穷举法 证明完毕。 73 | 74 | ## ^ 异或运算 的 交换律 证明 肯定 还有其他方法,暂时没想出来 75 | 76 | 77 | -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/Classes/00-DataDefinition.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __DL_DATA_DEFINITION_H__ 4 | #define __DL_DATA_DEFINITION_H__ 5 | 6 | //========================================================================= 7 | // 8 | // 数据结构定义 9 | // 10 | //========================================================================= 11 | typedef struct Point_2D_World 12 | { 13 | float x; 14 | float y; 15 | }Point2D; 16 | 17 | 18 | typedef struct Line_2D_World 19 | { 20 | Point2D startPos; 21 | Point2D endPos; 22 | }Line2D; 23 | 24 | typedef struct Rect_2D_World 25 | { 26 | Point2D LeftDownPoint; 27 | Point2D RightUpPoint; 28 | }Rect2D; 29 | 30 | //========================================================================= 31 | // 32 | // 数据定义 33 | // 34 | //========================================================================= 35 | const Point2D ScreenSize = {800.0f, 800.0f}; // 屏幕大小 36 | 37 | const float scaleScreen = 0.2f; // 决定要裁剪的区域有多大 38 | 39 | Rect2D ClippingSize; // 要裁剪的区域 40 | 41 | #endif // __DL_DATA_DEFINITION_H__ -------------------------------------------------------------------------------- /BOOK/《Introduction to Algorithms —— A Creative Approach》/第1章 引 论.md: -------------------------------------------------------------------------------- 1 | 2 | # 算法引论 ———— 一种创造性方法 3 | 4 | 5 | 【设计】《算法引论》->【证明】《算法导论》->【实现】《算法》 6 | 7 | **译者序** 8 | 9 | 本书的特色有二: 10 | 11 | 1.是强调算法设计的创造性过程,而不是拘泥于某个具体算法的详细讨论。 12 | 2.将算法设计类比于归纳证明。 13 | 14 | 正是因为强调的是算法设计的创造性过程而非算法本身,才使得本书可以永保活力。 15 | 16 | 17 | **前言** 18 | 19 | 讲授算法设计的**思维过程**与讲授问题**求解细节**是同样重要的。 20 | 21 | 一般来说,有关算法的书籍有两种不同的组织结构,一种是按照领域进行分类的,如分成图算法,几何算法等;另一种是按照算法设计技术进行分类的。本书是按照前者来的。 22 | 23 | 24 | ## 第1章 引 论 25 | **算法的设计和实现类似于房屋的设计和建筑。** 26 | 27 | 【建筑图纸设计】->【结构力学分析】->【经济成本考虑】->【建筑工人施工】 28 | 29 | 算法的设计也如此,先考虑基本的思想和方法,然后做出规划。我们必须证明规划是可行的并且代价是可接受的。最后的工作是在具体的计算机上实现。 30 | 31 | 每步都不同但又相关,每步都不能不顾及其他而在真空中完成。当然,我们很少能按照线性次序走过历程。在构造算法的每个阶段都可能出现困难。由此可能需要修改设计,还可能需要再次进行可行性证明,调整代价以及改变算法实现。 32 | > 33 | 算法可以将过程分为4步:设计,正确性证明,分析,实现。 34 | 35 | 【解决问题】->【数学证明】->【复杂度分析】->【结合具体的硬件,软件,语言】 36 | 37 | 本书主要注重算法的第一个阶段,即算法的设计。 38 | 39 | 本书也不会忽略算法的其他方面,书中在对大多数算法描述之后紧接着是算法的正确性证明,算法的复杂度分析以及对算法实现的讨论。 40 | 41 | 我们认为学习如何创造事物的最好方式就是尝试去创造它,因此本书首先引导读者给出其自己的算法。 42 | 43 | 其次,我们将遵循有助于 培养 读者创新思想的方法学,该方法学 基于数学归纳法。 44 | 45 | 这种方法的一个缺点在于它不是一种通用的方法,并不是所有程序都可以或者应该按照归纳法的思想进行设计。 46 | 47 | **算法描述计法** 48 | 在大多数情况下,使用类 Pascal 语言。 49 | 50 | -------------------------------------------------------------------------------- /BOOK/《挑战程序设计竞赛》/02-初出茅庐——初级篇.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 第02章 初出茅庐——初级篇 4 | /* 5 | 6 | */ 7 | 8 | // __________________________2.1 最基础的“穷竭搜索”__________________________ 9 | /* 10 | 穷竭搜索是将所有的可能性罗列出来,在其中寻找答案的方法。这里我们主要介绍【深度优先搜索】和【广度优先搜索】这两种方法。 11 | */ 12 | 13 | // 2.1.1 递归函数 14 | /* 15 | 在一个函数中再次调用该函数自身的行为叫做递归,这样的函数被称作递归函数。 16 | 17 | 在编写一个递归函数时,函数的停止条件是必须存在的。 18 | */ 19 | 20 | // 【阶乘】的递推式 n! = n*(n-1)! 21 | int fact(int n){ 22 | if( n == 0 )return 1; 23 | return n* fact(n-1); 24 | } 25 | 26 | // 计算 【斐波那契】 数列的函数 27 | int fib(int n){ 28 | if(n<=1)return n; 29 | return fib(n-1) + fib(n-2); 30 | } 31 | 32 | /* 33 | 实际使用这个函数时,即便是求 fib(40) 这样的 n 较小的结果,也要花费相当长的时间。 34 | 这是因为这个函数在递归时,会像下图一样按照指数级别扩展开来。 35 | fib(10) = fib(9) + fib(8) 36 | fib(9) = fib(8) + fib(7) 37 | 38 | 如果计算一次后,用数列将结果存储起来,便可优化之后的计算。fib(10)被调用时同样的n被计算了很多次, 39 | 因此可以获得很大的优化空间。这种方法是出于记忆化搜索或者动态规划的想法。 40 | */ 41 | 42 | int memo[MAX_N + 1]; 43 | 44 | int fib(int n){ 45 | if(n<=1)return n; 46 | if(memo[n] != 0)return memo[n]; 47 | return memo[n] = fib(n-1) + fib(n-2); 48 | } 49 | 50 | // 2.1.2 栈 51 | /* 52 | 栈(Stack)是支持push和pop两种操作的数据结构。 53 | (LIFO:Last In First Out, 既后进先出 ) 54 | 55 | C++的标准库中,stack::pop()完成的仅仅是移除最顶端的数据。 56 | 如果需要访问最顶端的数据,需要使用stack::top()函数【这个操作通常也被称为peek】 57 | */ 58 | 59 | // 2.1.3 队列 60 | /* 61 | 62 | */ -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/Template/Template.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 26 | 27 | 源文件 28 | 29 | 30 | 源文件 31 | 32 | 33 | -------------------------------------------------------------------------------- /排序算法/08-桶排序.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 08-桶/箱排序 4 | 5 | /* 6 | 介绍: 7 | 8 | */ 9 | 10 | 11 | /* 12 | 历史背景: 13 | 14 | */ 15 | 16 | 17 | /* 18 | 思路来源: 19 | 20 | 桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将阵列分到有限数量的桶子里。 21 | 每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。 22 | 当要被排序的阵列内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。 23 | 24 | 例如要对大小为[1..1000]范围内的n个整数A[1..n]排序,可以把桶设为大小为10的范围,具体而言,设集合B[1]存储[1..10]的整数, 25 | 集合B[2]存储(10..20]的整数,……集合B[i]存储((i-1)*10, i*10]的整数,i = 1,2,..100。总共有100个桶。然后对A[1..n]从头到尾扫描一遍, 26 | 把每个A[i]放入对应的桶B[j]中。 然后再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择,乃至快排,一般来说任何排序法都可以。 27 | 最后依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,这样就得到所有数字排好序的一个序列了。 28 | 29 | 假设有n个数字,有m个桶,如果数字是平均分布的,则每个桶里面平均有n/m个数字。如果对每个桶中的数字采用快速排序, 30 | 那么整个算法的复杂度是O(n+m*n/m*log(n/m))=O(n+nlogn-nlogm) 31 | 32 | 从上式看出,当m接近n的时候,桶排序复杂度接近O(n) 33 | 34 | 当然,以上复杂度的计算是基于输入的n个数字是平均分布这个假设的。这个假设是很强的,实际应用中效果并没有这么好。 35 | 如果所有的数字都落在同一个桶中,那就退化成一般的排序了。 36 | */ 37 | 38 | /* 39 | 前面说的几大排序算法 ,大部分时间复杂度都是O(n2),也有部分排序算法时间复杂度是O(nlogn)。而桶式排序却能实现O(n)的时间复杂度。 40 | 41 | 但桶排序的缺点是: 42 | 43 | 1)首先是空间复杂度比较高,需要的额外开销大。排序有两个数组的空间开销,一个存放待排序数组,一个就是所谓的桶, 44 | 比如待排序值是从0到m-1,那就需要m个桶,这个桶数组就要至少m个空间。 45 | 46 | 2)其次待排序的元素都要在一定的范围内等等。 47 | 48 | 桶式排序是一种分配排序。分配排序的特点是不需要进行关键码的比较,但前提是要知道待排序列的一些具体情况。 49 | 50 | 51 | */ -------------------------------------------------------------------------------- /BOOK/《Alogrithms IN C(part1-4)》/1.1.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 第一部分 基础知识 4 | 5 | 6 | // __________________________ 1.1 算法 __________________________ 7 | /* 8 | 当我们利用计算机帮助我们求解问题时,一般都会面对许多不同的方法。对于小规模的问题,利用哪一个方法是不重要的,只要能够有个方法正确解决问题就行。 9 | 然后对于大规模问题(或需要求解大量小规模的问题的应用),我们的动机就是设计时间和空间都尽可能高效的方法。 10 | 11 | 我们学习算法设计的主要原因是这个学科可以使我们节省大量的时间和空间,甚至可能使原本不可能解决的问题得以解决。 12 | 13 | 14 | 选择某个特定任务的 最好算法 可能是一个复杂的过程,也许涉及复杂的数学分析。 15 | 计算机科学中研究这些问题的分支称为算法分析( analysis of algorithm )。 16 | */ 17 | 18 | // __________________________ 1.2 典型问题————连通性 __________________________ 19 | 20 | /* 21 | 如何排列才能快速断定网络中的任何给定两点是连通的? 22 | 23 | 还有一个例子出现在某种程序设计环境中,连通性 可用来断言两个变量名是否等价。 24 | 25 | 扩展: 26 | 比如我们的连通问题的说明只要求我们的程序知道任意给定对 p-q 是否连通的,并不 27 | 能够表明连接那个对的任何方式。 28 | 添加这样一个说明的要求会使问题更加困难,会涉及其他的算法。 29 | 30 | 查找:包含给定数据项的集合 31 | 用它们的并集 替换包含 两个给定数据项 的集合。 32 | 33 | 根据查找和合并抽象操作容易求解连通性问题。 34 | 在从输入读取一个新的对 p-q 后,对于对中的每个数执行查找操作。如果对的成员在同一集合中,那么考虑下一对。 35 | 36 | 如果它们不在同一集合中,则执行合并工作,并输出这个对。 37 | 38 | 集合表示连通分量,即那些给定分量中的任何两个对象 是连通对象的集合。 39 | 40 | 这种方法把 开发连通问题算法解的过程变为定义表示集合的数据结构以及开发高效利用这个数据结构的查找和合并算法。 41 | 42 | */ 43 | 44 | 45 | // __________________________ 1.3 合并 - 查找算法 __________________________ 46 | 47 | /* 48 | 01 开发求解给定问题高效算法的过程的第一步是实现解这个问题的一个简单算法。 49 | 50 | 我们总是关注算法的效率,但我们在开发解决的问题的第一个程序时更关注的是确保程序的正确性。 51 | */ 52 | 53 | 54 | // 程序1.1 连通问题的快速查找算法 55 | // P5 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/2.2倒数第k个结点.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.2 链表 4 | 5 | 6 | // __________________________ 2.2 __________________________ 7 | /* 8 | 实现一个算法,找出单向链表中倒数第k个结点。 9 | */ 10 | 11 | // __________________________ 解决思路 __________________________ 12 | /* 13 | 思路1: 使用“双指针”技巧: 14 | 01-A指针先走,B指针不动, 15 | 02-等A指针往前走了k步后,B指针指向第1个结点。 16 | 03-B指针和A指针 一起每次移动一步,直到B指针走到尾节点,A指针指向的结点 就是倒数第K个结点。 17 | 18 | 19 | // 书上居然 先讲了3个比较笨的方法,才讲双指针法。 20 | */ 21 | 22 | 23 | 24 | 25 | // __________________________ 实现细节 __________________________ 26 | 27 | //思路1: 使用“双指针”技巧 __________________________ 28 | typedef struct NODE 29 | { 30 | int value; 31 | struct NODE * next; 32 | }Node,*LinkHead; // LinkHead 是头指针,*LinkHead 是头结点 33 | 34 | // 链表 头结点+头指针 初始化 35 | LinkHead = (Node *)malloc(sizeof(Node)); 36 | assert(LinkHead != NULL,"内存分配失败"); 37 | LinkHead->next = NULL; 38 | LinkHead->value = NULL; 39 | 40 | 41 | 42 | Node* findBackNode(int k,Node* LinkHead) 43 | { 44 | Node* node = LinkHead->next; 45 | if(!node)return NULL; 46 | 47 | Node* num_k_node = NULL; 48 | int len = 0; // 统计经历过 多少个 节点。 49 | while( node) 50 | { 51 | node = node->next; 52 | ++len; 53 | if (len == k) 54 | { 55 | num_k_node = node; 56 | }else if( len > k){ 57 | num_k_node = num_k_node->next; 58 | } 59 | } 60 | 61 | return num_k_node; 62 | } 63 | // 64 | // 65 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/1.6旋转图像.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 数组与字符串 4 | 5 | 6 | // __________________________ 1.6 __________________________ 7 | /* 8 | 给定一副由N*N矩阵表示的图像,其中每个像素的大小为4字节, 9 | 编写一个方法,将图像旋转90度。不占用额外内存空间能否做到? 10 | */ 11 | 12 | 13 | // __________________________ 解决思路 __________________________ 14 | /* 15 | 每个像素的大小为4字节。那就是32位真彩色。 16 | 实际的用途就是图像处理软件中的,旋转图像90度后,重新保存图像吧。 17 | 18 | e.g. 参考 bmp 图像存放格式:像素点按照从左往右,再从上往下的方式存放在数组中。 19 | 20 | 思路1: 从外层到里层,一层层循环。 21 | 每层进行:上边移动到右边,右边移动到下边,下边移动到左边,左边移动到上边。 22 | 23 | 时间复杂度为O(N^2),这已是最优的做法,因为任何算法都需要访问所有N^2个元素。 24 | 25 | */ 26 | 27 | 28 | // __________________________ 实现细节 __________________________ 29 | // 30 | struct rgb{ 31 | float r; 32 | float g; 33 | float b; 34 | }; 35 | 36 | void rotate(rgb matrix[n][n],int n) 37 | { 38 | for( int layer = 0; layer < n/2; ++layer ) 39 | { 40 | int first = layer; 41 | int last = n - 1 - layer; 42 | for(int i = first; i < last + 1; ++i) // 原书代码里居然少了 +1,写成 i < last 了 43 | { 44 | int offset = i - first; 45 | 46 | // 存储上边 47 | rgb top = matrix[first][i]; 48 | 49 | // 左到上 50 | matrix[first][i] = matrix[last-offset][first]; 51 | 52 | // 下到左 53 | matrix[last-offset][first] = matrix[last][last-offset]; 54 | 55 | // 右到下 56 | matrix[last][last-offset] = matrix[i][last]; 57 | 58 | // 从上到右 59 | matrix[i][last] = top; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /BOOK/《算法竞赛入门经典》/Chapter02-循环结构程序设计.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | //#include "Chapter02.h" 4 | 5 | 6 | // 第02章 循环结构程序设计 7 | 8 | 9 | // __________________________2.1 for循环__________________________ 10 | 11 | 12 | 13 | 14 | // __________________________2.2 循环结构程序设计__________________________ 15 | 16 | /* 17 | 例题:2-2 3n+1问题 18 | 猜想:对于任意大于1的自然数n,若n为奇数,则将n变为3n+1,否则变为n的一半,经过若干次这样的变换,一定会使n变为1 19 | 例如 3->10->5->16->8->4->2->1 20 | 输入n,输出变换的次数。 21 | 样例输入:3 22 | 样例输出:7 23 | */ 24 | // 注意可能会遇到的问题,3n+1 的时候,乘法可能会溢出。 25 | 26 | /* 27 | 例题:2-3 阶乘之和 28 | 输入n,计算 S = 1! + 2! + 3! + ... + n! 的末6位。 29 | */ 30 | 31 | /* 计时函数 32 | 函数clock()的使用。该函数返回程序目前为止运行的时间。这样,在程序结束之前调用它,便可获得整个程序的运行时间。 33 | 这个时间除以常数 CLOCKS_PER_SEC 之后得到的值以“秒”为单位。 34 | */ 35 | #include 36 | printf("Time used = %.2lf\n",(double)clock()/CLOCKS_PER_SEC); 37 | 38 | 39 | // __________________________2.3 文件操作__________________________ 40 | 41 | /* 42 | 例题:2-4 数据统计 43 | 输入一些整数,求出它们的最小值,最大值和平均值(保留3位小数)。输入保证这些数都是不超过1000的整数。 44 | */ 45 | 46 | /* 47 | scanf 函数有返回值,它返回的是成功输入的变量的个数。 48 | */ 49 | 50 | /* 51 | 一个好的方法是用文件————把输入数据保存在文件中,输出数据也保存在文件中。 52 | 这样,只要事先把输入数据保存在文件中,就不必每次重新输入了;数据输出在文件中也避免了 53 | “输出太多,一卷屏前面的就看不见了”这样的尴尬————运行结束后,慢慢浏览输出文件即可。 54 | 如果有标准答案文件,还可以进行文件比较,而无须用眼睛检查输出是否正确。 55 | 事实上,几乎所有算法竞赛的输入数据和标准答案都是保存在文件中的。 56 | 57 | P40/241 58 | */ 59 | 60 | 61 | // __________________________2.4 小结与习题__________________________ 62 | 63 | -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/text1/text1.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 29 | 30 | 源文件 31 | 32 | 33 | 源文件 34 | 35 | 36 | -------------------------------------------------------------------------------- /字符串比较算法/READ.md: -------------------------------------------------------------------------------- 1 | 2 | ##目录 3 | 4 | ### 字符串匹配算法 5 | 6 | * **第1章 ** 7 | - [1.0 本章导读](01.00.md) 8 | 9 | * **常用的字符串比较算法** 10 | - [1.1 蛮力匹配算法](Compare%20Strings.cpp) 11 | - [1.2 KMP算法](KMP.md) 12 | - [1.3 BM算法](BM.md) 13 | - [1.4 Horspool算法](Valid%20Palindrome.cpp) 14 | - [1.5 Sunday算法](Count%20and%20Say.cpp) 15 | - [1.6 fastsearch算法](Two%20Strings%20Are%20Anagrams.cpp) 16 | - [1.7 KR算法](Rotate%20String.cpp) 17 | - [1.8 《柔性字符串匹配》-](Reverse%20Words%20in%20a%20String.cpp) 18 | 19 | 20 | 21 | ## 待分拆到各个 md 22 | 23 | 关于字符串模式匹配算法的一点理解【有很多参考文章的地址】 24 | >http://www.cnblogs.com/a180285/archive/2012/02/22/String_Matching.html 25 | 26 | 27 | 字符串匹配BM(Boyer-Moore)算法学习心得【作者参考了很多文章,并给出很多文章的地址来】 28 | >http://www.cnblogs.com/a180285/archive/2011/12/15/BM_algorithm.html 29 | 30 | 串的模式匹配算法---Horspool 31 | >http://www.cnblogs.com/cobbliu/archive/2012/05/29/2524244.html 32 | 33 | --- 34 | 35 | O(n)回文子串算法 36 | >http://acm.uestc.edu.cn/bbs/read.php?tid=3258 37 | >http://www.cnblogs.com/a180285/archive/2012/03/03/Manacher_Algorithm.html 38 | 39 | 求回文子串 O(n) manacher算法 40 | >http://wenku.baidu.com/view/3031d2d3360cba1aa811da42.html 41 | 42 | --- 43 | 44 | - 字符串匹配算法(一)——朴素算法 45 | - 字符串匹配算法(二)——Rabin-Karp算法 46 | - 字符串匹配算法(三)——KMP算法 47 | - 字符串匹配算法(四)——Boyer-Moore算法 48 | - 字符串匹配算法(五)——Horspool算法 49 | - Python中的字符串匹配算法分析 50 | >http://www.xefan.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/index.html -------------------------------------------------------------------------------- /排序算法/03-直接插入排序.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 03-直接插入排序 4 | 5 | // __________________________1 直接插入排序 __________________________ 6 | /* 7 | 感觉就像是摸扑克牌,摸一张,就和手里的牌进行依次比较,选择合适的插入位置。 8 | 然后再摸下一张,再和手里的所有牌进行对比,选择合适的插入位置。 9 | 10 | 直观理解,就是玩扑克牌时,摸牌的那个阶段。 11 | */ 12 | 13 | 14 | /* 15 | 方法:从数组的二个元素开始,设当前位置索引是i,通过找到当前元素array[i]在前面i个元素中的正确位置j后,将j位置及j到i-1位置中间的元素后移一位, 16 | 然后将当前元素插在j位置,这样前i个元素变成有序的了,然后i++,再通过比较和插入使得前i+1个元素有序,直到最后。 17 | 18 | 直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。 19 | */ 20 | 21 | void InsertSort(int arr[], int length) 22 | { 23 | int i,j; 24 | int ntemp; 25 | for(i=1; i0 && arr[j-1]>ntemp; j--) 29 | arr[j] = arr[j-1]; 30 | arr[j] = ntemp; 31 | } 32 | } 33 | /* 34 | 时间复杂度分析: 35 | 36 | 我们来分析一下这个算法,从空间上来看,它只需要一个记录的辅助空间。因此关键是看它的时间复杂度。 37 | 38 | 当最好的情况,也就是要排序的表本身就是有序的,比如纸牌拿到后就是{2,3,4,5,6},那么我们比较次数共比较n次,因此没有移动的记录,时间复杂度为O(n)。 39 | 当最坏的情况,即待排序表是逆序的情况比如{6,5,4,3,2},此时需要比较 (n+2)*(n-1)/2 次,而记录的移动次数也达到最大值 (n+4)*(n-1)/2 次。 40 | 如果排序记录是随机的,那么根据概率相同的原则,平均比较和移动次约为(n^2)/4 次。因此,我们得出直接插入排序法的时间复杂度为O(n^2)。 41 | 42 | 从这里也看出,同样的O(n2)时间复杂度,直接插入排序法比冒泡和简单选择排序的性能要好一些。 43 | */ 44 | 45 | 46 | 47 | /* 48 | unsort array: 49 | 2 4 6 3 5 9 11 50 | sort array: 51 | 2 3 4 5 6 9 11 52 | */ 53 | 54 | /* 55 | 两种排序算法的时间复杂度都是O(N^2)。但是冒泡排序增加了哨兵指示,用来判断是否当前排序已经处于有序状态,从而避免没必要的比较。 56 | 在一般的小数据排序时,这两种都是很好的选择,因为实现起来简单。 57 | */ -------------------------------------------------------------------------------- /哈希算法-散列算法/Hash表.md: -------------------------------------------------------------------------------- 1 | 2 | ## 哈希算法 3 | 4 | 打造最快的Hash表-暴雪的Hash算法 5 | > 6 | http://blog.sina.com.cn/s/blog_5b29caf701015tpb.html 7 | > 8 | http://blog.csdn.net/wuliming_sc/article/details/1365962 9 | 10 | --- 11 | 12 | 什么是哈希表和哈希算法? 13 | > 14 | http://www.guokr.com/question/562532/ 15 | 16 | 17 | 哈希算法并不是一个特定的算法而是一类算法的统称。哈希算法也叫散列算法,一般来说满足这样的关系:f(data)=key,输入任意长度的data数据,经过哈希算法处理后输出一个定长的数据key。同时这个过程是不可逆的,无法由key逆推出data。 18 | 19 | 20 | 如果是一个data数据集,经过哈希算法处理后得到key的数据集,然后将keys与原始数据进行一一映射就得到了一个哈希表。一般来说哈希表M符合M[key]=data这种形式。 21 | 哈希表的好处是当原始数据较大时,我们可以用哈希算法处理得到定长的哈希值key,那么这个key相对原始数据要小得多。我们就可以用这个较小的数据集来做索引,达到快速查找的目的。 22 | 23 | 24 | 稍微想一下就可以发现,既然输入数据不定长,而输出的哈希值却是固定长度的,这意味着哈希值是一个有限集合,而输入数据则可以是无穷多个。那么建立一对一关系明显是不现实的。所以"碰撞"(不同的输入数据对应了相同的哈希值)是必然会发生的,所以一个成熟的哈希算法会有较好的抗冲突性。同时在实现哈希表的结构时也要考虑到哈希冲突的问题。 25 | 26 | 27 | 密码上常用的MD5,SHA都是哈希算法,因为key的长度(相对大家的密码来说)较大所以碰撞空间较大,有比较好的抗碰撞性,所以常常用作密码校验。 28 | 29 | --- 30 | 31 | 什么是哈希算法? 32 | > 33 | http://www.zhihu.com/question/20820286 34 | 35 | 真没有一个内行点的回答吗? 36 | 这个HASH算法不是大学里数据结构课里那个HASH表的算法。这里的HASH算法是密码学的基础,比较常用的有MD5和SHA,最重要的两条性质,就是不可逆和无冲突。 37 | 所谓不可逆,就是当你知道x的HASH值,无法求出x; 38 | 所谓无冲突,就是当你知道x,无法求出一个y, 使x与y的HASH值相同。 39 | 40 | 这两条性质在数学上都是不成立的。因为一个函数必然可逆,且由于HASH函数的值域有限,理论上会有无穷多个不同的原始值,它们的hash值都相同。MD5和SHA做到的,是求逆和求冲突在计算上不可能,也就是正向计算很容易,而反向计算即使穷尽人类所有的计算资源都做不到。 41 | 42 | 我觉得密码学的几个算法(HASH、对称加密、公私钥)是计算机科学领域最伟大的发明之一,它授予了弱小的个人在强权面前信息的安全(而且是绝对的安全)。举个例子,只要你一直使用https与国外站点通讯,并注意对方的公钥没有被篡改,G**W可以断开你的连接,但它永远不可能知道你们的传输内容是什么。 43 | 44 | 顺便说一下,王小云教授曾经成功制造出MD5的碰撞,即md5(a) = md5(b)。这样的碰撞只能随机生成,并不能根据一个已知的a求出b(即并没有破坏MD5的无冲突特性)。但这已经让他声名大噪了。 45 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/2.3删除链表结点.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.2 链表 4 | 5 | 6 | // __________________________ 2.3 __________________________ 7 | /* 8 | 实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。 9 | 示例 10 | 输入:单向链表 a->b->c->d->e 中的结点c 11 | 结果:不返回任何数据,但该链表变为:a->b->d->e 12 | */ 13 | 14 | 15 | // __________________________ 解决思路 __________________________ 16 | /* 17 | “节点覆盖”技巧: 18 | 01-访问该节点, 19 | 02-然后 复制 next 节点的值到该节点。 20 | 03-然后 指向 下一个节点,重复 02 步骤 直到走到尾结点时。 21 | 04-删除尾节点。 22 | */ 23 | 24 | // __________________________ 实现细节 __________________________ 25 | typedef struct NODE 26 | { 27 | int value; 28 | struct NODE * next; 29 | }Node,*LinkHead; // LinkHead 是头指针,*LinkHead 是头结点 30 | 31 | 32 | void freeNode( Node* deleNode) // free node 33 | { 34 | 35 | Node* thisNode = deleNode; 36 | Node* nextNode = thisNode->next; 37 | 38 | thisNode->value = nextNode->value; // 如果 nextNode == NULL ,那么此题无解,要提出来。 39 | thisNode->next = nextNode->next; 40 | 41 | // 释放结点内存。 42 | free(nextNode); 43 | } 44 | 45 | 46 | 47 | // __________________________ 下面是错误的代码,因为我们不知道 LinkHead __________________________ 48 | void deleteMidNode( Node* midNode, Node* LinkHead) 49 | { 50 | Node* thisNode = LinkHead->next ; 51 | Node* foreNode = thisNode; 52 | while( thisNode && thisNode != midNode) 53 | { 54 | foreNode = thisNode; 55 | thisNode = thisNode->next; 56 | } 57 | 58 | if( thisNode == midNode ) // find it 59 | { 60 | foreNode->next = thisNode->next; 61 | freeNode(thisNode); 62 | } 63 | } 64 | 65 | // 66 | // 67 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/1.5字符串压缩.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 数组与字符串 4 | 5 | 6 | // __________________________ 1.5 __________________________ 7 | /* 8 | 利用字符串重复出现的次数,编写一个方法,实现基本的字符串压缩功能。 9 | 比如,字符串“aabcccaaa” 会变为 “a2b1c3a3”.若压缩后的字符串没有变短, 10 | 则返回原先的字符串。 11 | */ 12 | 13 | // __________________________ 解决思路 __________________________ 14 | /* 15 | 思路01:创建一个新的 string ,像队列一样对它进行操作,不停的从尾部 压入就好了。 16 | 改进:要加 一个 能预先判断 是否要压缩的函数 就好了。 17 | */ 18 | 19 | 20 | // __________________________ 实现细节 __________________________ 21 | /* 22 | 23 | */ 24 | // 第1种方案:使用 string __________________________ 25 | // 总感觉下面的代码写的 好丑啊! 26 | string compress( string & str ) 27 | { 28 | if(str.length() < 2)return str; 29 | 30 | string q_insert = ""; 31 | int repeatNum = 1; 32 | 33 | for( int i = 1; i256 )return false; // 大于 Anscii 编码 最大数量了 34 | 35 | bool strHas[256]; 36 | memset(strHas, 0, sizeof(strHas)); 37 | 38 | for (int i = 0; i < strOrc.length() ; ++i) 39 | { 40 | int j = (int)strOrc[i]; 41 | if( strHas[j] )return false; 42 | strHas[j] = true; 43 | } 44 | 45 | return true; 46 | } 47 | 48 | /* 49 | 时间复杂度:O(n) 50 | 空间复杂度:O(1) 51 | */ 52 | 53 | 54 | 55 | // 第3种方案:用排序__________________________ 56 | bool confirm3( std::string & strOrc ) 57 | { 58 | // 容错处理 59 | if( strOrc.length() < 2 )return true; 60 | 61 | if( strOrc.length()>256 )return false; // 大于 Anscii 编码 最大数量了 62 | 63 | std::string str = strOrc; // 如果允许对原字符串排序,就不用这个了。 64 | sort(str,str + str.length()); 65 | 66 | for(int i = 1; i 2 | 3 | 4 | 5 | {7d8a52b3-f360-4a62-9126-08babb89b956} 6 | 7 | 8 | {81f42fb3-ba4c-4fa1-bb08-d4c42a55ddd4} 9 | 10 | 11 | {bbe07604-cc6c-4882-ac2c-c3faf5a87b02} 12 | 13 | 14 | 15 | 16 | proj.win32 17 | 18 | 19 | proj.win32 20 | 21 | 22 | Classes\OpenGLShow 23 | 24 | 25 | 26 | 27 | proj.win32 28 | 29 | 30 | proj.win32 31 | 32 | 33 | Classes\OpenGLShow 34 | 35 | 36 | Classes\OpenGLShow 37 | 38 | 39 | Classes\OpenGLShow 40 | 41 | 42 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/1.7行列置0.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 数组与字符串 4 | 5 | 6 | // __________________________ 1.7 __________________________ 7 | /* 8 | 编写一个算法,若 M*N 矩阵中 某个元素为0,则将其所在的行与列清零。 9 | */ 10 | 11 | 12 | // __________________________ 解决思路 __________________________ 13 | /* 14 | 思路1: 15 | 遍历第1遍矩阵,记录下哪些元素为0.等所有值为0 的元素 全部统计完成后。用栈记录? 16 | 然后再对0所在的行与列 清零 17 | 18 | 思路2: 19 | 遍历第1遍矩阵,把同行同列的都置为 true. 20 | 遍历第2遍矩阵,把值为 true 的全部设置为 0。 21 | */ 22 | 23 | 24 | // __________________________ 实现细节 __________________________ 25 | // 26 | 27 | // 思路1实现,不好。__________________________ 28 | stack stk; 29 | 30 | void recordZero(int matrix[n][n]) 31 | { 32 | 33 | 34 | for( int j = 0;j 17 | http://sxyckjzh.blog.163.com/blog/static/32629815201361010642951/ 18 | 19 | **滚动数组-用斐波那契数列的实现来举的例子** 20 | > 21 | http://blog.csdn.net/niushuai666/article/details/6677982 22 | 23 | ## 背包问题 24 | 背包问题-九讲 25 | > 26 | http://love-oriented.com/pack/ 27 | 28 | 背包之01背包、完全背包、多重背包详解 — TankyWoo 29 | > 30 | http://www.wutianqi.com/?p=539 31 | 32 | 0-1背包问题入门小结 动态规划(DP)经典题目 POJ324 POJ1276 33 | > 34 | http://www.csdn123.com/html/mycsdn20140110/d7/d78ea55beab3d5222d71264eaccfd2da.html 35 | 36 | 背包小结(普及贴) 37 | > 38 | http://acm.uestc.edu.cn/bbs/read.php?tid=4423 39 | 40 | 背包问题学习笔记(1)-基本的背包问题介绍 41 | > 42 | http://crescentmoon.info/2013/08/05/bag-problem-1/ 43 | 44 | ## 教学汇总 45 | 46 | 47 | **通过金矿模型介绍动态规划** 48 | > 49 | http://www.cnblogs.com/sdjl/articles/1274312.html 50 | 51 | 金矿模型讲解 非常清楚。 52 | 53 | **动态规划** 54 | > 55 | http://www.cnblogs.com/kkgreen/archive/2011/06/26/2090702.html 56 | 57 | 58 | **动态规划之背包问题** 59 | > 60 | http://www.hawstein.com/posts/dp-knapsack.html 61 | 62 | 代码详实,可以自己敲完后,进行对比。 63 | 64 | 65 | **动态规划:从新手到专家** 66 | > 67 | http://www.hawstein.com/posts/dp-novice-to-advanced.html 68 | 69 | “如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?” 70 | 71 | 72 | **acm之家-讲解动态规划** 73 | 74 | 看不太懂在讲些什么 75 | > 76 | http://www.acmerblog.com/article-np-3376.html -------------------------------------------------------------------------------- /查找算法/Randomize select.md: -------------------------------------------------------------------------------- 1 | # Randomize select algorithm 随机选择算法 2 | 3 | 从一个序列里面选择第k大的数在没有学习算法导论之前我想最通用的想法是给这个数组排序,然后按照排序结果返回第k大的数值。如果使用排序方法来做的话时间复杂度肯定至少为O(nlgn)。 4 | 5 | 问题是从序列中选择第k大的数完全没有必要来排序,可以采用分治法的思想解决这个问题。Randomize select 算法的期望时间复杂度可以达到O(n),这正是这个算法的迷人之处。具体的算法分析可以在《算法导论》这本书里查看。 6 | 7 | 8 | 一个n个元素组成的集合中,第K个顺序统计量(Order Statistic)指的是该集合中第K小的元素,我们要讨论的是如何在线性时间(linear time)里找出一个数组的第K个顺序统计量。 9 | 10 | 找最大值或最小值很简单,只需要遍历一次数组并记录下最大值或最小值就可以了。我们在这里要解决的问题是一般性的选择问题。 11 | 12 | 一种原始的解决方案是,用堆排序或归并排序将输入数据进行排序,然后返回第k个元素。这样在Θ(nlgn)时间内一定可以解决。但是我们希望有更好的方案,最好是线性时间。 13 | 14 | 15 | ## 期望线性时间的解决方案 16 | 17 | 为了在线性时间内解决这个选择问题,我们使用一个随机的分治算法,即RANDOMIZED-SELECT算法。此算法是使用随机化的快速排序中的随机划分子程序,对输入数组进行随机划分操作,然后判断第k小元素在划分后的哪个区域,对所在区域进行递归划分,最后找到第k小元素。 18 | 19 | ## O(n?) 20 | 21 | 这里的RANDOMIZED-PARTITION()是随机版的划分操作(快速排序的分析与优化),可见本算法是一个随机算法,它的期望时间是Θ(n)(假设元素的值是不同的)。 22 | 23 | 1、Lucky-Case:最好的情况是在正中划分,划分的右边和右边的元素数量相等,但是1/10和9/10的划分也几乎一样好。可以这么说,任何常数比例的划分都和1/2:1/2的划分一样好。这里以1/10和9/10的划分为例,算法运行时间递归式为T(n) <= T(9n/10) + Θ(n),根据主定理得到T(n) <= Θ(n)。 24 | 25 | 2、Unlucky-Case:虽然主元的选取是随机的,但是如果你运气足够差,每次都得到0:n-1的划分,这就是最坏的情况。此时递归式为T(n) = T(n-1) + Θ(n),则时间复杂度为T(n) = Θ(n^2)。 26 | 27 | 3、Expected-Time:期望运行时间为Θ(n),即线性时间。这里就不证明了,证明需要用到指示器随机变量。 28 | 29 | 30 | ## 最坏情况线性时间的解决方案 31 | 32 | 虽然最坏情况Θ(n^2)出现的概率非常非常小,但是不代表它不会出现。这里就介绍一个非同一般的算法,以保证在最坏情况下也能达到线性时间。 33 | 34 | 这个SELECT算法的基本思想就是要保证对数组的划分是一个好的划分,它通过自己的方法选取主元(pivot),然后将pivot作为参数传递给快速排序的确定性划分操作PARTITION。 35 | 36 | 基本步骤: 37 | 38 | 将输入数组的n个元素划分为n/5(上取整)组,每组5个元素,且至多只有一个组有剩下的n%5个元素组成。 39 | 40 | 寻找每个组织中中位数。首先对每组中的元素(至多为5个)进行插入排序,然后从排序后的序列中选择出中位数。 41 | 42 | 对第2步中找出的n/5(上取整)个中位数,递归调用SELECT以找出其中位数x。(如果是偶数取下中位数) 43 | 44 | 调用PARTITION过程,按照中位数x对输入数组进行划分。确定中位数x的位置k。 45 | 46 | 如果i=k,则返回x。否则,如果i < k,则在地区间递归调用SELECT以找出第i小的元素,若干i > k,则在高区找第(i-k)个最小元素。 47 | 48 | // 参考文章 : http://songlee24.github.io/2014/06/22/Kth-Order-Statistic/ -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/2.5链表求和.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 8.2 链表 4 | 5 | 6 | // __________________________ 2.5 __________________________ 7 | /* 8 | 给定两个用链表表示的整数,每个结点包含一个数位。这些数位是反向存放的,也就是个位排在链表首部。 9 | 编写函数对这两个整数求和,并用链表形式返回结果。 10 | 11 | 示例 12 | 输入:(7->1->6) + (5->9->2), 即是 617 + 295 = 912 13 | 输出:2->1->9 14 | 15 | 进阶:假设这些数位是正向存放的。 16 | 输入:1->2->3->4 ;5->6->7 17 | 输出:既 1234 + 567 。此时 5 应该与 2 对应起来相加。 18 | 19 | 20 | */ 21 | 22 | // __________________________ 解决思路 __________________________ 23 | /* 24 | 太简单了,不想写思路了。 25 | 就是练手的题目。 26 | */ 27 | 28 | // __________________________ 实现细节 __________________________ 29 | // 30 | typedef struct NODE 31 | { 32 | int value; 33 | struct NODE * next; 34 | }Node,*LinkHead; // LinkHead 是头指针,*LinkHead 是头结点 35 | 36 | 37 | /* 38 | 下面的代码有点问题: 39 | 1-假设了1>2链表,不应该做这种假设。 40 | 待改进,可以对谁长谁短都不影响。 41 | */ 42 | Node* add( Node* numNode1, Node* numNode2 ) 43 | { 44 | // 容错处理 45 | if( numNode1 == NULL && numNode2 == NULL) 46 | 47 | int sum; 48 | int oldSum = 0; 49 | int num1 = 0, num2 = 0; 50 | 51 | 52 | for(; numNode1!= NULL || numNode2!= NULL; 53 | numNode1 = numNode1->next,numNode2 = numNode2->next,num1 = 0;num2 = 0; ) 54 | { 55 | if(numNode1!= NULL) num1 = numNode1->value; 56 | if(numNode2!= NULL) num2 = numNode2->value; 57 | 58 | sum = num1 + num2 + oldSum; 59 | oldSum = sum/10; 60 | numNode1->value = sum%10; // 这里是 假设 numNode1 的结点个数 > numNode2 61 | } 62 | 63 | if( oldSum > 0 ) 64 | { 65 | Node* lastNode = new Node; 66 | numNode1->next = lastNode; 67 | lastNode->next = NULL; 68 | lastNode->value = oldSum; 69 | } 70 | 71 | return numNode1; 72 | 73 | } 74 | 75 | 76 | // 进阶题目: __________________________ 77 | 78 | // 思路1,先求出 两个链表 的长度,然后对短链表进行补位,全都补0 79 | // 思路2,逆转2个链表。 80 | -------------------------------------------------------------------------------- /位运算/位运算实现加法.md: -------------------------------------------------------------------------------- 1 | ## 位运算实现加法 2 | 3 | 用位运算实现加法也就是计算机用二进制进行运算,32位的CPU只能表示32位内的数,这里先用1位数的加法来进行,在不考虑进位的基础上,如下 4 | 5 | 1 + 1 = 0 6 | 1 + 0 = 1 7 | 0 + 1 = 1 8 | 0 + 0 = 0 9 | 10 | 很明显这几个表达式可以用位运算的“^”来代替,如下 11 | 12 | 1 ^ 1 = 0 13 | 1 ^ 0 = 1 14 | 0 ^ 1 = 1 15 | 0 ^ 0 = 0 16 | 这样我们就完成了简单的一位数加法,那么要进行二位的加法,这个方法可行不可行呢?肯定是不行的,矛盾就在于,如何去 17 | 获取进位?要获取进位我们可以如下思考: 18 | 19 | 0 & 0 = 0 20 | 1 & 0 = 0 21 | 0 & 1 = 0 22 | 1 & 1 = 1 23 | 24 | //换个角度看就是这样 25 | 26 | 0 & 0 = 不进位 27 | 1 & 0 = 不进位 28 | 0 & 1 = 不进位 29 | 1 & 1 = 进位 30 | 31 | 正好,在位运算中,我们用“<<”表示向左移动一位,也就是“进位”。那么我们就可以得到如下的表达式 32 | 33 | //进位可以用如下表示: 34 | (x&y)<<1 35 | 到这里,我们基本上拥有了这样两个表达式 36 | 37 | x^y //执行加法 38 | (x&y)<<1 //进位操作 39 | 40 | a + b = a ^ b + ((a & b) << 1) 41 | 42 | --- 43 | 44 | ## 代码实现 45 | 46 | 下面的代码还支持 传 负数进去... 好神奇。 47 | 48 | #include 49 | #include 50 | 51 | #define __DEBUG__ 1 52 | 53 | int MyAdd(int a, int b) 54 | { 55 | /* 定理1: 56 | * if (0 == (a & b)) { 57 | * a + b == a ^ b; 58 | * } 59 | * 换句话说就是,如果两数相加时没有进位,则加法运算可由异或运算代替。 60 | * 两数相与生存下来的位就是相加会向左产生进位的位,所以 61 | * (a & b)就可以判断两数相加会不会产生进位,而且(a & b) << 1 62 | * 就是所有--进位位--的进位值之和。 63 | * */ 64 | /* 定理2:a + b = a ^ b + ((a & b) << 1) 65 | * 就是把加法拆成--每位相加的和--与--进位值--两部分相加 66 | * 等式右边也是加法,又可以拆,这就形成了一个递归的过程 67 | * 要使递归终止,需使用定理1,也就是不再有进位,定理2等式右边 68 | * 的加号就可以换成异或符号。 69 | * */ 70 | 71 | /* 这里的a, b可视为某两个数A和B的--每位相加的和--和--进位值 */ 72 | int cf = a & b; 73 | int sum = a ^ b; 74 | 75 | while (cf) { 76 | a = sum; 77 | b = cf << 1; 78 | cf = a & b; 79 | sum = a ^ b; 80 | } 81 | 82 | #if __DEBUG__ 83 | assert(sum == a + b); 84 | #endif 85 | 86 | return sum; 87 | } -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/1.3相同字符串.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 4 | 5 | 6 | // __________________________ 1.3 __________________________ 7 | /* 8 | 给定两个字符串,请编写程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。 9 | */ 10 | // __________________________ 解决思路 __________________________ 11 | 12 | /*首先我们要问清楚细节 13 | 01: 是否区分大小写。 14 | 02:是否要考虑空白字符 15 | 03:核定字符集的大小,是不是 ASCII 编码 16 | */ 17 | 18 | // 解法01:排序字符串:对两个字符串用O(nlogn)的时间去进行排序,然后再用O(n)的时间比较它们是否相等即可。 19 | 20 | // 解法02:检查两个字符串的各个字符数是否相同。 21 | /* 22 | 由于组成变位词的字符是一模一样的, 因此我们可以先统计每个字符串中各个字符出现的次数, 然后看这两个字符串中各字符出现次数是否一样。 23 | 如果是,则它们是一对变位词。 这需要开一个辅助数组来保存各字符的出现次数。我们可以开一个大小是256的整数数组, 遍历第一个字符串时, 24 | 将相应字符出现的次数加1;遍历第二个字符串时, 将相应字符出现的次数减1。最后如果数组中256个数都为0,说明两个字符串是一对变位词。 25 | (第1个字符串中出现的字符都被第2个字符串出现的字符抵消了), 如果数组中有一个不为0,说明它们不是一对变位词。 26 | */ 27 | 28 | 29 | // __________________________ 实现细节 __________________________ 30 | 31 | // 解法02 的实现。 32 | // 只用了1个辅助数组。而且只用了2次 for 循环。O(2n) 33 | 34 | bool compareEqual(char *a,char *b) 35 | { 36 | if(strlen(a) != strlen(b))return false; 37 | 38 | int com[256]; 39 | memset(com, 0, sizeof(com)); 40 | 41 | for( int i = 0; i 4 | http://t1174779123.iteye.com/blog/2124357 5 | 6 | 递归专题 7 | 此文章所在专题列表如下: 8 | 漫谈递归:递归的思想 9 | 漫谈递归:递归需要满足的两个条件 10 | 漫谈递归:字符串回文现象的递归判断 11 | 漫谈递归:二分查找算法的递归实现 12 | 漫谈递归:递归的效率问题 13 | 漫谈递归:递归与循环 14 | 漫谈递归:循环与迭代是一回事吗? 15 | 递归计算过程与迭代计算过程 16 | 漫谈递归:从斐波那契开始了解尾递归 17 | 漫谈递归:尾递归与CPS 18 | 漫谈递归:补充一些Continuation的知识 19 | 漫谈递归:PHP里的尾递归及其优化 20 | 漫谈递归:从汇编看尾递归的优化 21 | > 22 | http://any9.com/939.html 23 | 24 | 25 | 如何理解 SICP-1.2.2 树形递归空间需求线性增长? 26 | > 27 | http://www.douban.com/group/topic/67330078/ 28 | 29 | 尾调用优化 30 | > 31 | http://www.ruanyifeng.com/blog/2015/04/tail-call.html 32 | 33 | 34 | 35 | ## 斐波那契数列的递归求法 36 | 37 | 38 | 根据定义很容易写出的斐波那契数列: 39 | 40 | int Fibonacci(int n) { 41 | if (n<=2) { 42 | return 1; 43 | } 44 | else { 45 | return Fibonacci(n-1) + Fibonacci(n-2); 46 | } 47 | } 48 | 49 | 但是面试官接着问这样不断的压栈很浪费内存空间,问是否可以优化?可能很多人给不出其他解法了,或者说感觉其他解法如果也是递归的话,也需要压栈,一样会占用内存空间。其实,面试官想考察的是尾递归。 50 | 51 | ## 尾递归 52 | > 53 | 在计算机科学里,**尾调用**是指一个函数里的最后一个动作是一个函数调用的情形即这个调用的返回值直接被当前函数返回的情形。这种情形下称该调用位置为尾位置。若这个函数在尾位置调用本身(或是一个尾调用本身的其他函数等等),则称这种情况为尾递归,是递归的一种特殊情形。尾调用不一定是递归调用,但是尾递归特别有用,也比较容易实现。 54 | > 55 | 尾调用的重要性在于它可以不在调用栈上面添加一个新的堆栈帧——而是更新它,如同迭代一般。 56 | 57 | 尾递归因而具有两个特征: 58 | 59 | - 调用自身函数(Self-called); 60 | - 计算仅占用常量栈空间(Stack Space)。 61 | 62 | 而形式上只要是最后一个return语句返回的是一个完整函数,它就是**尾递归**。 63 | 64 | 由于当前函数帧上包含局部变量等等大部分的东西都不需要了,当前的函数帧经过适当的更动以后可以直接当作被尾调用的函数的帧使用,然后程序即可以跳到到被尾调用的函数。产生这种函数帧更动代码与 “jump”(而不是一般常规函数调用的代码)的过程称作**尾调用消除**(Tail Call Elimination)或**尾调用优化**(Tail Call Optimization, TCO)。尾调用优化让位于尾位置的函数调用跟 goto 语句性能一样高,也因此使得高效的结构编程成为现实。 65 | 66 | 一般来说,尾调用消除是可选的。然而,在函数编程语言中,语言标准通常会要求虚拟机实现尾调用消除,这让程序员可以用递归替换循环而不丧失性能。 67 | 68 | 简单理解,就是处于函数尾部的递归调用本身的情形下,前面的变量状态都不需要再保存了,可以释放,从而节省很大的内存空间。在前面的代码中,明显在调用递归调用Fibonacci(n-1)的时候,还有Fibonacci(n-2)没有执行,需要保存前面的状态,因此开销较大的。 69 | 70 | 于是,我们可以改写这个斐波那契数列: 71 | 72 | // 求斐波那契数列 第N项 73 | Fibonacci(n, 1, 1); 74 | 75 | int Fibonacci(int n, int a, int b) { 76 | if (n<=2) { 77 | return b; 78 | } 79 | else { 80 | return Fibonacci(n-1, b, a+b); 81 | } 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Algorithm 2 | Algorithm 3 | 4 | ## 学习到的算法 5 | 6 | **排序算法** 7 | 8 | * 冒泡排序 9 | * 简单选择排序 10 | * 直接插入排序 11 | * 希尔排序 12 | * 堆排序 13 | * 归并排序 14 | * 快速排序 15 | * 基数排序 16 | 17 | 18 | **寻路算法** 19 | 20 | 21 | 22 | **显式栈或循环代替递归调用** 23 | 24 | 25 | **五大常用算法** 26 | 27 | http://www.cnblogs.com/steven_oyj/category/246990.html 28 | 29 | 五大常用算法之一:分治算法 30 | 五大常用算法之二:动态规划算法 31 | 五大常用算法之三:贪心算法 32 | 五大常用算法之四:回溯法 33 | 五大常用算法之五:分支限界法 34 | 35 | ## 网上学习的网址 + 竞赛题目网址 36 | 37 | 有很多游戏开发相关的算法介绍: 38 | > 39 | http://www.gamedev.net 40 | http://theory.stanford.edu/~amitp/GameProgramming 41 | http://www.gamasutra.com 42 | http://www.sudoku.com 43 | 44 | 俄罗斯方块游戏的算法网站: 45 | > 46 | http://gforge.inria.fr/projects/mdptetrishttp://colinfahey.com/tetris/tetris.html 47 | 48 | leetcode,最近很火的算法网站: 49 | > 50 | http://www.leetcode.com 51 | 52 | Topcoder,也很经典,每周都有竞赛,有奖金的: 53 | > 54 | http://community.topcoder.com/tc 55 | 56 | 晋中教育网的“信息学竞赛辅导”: 57 | > 58 | http://www.jzsyz.jzedu.cn/xxjs/suanfa/index.html 59 | 60 | 很多大学也有自己的竞赛题库,比如: 61 | > 62 | 北大:http://poj.org/ 63 | 杭电:http://acm.hdu.edu.cn/ 64 | 华中科技大学:http://acm.hust.edu.cn/vjudge/toIndex.action 65 | 66 | --- 67 | **51Nod** 68 | > 69 | http://www.51nod.com/onlineJudge/problemList.html 70 | 71 | 必须推荐一下我们的网站。 51Nod 问题 72 | 73 | 看我的签名,题目都是经过挑选的高质量问题。 74 | 75 | 支持10+种语言(包括C#,Python,Ruby,Nodejs等流行的语言),判题一次跑所有test,出现了错误可以查看输入输出数据,从而反思是哪里没有想对,还有QQ群让大家讨论(251587768),群里高手众多。 76 | 77 | --- 78 | **可视化的数据结构学习网站** 79 | > 80 | http://visualgo.net/ 81 | 82 | --- 83 | **在编程和算法领域,有哪些经典问题?** 84 | > 85 | http://www.zhihu.com/question/19927564 86 | 有各种算法题目在里面 87 | 88 | 89 | --- 90 | **《程序员编程艺术:面试和算法心得》** 91 | >7月份就要出版了,出版后去买一本吧,现在先去仔细阅读 github 版吧。 92 | > 93 | https://github.com/julycoding/The-Art-Of-Programming-By-July 94 | 95 | --- 96 | **Tanky Woo 算法专题 ---- 个人博客的 算法总结** 97 | > 98 | http://www.wutianqi.com/sfzt.html 99 | 100 | --- 101 | **ACMer的BLOG** 102 | > 103 | http://blog.csdn.net/jzqt_t/article/details/28126533 104 | 105 | 编程错误集锦 106 | 107 | *** 108 | 109 | **C++ 实现的很多算法:把这里的学会就够用了** 110 | > 111 | http://www.geeksforgeeks.org/fundamentals-of-algorithms/ -------------------------------------------------------------------------------- /排序算法/README.md: -------------------------------------------------------------------------------- 1 | # 排序算法 2 | 3 | 1、按照待排得记录是否全部被放置到内存中,可以分为内排序与外排序; 4 | 5 | 2、按照是否需要辅助空间,可以分为原址排序与非原址排序 6 | 7 | 3、按照两个相等的数排序后相对位置是否发生变化,可以分为稳定排序和不稳定排序 8 | 9 | 4、按照是否有比较的排序,可以分为基于比较的排序与非基于比较的排序,其中基于比较的排序又可以分为: 10 | 11 | (1)交换排序:冒泡排序、快速排序 12 | 13 | (2)插入排序:直接插入排序、希尔排序 14 | 15 | (3)选择排序:简单选择排序、堆排序 16 | 17 | (4)归并排序 18 | 19 | 基于比较的时间复杂度最好是O(nlogn) 20 | 21 | 非基于比较的排序:桶排序、计数排序、基数排序 22 | 23 | 当然,这些都还只是基础,相关变形太多了, 24 | topK问题, 25 | 链表进行排序, 26 | 多路归并排序等。 27 | 28 | 掌握其中几种确实够应付大多数问题了,但要每种算法都实现过一遍,相关问题上理解就会深刻很多 29 | 30 | 31 | ## 交换排序 32 | * 冒泡排序【 + 鸡尾酒排序 + 奇偶排序】 33 | * 快速排序 34 | 35 | ## 插入排序 36 | * 直接插入排序 37 | * 希尔排序 38 | 39 | ## 选择排序 40 | * 简单选择排序 41 | * 堆排序 42 | 43 | ## 归并排序 44 | * 归并排序 45 | 46 | ## 分配排序 47 | * 桶排序 48 | 49 | 50 | 51 | * 计数排序 52 | * 基数排序 53 | 54 | * bitmap排序 55 | 56 | 57 | 58 | 59 | 60 | 61 | ## **动画演示** 62 | 63 | 冒泡排序 64 | http://student.zjzk.cn/course_ware/data_structure/web/flashhtml/maopaopaixu.htm 65 | 66 | 桶排序 67 | http://www.cs.usfca.edu/~galles/visualization/BucketSort.html 68 | 69 | 70 | ## 排序算法-参考文章: 71 | http://www.cnblogs.com/wb-DarkHorse/category/430640.html 72 | 这篇介绍的排序算法有:1) 冒泡排序,2) 插入排序。 73 | 74 | 九大排序算法再总结 75 | http://blog.csdn.net/xiazdong/article/details/8462393 76 | 77 | 78 | 八大排序算法 79 | http://blog.csdn.net/hguisu/article/details/7776068 80 | 81 | 82 | 大话数据结构-排序的那篇章节 83 | http://www.cnblogs.com/cj723/category/282222.html 84 | 85 | 86 | 漫谈经典排序算法:一、从简单选择排序到堆排序的深度解析 87 | http://blog.csdn.net/touch_2011/article/details/6767673 88 | 89 | 90 | 快速排序算法 91 | http://blog.csdn.net/v_july_v/article/details/6116297 92 | 93 | 94 | 怎样让快速排序更快? 95 | http://blog.sina.com.cn/s/blog_4dff8712010136jh.html 96 | 97 | 98 | 数学之美番外篇:快排为什么那样快【思想高度篇的文章————信息论的知识】 99 | http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/ 100 | 101 | 102 | 为什么说任何基于比较的算法将5个元素排序都需要7次?【信息论的角度】 103 | http://justjavac.com/other/2013/04/10/why-any-sort-algorithm-based-on-the-comparison-of-the-five-elements-are-needed-7-times.html 104 | 105 | 106 | 107 | 108 | 109 | 可视化对比十多种排序算法(C#版) 110 | http://blog.jobbole.com/72850/ 111 | 双向冒泡排序 112 | 冒泡排序 113 | 桶排序 114 | 梳排序 115 | 循环排序 116 | 地精排序 117 | 堆排序 118 | 插入排序 119 | 归并排序 120 | 奇偶排序 121 | 鸽笼排序 122 | 快速排序 123 | 使用冒泡的快排 124 | 选择排序 125 | 希尔排序 126 | -------------------------------------------------------------------------------- /BOOK/《算法竞赛入门经典》/Chapter01-程序设计入门.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | //#include "Chapter01.h" 4 | 5 | 6 | // 第01章 程序设计入门 7 | 8 | 9 | // __________________________1.1 算术表达式__________________________ 10 | // 程序 1-2 计算并输出 8/5的值,保留小数点后1位 11 | printf("%d\n",8/5); // 1 12 | printf("%lf\n",8.0/5.0); // 1.600000 13 | printf("%2lf\n",8.0/5.0); // 1.600000 14 | printf("%.1lf\n",8.0/5.0); // 1.6 15 | 16 | printf("%.1lf\n",8/5); // 0.0 //出现这2个结果的原因 涉及 整数和浮点数编码 17 | printf("%d\n\n",8.0/5.0); // -1717986918 18 | 19 | 20 | // 程序 1-3 复杂的表达式计算 21 | printf("%.8lf\n",1+2*sqrt(3)/(5-0.1)); 22 | 23 | 24 | system("pause"); // 加上这句后,控制台显示运行结果后会显示“请按任意键继续” 25 | 26 | /* 27 | 提示1-1:整数值用%d输出,实数用%lf输出 28 | 提示1-2:整数/整数=整数,浮点数/浮点数=浮点数 29 | */ 30 | 31 | 32 | // __________________________1.2 变量及其输入__________________________ 33 | // 程序 1-4 A+B 问题 34 | int a,b; 35 | //scanf("%d%d",&a,&b); // C 语言版本 36 | cin>>a; // C++ 语言版本 37 | cin>>b; 38 | printf("%d\n",a+b); 39 | 40 | // 程序1-5 圆柱体的表面积 41 | const double pi = 4.0*atan(1.0); 42 | double r,h,s1,s2,s; 43 | cin>>r; 44 | cin>>h; 45 | 46 | s1 = pi*r*r; 47 | s2 = 2*pi*r*h; 48 | s = s1*2.0 + s2; 49 | printf("Area = %.3lf\n",s); 50 | 51 | 52 | 53 | // __________________________1.3 顺序结构程序设计__________________________ 54 | /* 55 | 例题:1-2 三位数反转 56 | 输入一个三位数,分离出它的百位、十位和个位,反转后输出 57 | cin>> 127 58 | cout>> 721 59 | */ 60 | // 程序1-7 三位数反转 61 | for ( ;1;) // 加了for循环来不停的输入数据。 62 | { 63 | int n,m; 64 | cin>>n; 65 | 66 | m = (n%10)*100 + (n/10%10)*10 + (n/100); 67 | printf("%03d\n",m); 68 | printf("%d\n",m); 69 | printf("%02d\n\n",m); 70 | } 71 | 72 | /* 73 | %d就是普通的输出了 74 | %2d是将数字按宽度为2,采用右对齐方式输出,若数据位数不到2位,则左边补空格 75 | %02d,和%2d差不多,只不过左边补0 76 | */ 77 | 78 | /* 79 | 例题:1-3 交换变量 80 | 输入两个整数a和b,交换二者的值,然后输出。 81 | 样例输入:824 16 82 | 样例输出:16 824 83 | */ 84 | // 程序1-8 变量交换 85 | 86 | // __________________________1.4 分支结构程序设计__________________________ 87 | /* 88 | 例题:1-4 鸡兔同笼 89 | 已知鸡和兔的总数量为n,总腿数为m.依次输出鸡的数目和兔的数目。 90 | 如果无解,则输出“No answer” 91 | */ 92 | // 程序1-11 鸡兔同笼 93 | 94 | 95 | /* 96 | 例题:1-5 三整数排序 97 | 输入3个整数,从小到大排序后输出。 98 | 样例输入:20 7 33 99 | 样例输出:7 20 33 100 | */ 101 | 102 | 103 | // __________________________1.5 小结与习题__________________________ 104 | 105 | 106 | /* 107 | 1.5.6 上机练习 -P30/241 108 | 有大量的习题,太简单了。。。。 109 | */ 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /BOOK/《Cracking the Coding Interview》/1.4替换空格.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 数组与字符串 4 | 5 | 6 | // __________________________ 1.4 __________________________ 7 | /* 8 | 编写一个方法,将字符串中的空格全部替换为“%20”。假定该字符串尾部有足够的空间存放新增字符,并且知道字符串的“真实”长度 9 | */ 10 | 11 | 12 | // __________________________ 解决思路 __________________________ 13 | /* 14 | 解法01: 用队列来实现, 15 | 01-遇到空格后,就往队列里压入 3个元素,%20。 16 | 02-队列非空时,把当前字符压入队列,然后从队首弹出一个元素,写入当前位置字符处。 17 | 03-最后在末尾加入 18 | 复杂度:1个for循环,用到一个额外的数据结构 队列 19 | 20 | 解法02: 从后往前遍历 21 | 01-先遍历一遍算出有多少个空格 22 | 02-再从后往前遍历写原来的字符串 23 | 复杂度:2个for循环,不用额外开辟内存。 24 | 25 | 对比:一个速度快,但占用空间可能会多一点。 26 | 一个速度慢点,但是不占用额外的任何空间。 27 | 28 | 解法03:使用 string 的 replace 29 | 30 | 现实中实际用处: 31 | 就是URL 中的空格需要替换成 %20 32 | */ 33 | 34 | 35 | 36 | // __________________________ 实现细节 __________________________ 37 | 38 | // 解法01:用队列__________________________ 39 | #include 40 | 41 | char * insertString( char * str ) 42 | { 43 | // 容错处理 44 | if( str == NULL )return NULL; 45 | int len = strlen(str); 46 | if( len == 0 )return NULL; 47 | 48 | queue q_insert; 49 | 50 | int num_gap = 0; 51 | for(int i = 0; i0; --i) 93 | { 94 | if(str[i] == ' ') 95 | { 96 | str[--newlen] = '0'; 97 | str[--newlen] = '2'; 98 | str[--newlen] = '%'; 99 | }else{ 100 | str[--newlen] = str[i]; 101 | } 102 | } 103 | 104 | return str; 105 | } 106 | 107 | // 解法03:使用 string.replace()__________________________ 108 | void repalce_space( string& str,string& insert ) 109 | { 110 | int pos; 111 | while( (pos = str.find(' ')) != -1) 112 | { 113 | str.replace(pos, 1, insert); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /排序算法/06-归并排序.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 06-归并排序 4 | 5 | 6 | /* 7 | 归并:即将2个有序的数组归并成一个更大的有序数组。很快人们就根据这个操作发明了一种简单的递归排序算法:归并排序。 8 | 要将一个数组排序,可以先(递归地)将它分成2半分别排序,然后将结果归并起来。 9 | 10 | 归并排序最吸引人的性质是它能够保证将任意长度为N的数组排序所需时间和NlogN成正比;它的主要缺点则是它所需要的额外空间和N成正比。 11 | */ 12 | 13 | 14 | /* 15 | 介绍: 16 | 前面我们讲了堆排序,因为它用到了完全二叉树,充分利用了完全二叉树的深度是⌊log2n⌋+1的特性,所以效率比较高。不过堆结构的设计本身是比较复杂的, 17 | 老实说,能想出这样的结构就挺不容易,有没有更直接简单的办法利用完全二叉树来排序呢?当然是有。 18 | 先来举一个例子。你们知道高考一本、二本、专科分数线是如何划分出来的吗? 19 | 简单地说,如果各高校本科专业在某省高三理科学生中计划招收1万名,那么将全省参加高考的理科学生分数倒排序, 20 | 第1万名的总分数就是当年本科生的分数线(现实可能会比这复杂,这里简化之)。 21 | 也就是说,即使你是你们班级第一、甚至年级第一名,如果你没有上分数线,则说明你的成绩排不到全省前1万名,你也就基本失去了当年上本科的机会了。 22 | 换句话说,所谓的全省排名,其实也就是每个市、每个县、每个学校、每个班级的排名合并后再排名得到的。注意我这里用到了合并一词。 23 | 我们要比较两个学生的成绩高低是很容易的,比如甲比乙分数低,丙比丁分数低。那么我们也就可以很容易得到甲乙丙丁合并后的成绩排名,同样的, 24 | 戊己庚辛的排名也容易得到,由于他们两组分别有序了,把他们八个学生成绩合并有序也是很容易做到的了,继续下去……最终完成全省学生的成绩排名, 25 | 此时高考状元也就诞生了。 26 | 为了更清晰地说清楚这里的思想,大家来看图9-8-1,我们将本是无序的数组序列{16,7,13,10,9,15,3,2,5,8,12,1,11,4,6,14},通过两两合并排序后, 27 | 再合并,最终获得了一个有序的数组。注意仔细观察它的形状,你会发现,它像极了一棵倒置的完全二叉树,通常涉及到完全二叉树结构的排序算法, 28 | 效率一般都不低的——这就是我们要讲的归并排序法。 29 | */ 30 | 31 | 32 | /* 33 | 历史背景: 34 | 35 | */ 36 | 37 | 38 | /* 39 | 思路来源: 40 | “归并”一词的中文含义就是合并、并入的意思,而在数据结构中的定义是将两个或两个以上的有序表组合成一个新的有序表。 41 | 归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1, 42 | 然后两两归并,得到⌈n/2⌉(⌈x⌉表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止, 43 | 这种排序方法称为2路归并排序。 44 | 好了,有了对归并排序的初步认识后,我们来看代码。 45 | */ 46 | 47 | 48 | /* 对顺序表L作归并排序 */ 49 | void MergeSort(SqList *L) 50 | { 51 | MSort(L->r,L->r,1,L->length); 52 | } 53 | 54 | 55 | /* 将SR[s..t]归并排序为TR1[s..t] */ 56 | void MSort(int SR[],int TR1[],int s, int t) 57 | { 58 | int m; 59 | int TR2[MAXSIZE+1]; 60 | if(s==t) 61 | TR1[s]=SR[s]; 62 | else 63 | { 64 | m=(s+t)/2; /* 将SR[s..t]平分为SR[s..m]和SR[m+1..t] */ 65 | MSort(SR,TR2,s,m); /* 递归地将SR[s..m]归并为有序的TR2[s..m] */ 66 | MSort(SR,TR2,m+1,t); /* 递归地将SR[m+1..t]归并为有序TR2[m+1..t] */ 67 | Merge(TR2,TR1,s,m,t); /* 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t] */ 68 | } 69 | } 70 | 71 | 72 | /* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */ 73 | void Merge(int SR[],int TR[],int i,int m,int n) 74 | { 75 | int j,k,l; 76 | for(j=m+1,k=i;i<=m && j<=n;k++) /* 将SR中记录由小到大归并入TR */ 77 | { 78 | if (SR[i] 38 | void swip(type &a, type &b) 39 | { 40 | type temp = a; 41 | a = b; 42 | b = tmep; 43 | } 44 | 45 | 46 | void SelectSort0(int array[], int length) // length 为数组的长度。 47 | { 48 | int i,j,min; 49 | for(i = 0;i= end) 79 | return; 80 | 81 | int base = a[start]; //temp中存的就是基准数 82 | int left = start; 83 | int right = end; 84 | while(left != right) 85 | { 86 | //顺序很重要,要先从右边开始找 87 | while(a[right] >= base && left < right) right--; 88 | 89 | //再找右边的 90 | while(a[left] <= base && left < right) left++; 91 | 92 | //交换两个数在数组中的位置 93 | if(left < right) swap(a[left], a[right]); 94 | } 95 | 96 | //最终将基准数归位 97 | a[start] = a[left]; 98 | a[left] = base; 99 | 100 | quicksort(a[], start, left - 1); //继续处理左边的,这里是一个递归的过程 101 | quicksort(a[], left + 1, end); //继续处理右边的 ,这里是一个递归的过程 102 | } 103 | 104 | 105 | /* 106 | 分析: 107 | 快速排序的时间主要耗费在划分操作上,对长度为k的区间进行划分,共需k-1次关键字的比较。 108 | 109 | 最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空), 110 | 而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。时间复杂度为O(n*n) 111 | 112 | 在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。 113 | 总的关键字比较次数:O(nlgn) 114 | 115 | 尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。 116 | 它的平均时间复杂度为O(nlgn)。 117 | */ -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/Classes/01-windowsInit.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | #include 10 | #if 1 11 | #include "01-windowsInit.h" 12 | #include "01-SimpleGraphics.h" 13 | //#include "02-ComplexGraphics.h" 14 | 15 | #include "00-DataDefinition.h" 16 | //#include "[zsj]-3D Coordinate System.h" 17 | 18 | #define CUBE_ARRAY_SIZE 24 //立方体每个面有4个顶点 19 | float* OpenGLwindows::m_corners = NULL; 20 | OpenGLwindows* OpenGLwindows::m_pSceneManager = NULL; 21 | int OpenGLwindows::frameCount = 0; 22 | 23 | float screen_scale = 1.0f/2000.0f; // 缩放比例 24 | int delay_call = 500; // ms 毫秒,延迟多少 毫秒来调用函数。 25 | 26 | OpenGLwindows* OpenGLwindows::sharedSceneManager() 27 | { 28 | if(m_pSceneManager == NULL) 29 | { 30 | m_pSceneManager = new OpenGLwindows(); 31 | } 32 | return m_pSceneManager; 33 | } 34 | 35 | 36 | 37 | void OpenGLwindows::myDisplay(void) 38 | { 39 | //myDisplay_Draw_3D_Coordinate_System_On_Windows_function(); 40 | myDisplay_GL_POLYGON(); 41 | } 42 | 43 | // 输入 44 | void ProcessMouse(int button,int state,int x,int y) // 鼠标响应 45 | { 46 | if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) 47 | { 48 | GLfloat ang = -10; 49 | glRotatef(ang, -1,0,1); 50 | glutPostRedisplay(); 51 | } else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) 52 | { 53 | GLfloat ang = 10; 54 | glRotatef(ang, -1,0,1); 55 | glutPostRedisplay(); 56 | } 57 | } 58 | 59 | 60 | //========================================================================= 61 | // 62 | // 窗口初始化 63 | // 64 | //========================================================================= 65 | int OpenGLwindows::windowsInit(int argc, _TCHAR* argv[]) 66 | { 67 | glutInit(&argc, (char**) argv); // 初始化glut,必须调用,复制黏贴这句话即可 68 | glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); // 设置显示方式,RGB、单缓冲。当然还有GLUT_INDEX索引颜色 GLUT_DOUBLE双缓冲(Qt中看到过双缓冲) 69 | glutInitWindowPosition(300,300); // 窗口在显示器屏幕中的位置,指定的是窗口左上角的坐标。(0,0)就会显示在屏幕左上角。 70 | glutInitWindowSize(ScreenSize.x, ScreenSize.y); // 设置所需窗口大小 71 | 72 | //glutCreateWindow("第一个OpenGL程序"); // 创建窗口,设置标题 73 | glutCreateWindow((char*)argv[0]); // 这样也行啊? 74 | //glutDisplayFunc(&this->myDisplay); // 当绘制窗口时调用myDisplay,像Cocos2d-x刷帧Draw中的操作 75 | //glutDisplayFunc(&(OpenGLwindows::myDisplay)); 76 | //glutDisplayFunc((OpenGLwindows::sharedSceneManager()->myDisplay)); 77 | glutDisplayFunc(myDisplay); 78 | glutMouseFunc(&ProcessMouse); 79 | //glutTimerFunc(500 + delay_call, TimerFunction, 1); // 定时器的使用, 输入的参数必须用 static 函数。 第1次调用需要 500ms 以上才能保证myDisplay()那边的初始化完成。 80 | glutMainLoop(); // 无限执行的循环,glutMainLoop()会判断窗口是否需要进行重绘, 会自动调用 glutDisplayFunc()中注册的函数。 81 | return 0; 82 | } 83 | 84 | /* 初始化说明 85 | 1、首先将必要的头文件glut.h包含进来,以便使用opengl提供的函数库 86 | 2、mydisplay是程序员自己写的回调函数,用于在窗口内进行画图。在主函数里用glutDisplayFunc把它注册之后, 87 | 每次窗口里的图像需要刷新时,就调用这个我们写的mydisplay函数。 88 | 3、glutInitWindowSize函数指定窗体的宽度和高度 89 | 4、glutInitWindowPosition指定窗口左上角的坐标 90 | 5、glutCreateWindow建立一个窗体 91 | 6、glutMainLoop进入窗体消息循环。 92 | 以后如无特别说明,后面的例子代码只改动display函数的内容即可。 93 | 94 | 95 | glutTimerFunc 参数对应关系为:glutTimerFunc(毫秒数, 回调函数指针, 区别值); 96 | 97 | (1)如果用定时器的话,初始的时候注册一个定时器的回调函数,原型是 98 | glutTimerFunc(unsigned int millis, void (*func)(int value), int value); 99 | 参数对应关系为:glutTimerFunc(毫秒数, 回调函数指针, 区别值); 100 | (2)写自己的回调函数 void OnTimer(int value); 101 | 用value区分是哪个定时器 102 | (3)在函数里改变和位置有关的变量,然后调用glutPostRedisplay();用来重绘 103 | (4)最后再次调用glutTimerFunc,因为glut的定时器是调用一次才产生一次定时,所以如果要持续产生定时的话, 104 | */ 105 | 106 | void OpenGLwindows::TimerFunction(int value) 107 | { 108 | frameCount++; 109 | 110 | //glutPostRedisplay(); 111 | GLfloat ang = 10; 112 | glRotatef(ang, -1,0,1); 113 | glutPostRedisplay(); 114 | 115 | glutTimerFunc(delay_call, TimerFunction, 1); 116 | } 117 | 118 | #endif -------------------------------------------------------------------------------- /BOOK/《挑战程序设计竞赛》/01-蓄势待发——准备篇.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 第01章 蓄势待发——准备篇 4 | /* 5 | 6 | */ 7 | 8 | // __________________________1.1 何谓程序设计竞赛__________________________ 9 | /* 10 | 抽签 11 | 你的朋友提议玩一个游戏:将写有数字的n个纸片放入口袋中,你可以从口袋中抽取4次纸片,每次记下纸片上的数字后都将其放回口袋中。 12 | 如果这4个数字的和是m,就是你赢,否则就是你的朋友赢。 13 | 请你编写一个程序,判断当纸片上所写的数字是k1,k2,...,kn时,是否存在抽取4次和为m的方案。如果存在,输出Yes;否则,输出No 14 | 15 | 限制条件 16 | 1<=n<=50 17 | 1<=m<=10^8 18 | 1<=ki<=10^8 19 | 20 | 样例: 21 | 输入: n = 3 22 | m = 10 23 | k = {1,3,5} 24 | 输出: Yes,例如4次抽取的结果是 1,1,3,5 和就是 10 25 | 26 | 27 | 方案:4重循环穷举。 28 | */ 29 | 30 | 31 | 32 | // __________________________1.2 最负盛名的程序设计竞赛__________________________ 33 | 34 | // 1.2.1 Google Code Jam (GCJ) http://code.google.com/codejam/ 35 | // 1.2.2 TopCoder 36 | // 1.2.3 ACM (大学生) 37 | // 1.2.4 IOI(初中,高中) 38 | /* 1.2.5 Online Judge(OJ) 39 | 40 | PKU Online Judge(POJ) http://poj.org 41 | 题库中有大量的题目 42 | 43 | Sphere Online Judge(SPOJ) http://www.spoj.pl/ 44 | 允许使用各种各样的编程语言 45 | 46 | SGU Online Contester http://acm.sgu.ru/ 47 | 具有模拟参加历史比赛的虚拟赛功能 48 | 49 | Codeforces http://codeforces.com 50 | 不断维护历届题库 51 | */ 52 | 53 | 54 | 55 | // __________________________1.4 如何提交解答__________________________ 56 | 57 | 58 | 59 | 60 | // __________________________1.6 轻松热身__________________________ 61 | // 1.6.1 先从简单题开始 62 | /* 63 | 三角形 64 | 有n根棍子,棍子的长度为ai.想要从中选出3根棍子组成周长尽可能长的三角形。 65 | 请输出最大的周长,若无法组成三角形则输出0. 66 | 67 | 限制条件 68 | 3<=n<=100 69 | 1<=a[i]<=10^6 70 | 71 | 样例: 72 | 输入: n = 5 73 | a = {2,3,4,5,10} 74 | 输出: 12 75 | */ 76 | 77 | /* 78 | 书上用的方法:首先用三重循环枚举所有棍子的选择方案,再利用上式判断能否组成三角形。如果可以,那么该三角形就是备选方案。 79 | 这里用了三重循环,所以复杂度是O(n^3) 80 | */ 81 | 82 | /* 改进方法 83 | 首先对数组排序,然后从最大的数字开始,只要遍历1遍就可以找到周长最长的 三角形。 84 | 所以这里的复杂度其实是涉及到一个排序。 85 | */ 86 | i = n; 87 | longgirth = 0; 88 | while(i-2>-1) 89 | { 90 | if(a[i]>n; 44 | cin>>k; 45 | 46 | // 待加 输入范围检测处理 1>max_line; 111 | int col = 0,row = max_line - 1; // 设置填入的 sneak 的第一个位置 112 | int colChange = COL_DOWN ,rowChange = ROW_STAY ; // 设置的 sneak 的初始前进方向 113 | 114 | for (int i =1; i<=max_line*max_line; i++) 115 | { 116 | crawl[col][row] = i; 117 | int colNext = col + colChange ; 118 | int rowNext = row + rowChange ; 119 | 120 | if ( !((colNext == max_line)||(rowNext == max_line)||(colNext == -1)||(rowNext == -1)) ) //数组没有越界 121 | { 122 | if ( crawl[colNext][rowNext] == 0 ) // 没有已经赋值。 123 | { 124 | col = colNext; 125 | row = rowNext; 126 | continue; //不执行换方向的动作 127 | } 128 | } 129 | 130 | // 执行换方向的动作。 131 | if ( colChange == COL_DOWN ) 132 | { 133 | colChange = COL_STAY; 134 | rowChange = ROW_LEFT; 135 | }else if ( colChange == COL_UP ) 136 | { 137 | colChange = COL_STAY; 138 | rowChange = ROW_RIGHT; 139 | }else if ( rowChange == ROW_LEFT ) 140 | { 141 | colChange = COL_UP; 142 | rowChange = ROW_STAY; 143 | }else if ( rowChange == ROW_RIGHT ) 144 | { 145 | colChange = COL_DOWN; 146 | rowChange = ROW_STAY; 147 | } 148 | 149 | col += colChange ; 150 | row += rowChange ; 151 | } 152 | 153 | // 打印 sneak matrix 154 | for ( int i = 0; i max) max = j-i+1; 216 | 217 | } 218 | } 219 | 220 | printf("max = %d\n",max); 221 | 222 | /* 223 | 还有第2种修改版来显示。这个写的可读性 也太差了吧。 224 | P45(60/241) 225 | */ 226 | 227 | 228 | // __________________________3.4 小结与习题__________________________ 229 | 230 | // 3.4.1 必要的存储量 231 | 232 | 233 | // 3.4.3 补码表示法 234 | 235 | 236 | // 3.4.4 重新实现库函数 237 | 238 | 239 | // 3.4.5 字符串处理的常见问题 240 | 241 | 242 | // 3.4.6 关于输入输出 243 | 244 | 245 | // 3.4.7 I/O的效率 246 | 247 | // 3.4.8 小结 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /阶段-状态转移/动态规划/什么是动态规划.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 什么是动态规划 4 | 5 | 1> 动态规划中递推式的求解方法不是动态规划的本质。 6 | 7 | 2> 动态规划是对于 某一类问题 的解决方法! 8 | 9 | 3> 动态规划的实现方法有两大类:“自顶向下”的实现方式和“自低向上”的实现方式。 10 | 11 | 动态规划主要有两个要点: 12 | 13 | 1.状态和转移 14 | 15 | 2.满足无后效性要求,基本就可以完成一个动态规划了 16 | 17 | 18 | ## 动态规划中递推式的求解方法不是动态规划的本质。 19 | 20 | 网址来源: 21 | > 22 | http://www.zhihu.com/question/23995189 23 | 24 | 讲的很好。 25 | 26 | 27 | 28 | ## 动态规划是对于 某一类问题 的解决方法! 29 | 30 | 网址来源: 31 | > 32 | http://www.zhihu.com/question/23995189/answer/35429905 33 | 34 | 动态规划的本质不在于是递推或是递归,也不需要纠结是不是内存换时间。 35 | 36 | 理解动态规划并不需要数学公式介入,只是完全解释清楚需要点篇幅…首先需要明白哪些问题不是动态规划可以解决的,才能明白为神马需要动态规划。不过好处时顺便也就搞明白了递推贪心搜索和动规之间有什么关系,以及帮助那些总是把动规当成搜索解的同学建立动规的思路。当然熟悉了之后可以直接根据问题的描述得到思路,如果有需要的话再补充吧。 37 | 38 | 动态规划是对于 某一类问题 的解决方法!!重点在于如何鉴定“某一类问题”是动态规划可解的而不是纠结解决方法是递归还是递推! 39 | 40 | 怎么鉴定dp可解的一类问题需要从计算机是怎么工作的说起… **计算机的本质是一个状态机,内存里存储的所有数据构成了当前的状态,CPU只能利用当前的状态计算出下一个状态** (不要纠结硬盘之类的外部存储,就算考虑他们也只是扩大了状态的存储容量而已,并不能改变下一个状态只能从当前状态计算出来这一条铁律) 41 | 42 | 43 | 当你企图使用计算机解决一个问题是,其实就是在思考**如何将这个问题表达成状态**(用哪些变量存储哪些数据)以及**如何在状态中转移**(怎样根据一些变量计算出另一些变量)。 44 | 45 | 所以 **所谓的空间复杂度就是为了支持你的计算所必需存储的状态最多有多少,所谓时间复杂度就是从初始状态到达最终状态中间需要多少步!** 46 | 47 | 太抽象了还是举个例子吧: 48 | 49 | 比如说我想计算第100个非波那契数,每一个非波那契数就是这个问题的一个状态,每求一个新数字只需要之前的两个状态。所以同一个时刻,最多只需要保存两个状态,空间复杂度就是常数;每计算一个新状态所需要的时间也是常数且状态是线性递增的,所以时间复杂度也是线性的。 50 | 51 | 上面这种状态计算很直接,只需要依照固定的模式从旧状态计算出新状态就行(a[i]=a[i-1]+a[i-2]),**不需要考虑是不是需要更多的状态,也不需要选择哪些旧状态来计算新状态。对于这样的解法,我们叫递推。** 52 | 53 | 非波那契那个例子过于简单,以至于让人忽视了阶段的概念,所谓**阶段是指随着问题的解决,在同一个时刻可能会得到的不同状态的集合**。非波那契数列中,每一步会计算得到一个新数字,所以每个阶段只有一个状态。想象另外一个问题情景,假如把你放在一个围棋棋盘上的某一点,你每一步只能走一格,因为你可以东南西北随便走,所以你当你同样走四步可能会处于很多个不同的位置。从头开始走了几步就是第几个阶段,**走了n步可能处于的位置称为一个状态,走了这n步所有可能到达的位置的集合就是这个阶段下所有可能的状态**。 54 | 55 | 现在问题来了,有了阶段之后,计算新状态可能会遇到各种奇葩的情况,针对不同的情况,就需要不同的算法,下面就分情况来说明一下: 56 | 57 | 58 | 假如问题有n个阶段,每个阶段都有多个状态,不同阶段的状态数不必相同,一个阶段的一个状态可以得到下个阶段的所有状态中的几个。那我们要计算出最终阶段的状态数自然要经历之前每个阶段的某些状态。 59 | 60 | 好消息是,有时候我们并不需要真的计算所有状态,比如这样一个弱智的棋盘问题:从棋盘的左上角到达右下角最短需要几步。答案很显然,用这样一个弱智的问题是为了帮助我们理解阶段和状态。某个阶段确实可以有多个状态,正如这个问题中走n步可以走到很多位置一样。但是同样n步中,有哪些位置可以让我们在第n+1步中走的最远呢?没错,正是第n步中走的最远的位置。换成一句熟悉话叫做“下一步最优是从当前最优得到的”。**所以为了计算最终的最优值,只需要存储每一步的最优值即可,解决符合这种性质的问题的算法就叫贪心。**如果只看最优状态之间的计算过程是不是和非波那契数列的计算很像?所以计算的方法是递推。 61 | 62 | 既然问题都是可以划分成阶段和状态的。这样一来我们一下子解决了一大类问题:**一个阶段的最优可以由前一个阶段的最优得到。** 63 | 64 | 如果一个阶段的最优无法用前一个阶段的最优得到呢? 65 | 66 | 什么你说只需要之前两个阶段就可以得到当前最优?那跟只用之前一个阶段并没有本质区别。**最麻烦的情况在于你需要之前所有的情况才行。** 67 | 68 | 再来一个迷宫的例子。在计算从起点到终点的最短路线时,你不能只保存当前阶段的状态,因为题目要求你最短,所以你必须知道之前走过的所有位置。因为即便你当前再的位置不变,之前的路线不同会影响你的之后走的路线。这时你需要保存的是之前每个阶段所经历的那个状态,根据这些信息才能计算出下一个状态! 69 | 70 | 每个阶段的状态或许不多,但是每个状态都可以转移到下一阶段的多个状态,所以解的复杂度就是指数的,因此时间复杂度也是指数的。哦哦,刚刚提到的之前的路线会影响到下一步的选择,这个令人不开心的情况就叫做**有后效性**。 71 | 72 | 刚刚的情况实在太普遍,解决方法实在太暴力,有没有哪些情况可以避免如此的暴力呢? 73 | 74 | 契机就在于后效性。 75 | 76 | 有一类问题,看似需要之前所有的状态,其实不用。不妨也是拿最长上升子序列的例子来说明为什么他不必需要暴力搜索,进而引出动态规划的思路。 77 | 78 | 假装我们年幼无知想用搜索去寻找最长上升子序列。怎么搜索呢?需要从头到尾依次枚举是否选择当前的数字,每选定一个数字就要去看看是不是满足“上升”的性质,这里第i个阶段就是去思考是否要选择第i个数,第i个阶段有两个状态,分别是选和不选。哈哈,依稀出现了刚刚迷宫找路的影子!咦慢着,每次当我决定要选择当前数字的时候,只需要和之前选定的一个数字比较就行了!这是和之前迷宫问题的本质不同!这就可以纵容我们不需要记录之前所有的状态啊!既然我们的选择已经不受之前状态的组合的影响了,那时间复杂度自然也不是指数的了啊!虽然我们不在乎某序列之前都是什么元素,但我们还是需要这个序列的长度的。所以我们只需要记录以某个元素结尾的LIS长度就好!因此第i个阶段的最优解只是由前i-1个阶段的最优解得到的,然后就得到了DP方程(感谢 @韩曦 指正) 79 | 80 | 81 | **所以一个问题是该用递推、贪心、搜索还是动态规划,完全是由这个问题本身阶段间状态的转移方式决定的!** 82 | 83 | **每个阶段只有一个状态->递推**; 84 | 85 | **每个阶段的最优状态都是由上一个阶段的最优状态得到的->贪心**; 86 | 87 | **每个阶段的最优状态是由之前所有阶段的状态的组合得到的->搜索**; 88 | 89 | **每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->动态规划**。 90 | 91 | >每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到 92 | 93 | 这个性质叫做**最优子结构**; 94 | 95 | >而不管之前这个状态是如何得到的 96 | 97 | 这个性质叫做**无后效性**。 98 | 99 | 另:其实动态规划中的最优状态的说法容易产生误导,以为只需要计算最优状态就好,LIS问题确实如此,转移时只用到了每个阶段“选”的状态。但实际上有的问题往往需要对每个阶段的所有状态都算出一个最优值,然后根据这些最优值再来找最优状态。比如背包问题就需要对前i个包(阶段)容量为j时(状态)计算出最大价值。然后在最后一个阶段中的所有状态种找到最优值。 100 | 101 | 102 | ## 动态规划的实现方法有两大类:“自顶向下”的实现方式和“自低向上”的实现方式。 103 | 104 | 网址来源: 105 | > 106 | http://www.zhihu.com/question/23995189 107 | 108 | @熊大大 的回答真好,理解的准确又深刻。 109 | 我的答案无法给出更深刻的视角,试试看看能不能在其他方面创造一点价值。 110 | 111 | 首先,看完这篇回答你会弄清楚这么几个事实: 112 | 113 | 1. 动态规划是解决问题的一种方法。 114 | 115 | 2. 动态规划的本质在于它看待问题的角度,即它试图将问题定义成这样的形式:原问题的解如何由子问题的解组合而成。一旦得到这种形式的问题定义,动态规划是有一套成熟的实现方式的。 116 | 117 | 3. 动态规划的实现方法有两大类:“自顶向下”的实现方式和“自低向上”的实现方式。其中**递归属于自顶向下的实现方式(是的,递归只是动态规划的一种实现方式)**。至于人们长提起的Caching所在的层次还要更低一点,它是自顶向下实现方式或者自低向上实现方式中用到的一种优化方法(也就是说递归实现完全可以不用caching,只不过效率差罢了) 118 | 119 | 好啦,下面是更详细附带栗子和饮料的长答案: 120 | 121 | 1. 动态规划是解决问题的一种方法。 122 | 是的,它只是问题的一种解决方法。一个问题完全可能同时适用多种解决方法。 123 | 124 | 2. 动态规划的本质是它试图将问题定义成这样的形式:原问题的解如何由子问题的解组合而成。如何无法将问题定义成这样的形式,那么很抱歉说明这个问题无法以动态规划求解。 125 | 我们常常说的状态转移方程,其实就是这句话的数学表达——“**原问题的解如何由子问题的解组合而成**”。所以 @熊大大 说“动态规划的本质,是对问题状态的定义和状态转移方程的定义。”是完全严谨准确的。 126 | 127 | 3. 对于有经验的人来说,得到了状态转移方程,该问题就已经解决了。 128 | 因为动态规划的实现就“自顶向下”(top-down dynamic programming:) 和 “自低向上” (bottom-up dynamic programming:)两种。 129 | 其中**递归属于“自顶向下”的实现方法**,而caching则只不过是为了提升效率采用的优化方法。 130 | 131 | 4. “自顶向下”的实现方式 和 “自低向上”的实现方式各有什么优缺点,我的理解如下。 132 | 两种方法的取舍我个人的喜好是——优先选择Top-Down dynamic programming,除非不容易得到递归公式或空间复杂度无法接受。 133 | 134 | “**自顶向下**”(top-down dynamic programming): 135 | 136 | 1.能方便的得到递归公式,并用递归函数实现 137 | 138 | 2.保持了递归实现的代码结构,**逻辑上容易理解**。 139 | 140 | 3.过程中只计算需要计算的子结果。 141 | 142 | 4.当采用了caching技术时多次被调用时天然的复用之前被调用时的子结果。(比如连续两次计算fibonacci数F(4), F(5),则计算F(5)时已知F(3)和F(4),直接相加即可) 143 | 144 | “**自底向上**”(bottom-up dynamic programming): 145 | 146 | 1.需要设计数据结构来完成自底向上的计算过程。逻辑上相对不那么直观。 147 | 148 | 2.常常可以进行**空间复杂度的优化**。比如Fibonacci数列可以优化为只使用两个变量的额外存储空间,0-1背包问题可以优化为O(n)的空间复杂度。 149 | 150 | 3.若进行了空间优化,则连续多次被调用时无法复用之前被调用的子结果。 151 | 152 | 153 | 最后,如果想要更全面完整的理解动态规划,我的学习路线上只有两步: 154 | 155 | 1. 读一下Algrithm in C这本书的相应章节 156 | 157 | 2. 把几个经典动态规划问题分别用“自顶向下”和“自低向上”两种方法实现一遍,用任何你喜欢的语言都行 -------------------------------------------------------------------------------- /排序算法/00-排序标准.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 00-排序标准 4 | 5 | 6 | // __________________________ __________________________ 参考代码 __________________________ __________________________ 7 | /// 自己练习后 与 参考代码 进行对比 。 8 | 9 | 10 | 11 | // __________________________1 冒泡排序-带哨兵 __________________________ 12 | // 默写练习次数:1 13 | void bubbleSort(int array[],int length) 14 | { 15 | int i,j; 16 | bool fuck = false; 17 | 18 | for(i = 0;(ii;j--) 22 | { 23 | if(array[j]=1)&&(array[j-1]>ntemp);j-=1) 67 | { 68 | array[j] = array[j-1]; 69 | } 70 | array[j] = ntemp; 71 | } 72 | } 73 | 74 | 75 | // __________________________4 希尔排序 __________________________ 76 | // 默写练习次数:1 77 | // 就是把上面的 1全部替换成一个 变量 ngap 78 | 79 | void shellSort(int array[],int length) 80 | { 81 | int i,j,ntemp,ngap; 82 | for(ngap = length/3 + 1;ngap>0;ngap=ngap/3+1) 83 | { 84 | for(i = ngap;i=ngap&&array[j-ngap]>ntemp;j-=ngap) 88 | { 89 | array[j] = array[j-ngap]; 90 | } 91 | array[j] = ntemp; 92 | } 93 | } 94 | } 95 | 96 | 97 | 98 | // __________________________5 堆排序 __________________________ 99 | // 默写练习次数: 100 | void heapSort(int array[],int length) 101 | { 102 | int i; 103 | for(i = length/2-1;i>=0;i--) 104 | { 105 | percolatedown(array[],length,i); 106 | } 107 | 108 | for(i = length;i>0;i--) 109 | { 110 | swap(array[0],array[i-1]); 111 | percolatedown(array[],i-1,0); 112 | } 113 | } 114 | 115 | void percolatedown(int arr[],int length,int index) 116 | { 117 | int ntemp; 118 | int nchild; 119 | for(ntemp = array[index];2*index+1<=length-1;index =nchild) 120 | { 121 | nchild = 2*index+1; 122 | if( nchild != length-1 && array[nchild]= right ) 191 | return; 192 | 193 | int i = left; 194 | int j = right; 195 | int base = array[left]; 196 | 197 | while(i!=j) 198 | { 199 | while( i=base ) j--; 200 | 201 | while( i11,所以不需要交换。 123 | 继续左移一位,到达值为1的节点,判断然后交换后如下: 124 | 125 | 2 126 | / \ 127 | 3 34 128 | / \ / \ 129 | 29 79 16 1 130 | / \ / \ 131 | 11 10 33 70 132 | 133 | 接着移动到值为3的节点,判断后应该是交换3和79。注意3的节点也有子节点,且此时破坏了原来的堆序性质,因为3<33&&3<70, 134 | 所以此时应该继续对3这个节点进行下滤,直到找到正确位置,类似于冒泡排序,小的节点直接沉到最低端。最后结果为: 135 | 136 | 2 137 | / \ 138 | 79 34 139 | / \ / \ 140 | 29 70 16 1 141 | / \ / \ 142 | 11 10 33 3 143 | 144 | 然后继续移动当前节点到2,下滤完成以后,最后一个完好的二叉堆就新鲜出炉了。如下: 145 | 146 | 79 147 | / \ 148 | 70 34 149 | / \ / \ 150 | 29 33 16 1 151 | / \ / \ 152 | 11 10 2 3 153 | 154 | 建好以后,下面就是排序了。我们知道最大堆的堆顶元素是最大的,那么我取出堆顶元素,放到一个新数组的最后,然后恢复最大堆的顺序,再取出堆顶, 155 | 放入新数组的倒数第二个,依次这样进行,最后得到的新数组就是一个递增排序好了的序列了。不过这样做,浪费空间,可以不用去申请一个新数组吗? 156 | 答案是:yes。如何实现?很简单,排序时,我们交换堆顶元素和堆底元素(其实就是数组的最后一个元素),然后恢复堆的有序性,堆的大小减一, 157 | 即舍弃最后的那个最大的元素。接着再交换堆顶和堆底,继续恢复有序性,堆的大小减一,如此反复。。。。最后堆就是一个完全排好序的数组了。 158 | 是不是很简单?好吧,我承认还是有点复杂的。也许你会问:交换堆顶和堆底后,恢复堆的有序性不会很复杂吗?答案当然是:no!!, 159 | 因为要恢复堆的有序性,同样只是而且仅仅只是将堆顶元素执行下滤操作而已,这个其实是非常快的,复杂度是O(lgh),h是这个堆的高度。 160 | ok,讲完了,上代码: 161 | 162 | */ 163 | 164 | /* 165 | 编号为i的父节点为:(i-1)/2 166 | 左节点:2*i+1 167 | 右节点:2*i+2 168 | 169 | 数组长度为 nsize 的最后一个非叶子节点: 170 | i = nsize/2-1; 171 | */ 172 | 173 | 174 | void HeapSort(int arr[], int nsize) 175 | { 176 | int i; 177 | for(i=nsize/2-1; i>=0; i--) //从最后一个非叶子节点开始,对每个非叶子节点进型最小根调整,保证每个根节点都是其子树中的最小值 178 | { 179 | percolatedown(arr, nsize, i); // 进行下滤操作 180 | } 181 | 182 | for(i=nsize; i>0; i--) 183 | { 184 | swap(&arr[0],&arr[i-1]); //交换头尾元素 185 | percolatedown(arr, i-1, 0); //重新恢复堆序 186 | } 187 | 188 | } 189 | void percolatedown(int arr[], int nsize, int index) 190 | { 191 | int ntemp; 192 | int nchild; 193 | for (ntemp=arr[index]; 2*index+1<=nsize-1; index=nchild) 194 | { 195 | nchild = 2*index+1; //左儿子节点序号 196 | if (nchild != nsize-1 && arr[nchild] < arr[nchild+1]) //如果存在右儿子,且左儿子小于右儿子 197 | { 198 | nchild++; //指向两个儿子中比较大的节点 199 | } 200 | if (ntemp < arr[nchild]) //如果要下滤的值小于儿子节点 201 | { 202 | arr[index] = arr[nchild]; //儿子节点上滤 203 | } 204 | else 205 | break; 206 | } 207 | arr[index] = ntemp;//将要下滤的节点放在最终正确的位置上 208 | } -------------------------------------------------------------------------------- /BOOK/assess.md: -------------------------------------------------------------------------------- 1 | # 书评价 2 | ---------- 3 | **评价收集方法** 4 | > 亚马逊,当当网,豆瓣(文艺范) 5 | > 6 | > 当当网 上的评价 比 亚马逊 上的多的多。 7 | 8 | *** 9 | 学习路线: 《算法》【实现】->《算法导论》【证明】->《算法引论》【设计】 10 | 11 | 参考的帖子: 12 | > 13 | http://www.zhihu.com/question/27744730/answer/57823171 14 | 15 | 原因如下,没读过而且打算读的人请仔细看: 16 | 1. CLRS的重点是算法分析,《算法》的重点是算法实现,《算法引论》的重点是算法设计。于是你应该能推测出这三者的难度排序吧,显然《算法》最容易,《算法导论》第二,《算法引论》最难。所以要入门还是从写代码开始比较靠谱,之后再搞证明。 17 | 18 | 2. 你可以用铅笔和草稿纸做习题,也可以用LaTeX(Office Word不能用,因为整本书都要各种证明推理)。但作为CS科班学生,用LaTeX写作业应该是大三以上学生的要求。所以把LaTeX学的顺手些再来用LaTeX做习题比较靠谱。说来惭愧,我LaTeX还在未入门阶段,用这个写习题实在是拙计,经常脑子里证明过程都搞定了,再花N久用LaTeX写出来。显然是没磨刀就砍柴的结果。 19 | 20 | 3. 《算导》的习题必须做,因为实在是很好。能让你把丢了很久的基础数学课捡回来。(就是初高中数学+大一数学课) 21 | 22 | 4. 如果你打算用《算导》来学算法(一般指能写出正确的代码),那你确实选错对象了。看算法模板或者《算法》吧。《算导》上教的算法确实不算多。在上面找不到你想要的东西,你会烦躁和泄气的,既读不进去,也坚持不下去。比如你要看KMP算法的分析,如果你都没用KMP解过题,没动手写过几次正确的KMP代码,又如何能看懂证明过程呢?能写出能AC的代码比能够证明代码正确性和分析效率要容易。所以个人觉得会写代码了再去读《算导》更舒服点。 23 | 24 | 大多数人确实不会证明代码正确性,他们都是brainstorm可能存在的bug,写下代码,然后想到一个修一个。修到AC了为止。 25 | 而不是证明自己的思路不存在bug,然后动手实现。 26 | 27 | ## 《算法》 28 | 29 | **《算法:C语言实现(第1~4部分,基础知识,数据结构,排序及搜索)》【第3版】** 30 | 31 | **《算法:C语言实现(第5部分)图算法》【第3版】** 32 | 33 | **《算法》(用Java实现的)【第4版】** 34 | 35 | **豆瓣书评:** 36 | 37 | ---------- 38 | 39 | > 40 | 和算法导论同样算是经典的书籍,这本书更加偏重于实践,算法导论偏重与理论,有能力的人,两本书结合看。 41 | > 42 | 不过个人推荐先看这本书,这本书更加适合工程实践当中,作者已经将相关的算法用ANSI C实现了。 43 | >    44 | 现在正在看Mark Allen Weiss的《数据结构与算法分析--C语言描述》,打算先把那本书看完之后再来研究这本书。 算法不只是为了找工作的时候可以轻松的找到一份体面的工作,更重要的是学习完算法和数据结构之后,你在编程上的能力就会潜移默化的上升了。 45 | 46 | ---------- 47 | > 48 | 没有全部读完,这本书实在太老了,最大的缺点是代码可用性不高,讲解也非常晦涩,远不如作者的新书algorithms 4th 49 | 50 | ---------- 51 | > 52 | 上一本说第5部分是字符串算法,结果是图算法……也许Sedgewick老爷子自己都对自己的雄心壮志产生怀疑(part 1-4里规划的一共有8个部分,第5部分字符串算法,第6部分几何算法,第7部分图算法,第8部分高级话题(算法设计与分析,NP,线性规划,快速傅立叶变换,等等)),所以先把最紧要的图算法出版了…… 53 | >    54 | 看的英文影印版,网络流那一章没看,因为时间不允许了,也确实很难。即使只看17-21章,也用了两个半月的全部业余时间!翻开22章网络流算法就直接崩溃了,过于抽象,非我能接受。 55 | >    56 | 和上一本(part1-4)相比,part 5依然延续了代码紧凑、令人叫绝的传统,任何一小段代码都力求精简到无可挑剔,实在是太佩服了。小错误多了不少,像“see figure xx”这种就经常把标号写错,不过瑕不掩瑜,仍然是一本值五颗星的好书。同样是讲图算法,此书比算法导论浅显易懂多了,真的,基本上读下来就能明白是怎么回事,看代码一遍加深理解。我手边也有算法导论,翻开一看就是不会念的希腊字母和各种式子,看不下去(高等数学60分,实在是伤不起)。 57 | >    58 | 最后,希望老爷子有精力的话还是把其他几个部分也出版了呗,翘首以待啊! 59 | 60 | 61 | ## 《算法导论》 62 | 63 | 64 | ## 《算法引论》 65 | “和普通的算法书不同,这本书从创造性的角度出发——如果说算法导论讲的是有哪些算法,那么算法引论讲的就是如何创造算法。” 66 | 67 | 68 | 69 | *** 70 | ### 《数据结构与算法分析》 71 | 72 | **《数据结构与算法分析:C++ 描述(第3版)》** 73 | > 74 | >感觉讲 C++ 的地方 比 算法本身还多,各种STL都出现了 75 | 76 | **《数据结构与算法分析:C语言描述》** 77 | > 入门级的好书 78 | 79 | > “学习数据结构,去搞本英文版来吧,中文版网上都在骂翻译太烂” 80 | 81 | *** 82 | 83 | ### 《算法概论(注释版)》 84 | >http://book.douban.com/review/1325850/ 85 | 86 | >对我来说,算法的教与学有两个困难的地方: 87 | 88 | >其一,我们学习了那些经典的算法,除了赞叹一下设计的巧思,但总难免问上一句:怎么想到的?对学生来说,这可能是最费解、也最让人窝火的地方。我们下再多的功夫去记忆书上的算法、去分析这些算法的效率,却终究不能理喻得到这些算法的过程。心理盘算着:给我一个新问题,让我设计个算法出来,我能行吗?答案是:不知道。 89 | 90 | >可这偏偏又是极重要的,无论作研究还是实际工作,一个计算机专业人士最重要的能力,就是解决问题——解决那些不断从理论模型、或者从实际应用中冒出来的新问题。 91 | 92 | >其二,算法作为一门学问,有两条正交的线索。一个是算法处理的对象:数、矩阵、集合、串(strings)、排列(permutations)、图(graphs)、表达式(formula)、分布(distributions),等等。另一个是算法的设计思想:贪婪、分治、动态规划、线性规划、局部搜索(local search),等等。这两条线索几乎是相互独立的:同一个离散对象,例如图,稍有不同的问题,例如single-source shortest path和all-pair shortest path,就可以用到不同的设计思想,如贪婪和动态规划;而完全不同的离散对象上的问题,例如排序和整数乘法,也许就会用到相同的思想,例如分治。 93 | 94 | >两条线索交织在一起,该如何表述。对学生而言,不同离散对象的差别是直观的——我们已经惯于在一章里面完全讲排序、而在另一章里面完全讲图论算法;可是对算法而言,设计思想的差别是根本的,因为这是从问题的结构来的:不同离散对象上面定义的问题,可以展现出类似的结构,而这结构特征,就是支持一类算法的根本,也是我们设计算法的依据。 95 | 96 | >坦率的说,之前还没有哪一本算法书很好的解决这两个困难,就连算法导论在这两个问题上也都做得不好。传统的算法书,大多注重内容(content)的收录,但却忽视思维过程的展示(exposition),因此我们学习了经典的算法,却费解于得到算法的过程;而且算法教材对于内容的编排多是枚举式的(enumerative),这多少是受了the art of computer programming的影响——可那是本工具书而不是教材,因此我们一提到算法课,就想起了排序、哈希、最短路径……这些题目(topics),却没有一个统一的线索在心中。 97 | > 98 | > 这本Algorithms,在短短的篇幅内,做到了。 99 | 100 | *** 101 | 102 | ### 其他书籍 103 | 104 | 《Cracking the Coding Interview》 《程序员面试金典》 105 | > 106 | 107 | 《剑指offer》 108 | 109 | 《编程之美——微软技术面试心得》 110 | 111 | 《编程珠玑》 112 | 113 | 《编程珠玑(续)》 114 | 115 | 《挑战程序设计竞赛(第2版)》 116 | 117 | 《算法设计与分析基础》 118 | 119 | 《计算机算法的设计与分析(英文版)》 DACA 120 | >我一直认为搞算法应该看三本书,但是如果一个人把这三本书都花时间去钻研,那要么就是对算法极有天赋以及狂热的学者,要么就是附庸风雅的俗人。就如同当年胸口别四只钢笔的显摆人士,不足以模仿之。 121 | 这三本书中有两本可说是如雷贯耳,TAOCP和算法导论,而这本DACA却鲜有人问津。这恐怕得益于TAOCP的大牛作者Knuth,以及MIT OCW对算法导论的推广。相比之下,DACA名气就小很多了。尽管如此,DACA的编者阵容仍可说是十分豪华:大名鼎鼎的Aho和Ullman,以及86年图灵奖获得者Hopcroft。 122 | 123 | >由此可以看到,TAOCP的内容最底层,但难度也是最高的;算法导论居中;DACA就比较适合对算法要求相对要“浅”一些的人,当然,这种所谓的“浅”,并不是指浅薄,仅是避免过于深入的钻研而已。本质上来说,这种“浅”对大多数人来说,已经足够深入了...... 124 | 125 | ### 国内教材 126 | 127 | **《大话数据结构》** 128 | > 小白的入门级书,看完这本后,有个大概印象了,可以去看上面的国外三部巨头了。 129 | 130 | 《算法竞赛入门经典》 131 | 132 | 《数据结构-严蔚敏》 133 | >首先我想知道你用的哪本数据结构的教科书?每本书的算法精髓是大差不差的,但是具体代码就千差万别了。 134 | 是清华大学严蔚敏的么?如果是,重在理解,不要太在意规范化代码,最好能自己找些例子实践一下。 135 | 136 | >国内计算机教辅没有所谓“教科书级”的的书,不要纠结于教材书籍,随便找一本数据结构的本科教辅,重算法,轻代码,重理解,忌死背。 137 | 138 | 139 | 140 | ### 其他人推荐的 141 | 142 | **STL** 143 | 144 | > 入门版:任何一本权威的数据结构书 - 把所有数据结构用你所使用的语言写一遍,编译通过. 145 | 146 | > 进阶版:《STL源码剖析》 147 | 148 | > 辅助:可以参照相关语言的内部原理来学习 149 | 150 | 151 | 152 | **我的算法学习之路** 153 | > 这人NB大法了 154 | http://zh.lucida.me/blog/on-learning-algorithms/ 155 | 156 | > 作者看书的顺序: 157 | > 158 | 《数据结构与算法分析:C语言描述》 159 | 160 | > 161 | 《微软的梦工厂》 162 | > 163 | 《算法设计与分析基础》 164 | > 165 | 166 | > 167 | 《编程原本》 == 《Elements of Programming》 168 | “强大的数学能力” 169 | > 170 | 《C语言接口与实现:创建可重用软件的技术》 171 | “提升自己编写C代码的能力” 172 | > 173 | 《算法》【第4版】“提高自己的算法能力” 174 | 175 | 176 | *** 177 | 178 | > 学习算法导论 时可以看的视屏: 179 | http://mooc.nthu.edu.tw/sharecourse/course/view/courseInfo/50 180 | 181 | 182 | **google面试官** 183 | >我多次在google面试或者毕业招聘的时候看到这样的情形:学习数据结构和算法--CS课程里面几乎最重要的课程--的方式很不科学!! 184 | 到不是说大家用的书或者老师用的材料不对,而是说学生们对于这些课程本身的理解非常缺乏. 185 | 186 | >打好数据结构和算法基础的关键并不在于对于所有数据结构的细致的了解,不是记住每一个大O值或者摊余成本..((@_@;)? [不懂]). 187 | 如果这些知识你都掌握了,当然很棒并且可以给人留下很深的印象,但是你基本上用不着啊! 188 | 你的职业生涯中或许永远都不会要求你实现一个红黑树删除节点的算法.但是!你必须有能力而且手起刀落轻轻松松的识别出什么时候使用二叉树更简单更有效, 因为你十分需要这样的技巧. 189 | 190 | >所以,不要试图记住所有的东西.而是从基础开始,做两件事: 191 | 192 | 第一件事. 把 **数据结构图形化** ,视觉化.(突然想起来我高中竞赛老师说的一句话:数形结合千般好,一旦不做万事休啊! 就是要画图! )在直觉上感受一个数据结构是什么样子的.使用它是什么感觉,抽象上和具体实现上是什么样子的.这就是最重要的事情.并且无论是对于简单的队列,栈还是天杀的平衡树都很重要而且有效.把数据结构画出来,在你的脑袋瓜里面就能想象出来,总之,你需要做的就是,直观的去了解这些数据结构. 193 | 194 | 第二件事.学习 **什么时候用什么样的数据结构和算法** .对于学生来说这很难,而且你要做作业的时候老师也没告诉你们这该怎么办.╮( ̄▽ ̄")╭ 不过没关系. 你要认识到当你真正处理到现实问题的时候或许你才能掌握某些数据结构,比如哈希表.但是即使是个学生,你也应该知道数据结构的实用性:什么时候你需要个哈希表,什么时候你需要个树,什么时候你需要个堆? 而不是一开始就陷入到追求细节中去. 195 | 196 | >我在google面试的时候,我经常会问一个可以由二叉树搜索解决的问题. 好的应聘者可以几分钟内就可以想到用二叉树来解决,而且对于我的其他问题也差不多10-15分钟就可以解决.当然,偶尔会有一个应聘者,他能直观的认识树这种结构,而且可以把我的问题形象化,图形化的描述出来.当然他或许对于某些操作的时间复杂度不甚了解,但是对于问题他却可以立马回应,因为他们脑袋里就有这样的树结构啊~所以他也能拿到工作啊. 197 | 198 | >至于书嘛,只推荐一本--- <算法导论> 199 | 200 | >如果你想要一本有很多例子并且和语言相关的书的哈u,我就推荐 当然我还是更推荐<算法导论>,不过这些也是很好的教辅书啦~ 201 | 202 | -------------------------------------------------------------------------------- /排序算法/04-希尔排序.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 排序算法-02 4 | /* 5 | 前面讲了冒泡排序和插入排序,今天讲的排序算法是shell排序-希尔排序 6 | 7 | 历史背景: 8 | 优秀排序算法的首要条件就是速度(还有其他要求,速度是第一位的)。于是人们想了许许多多的办法,目的就是为了提高排序的速度。 9 | 而在很长的时间里,众人发现尽管各种排序算法花样繁多(比如前面我们提到的三种不同的排序算法),但时间复杂度都是O(n2), 10 | 似乎没法超越了(这里排序指内排序)。此时,计算机学术界充斥着“排序算法不可能突破O(n^2)”的声音,“不可能”成了主流。 11 | 12 | 终于有一天,当一位科学家发布超越了O(n^2)新排序算法后,紧接着就出现了好几种可以超越O(n^2)的排序算法, 13 | 并把内排序算法的时间复杂度提升到了O(nlog2n)。“不可能超越O(n^2)”彻底成为了历史。 14 | 15 | 现在,我要讲解的算法叫希尔排序(Shell Sort)。希尔排序是D.L.Shell于1959年提出来的一种排序算法, 16 | 在这之前排序算法的时间复杂度基本都是O(n^2)的,希尔排序算法是突破这个时间复杂度的第一批算法之一。 17 | */ 18 | 19 | /* 20 | 思路起源: 21 | 我们前一节讲的直接插入排序,应该说,它的效率在某些时候是很高的,比如,我们的记录本身就是基本有序的,我们只需要少量的插入操作, 22 | 就可以完成整个记录集的排序工作,此时直接插入很高效。还有就是记录数比较少时,直接插入的优势也比较明显。 23 | 可问题在于,两个条件本身就过于苛刻,现实中记录少或者基本有序都属于特殊情况。 24 | 25 | 于是科学家希尔研究出了一种排序,对直接插入排序改进后可以增加效率的方法。 26 | 如何让待排序的记录个数较少呢?很容易想到的就是将原本有大量记录数的记录进行分组。分割成若干个子序列,此时每个子序列待排序的记录个数就比较少了。 27 | 然后在这些子序列内分别进行直接插入排序,当整个序列都基本有序时,注意只是基本有序时,再对全体记录进行一次直接插入排序。 28 | 29 | 此时一定有同学开始疑惑了。这不对呀,比如我们现在有序列是{9,1,5,8,3,7,4,6,2},现在将它分成三组,{9,1,5},{8,3,7},{4,6,2}, 30 | 哪怕将它们各自排序排好了,变成{1,5,9},{3,7,8},{2,4,6},再合并它们成{1,5,9,3,7,8,2,4,6},此时,这个序列还是杂乱无序,谈不上基本有序, 31 | 要排序还是重来一遍直接插入有序,这样做有用吗? 32 | 33 | 需要强调一下,所谓的基本有序,就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间,像{2,1,3,6,4,7,5,8,9}这样可以称为基本有序了。 34 | 但像{1,5,9,3,7,8,2,4,6}这样的9在第三位,2在倒数第三位就谈不上基本有序。 35 | 36 | 问题其实也就在这里,我们分割待排序记录的目的是减少待排序记录的个数,并使整个序列向基本有序发展。 37 | 而如上面这样分完组后,就各自排序的方法达不到我们的要求。因此,我们需要采取跳跃分割的策略: 38 | 【将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。】 39 | */ 40 | 41 | 42 | // __________________________1 希尔排序-shell排序 __________________________ 43 | /* 44 | shell排序命名来自于Donald Shell,该算法被证明为有亚二次时间界。是插入排序的一种改进,舍弃了插入排序逐一比较的不足之处, 45 | 而是设定一个增量,间隔比较。然后逐渐缩小这个增量,最终达到排序目的,所以shell排序也叫缩减增量排序。 46 | 47 | 首先,需要设定一组增量序列,h1,h2,...hk。然后先从h1开始,以此增量为步长,将全部序列分成若干组,每一组进行插入排序。 48 | 接着增量变为h2,继续分组,然后再插入排序,这样直到最后。不同的增量序列带来的计算量不一样, 49 | 本文以shell建议的增量序列为例进行算法说明,即h1=N/2,hk=hk-1/2。下面是具体步骤: 50 | */ 51 | 52 | void ShellSort(int arr[], int nlen) 53 | { 54 | int i,j; 55 | int ntemp; 56 | int ngap; 57 | for(ngap=nlen/3 + 1; ngap>0; ngap=ngap/3+1)//shell增量序列 58 | { 59 | for(i=ngap; i=ngap && arr[j-ngap]>ntemp; j-=ngap)//对当前位置i的元素,将其插入到arri-hi,arri-2hi,...序列中。 63 | { 64 | arr[j] = arr[j-ngap];//这个跟插入排序的过程是一样的 65 | } 66 | arr[j] = ntemp; 67 | 68 | /******for test*********/ 69 | cout<<"gap="<>的排序算法里面有介绍。 171 | */ 172 | 173 | -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/Classes/01-SimpleGraphics.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | #include 10 | #include 11 | 12 | /* 下面 学习到内容 13 | 14 | POINTS 15 | LINES 16 | TRIANGLES 17 | Rect 18 | POLYGON 19 | 20 | */ 21 | 22 | #define VF std::vector 23 | #define VVF std::vector 24 | 25 | //========================================================================= 26 | // 27 | // 点的绘制 28 | // 29 | //========================================================================= 30 | void myDisplay_POINTS(void) 31 | { 32 | glClear(GL_COLOR_BUFFER_BIT); 33 | glColor3f(1.0, 0.0, 0.0); 34 | glPointSize(5.0f); 35 | glBegin(GL_POINTS); 36 | glVertex2f(-0.5,-0.5); // 点P1 37 | glVertex2f(-0.5,0.5); // 点P2 38 | glVertex2f(0.5,0.5); // 点P3 39 | glVertex2f(0.5,-0.5); // 点P4 40 | glEnd(); 41 | glFlush(); 42 | } 43 | 44 | /* 45 | 代码解释 46 | 47 | 名字以glVertex开头的一系列函数用于指定顶点坐标,名字里的2表示2维即两个坐标(x和y),f表示坐标的类型是float。 48 | 49 | 这一系列函数包括: 50 | glVertex2d 51 | glVertex2f 52 | glVertex3f 53 | glVertex3fv 54 | 等等。 55 | 数字表示参数的个数,字母表示参数的类型,具体含义是 56 | 57 | s表示16位整数(OpenGL中将这个类型定义为GLshort), 58 | i表示32位整数(OpenGL中将这个类型定义为GLint和GLsizei), 59 | f表示32位浮点数(OpenGL中将这个类型定义为GLfloat和GLclampf), 60 | d表示64位浮点数(OpenGL中将这个类型定义为GLdouble和GLclampd)。 61 | v表示传递的几个参数将使用指针的方式,见下面的例子。 62 | */ 63 | 64 | void myDisplay_POINTS_byOthers(void) 65 | { 66 | glVertex2i(1, 3); 67 | glVertex2f(1.0f, 3.0f); 68 | glVertex3f(1.0f, 3.0f, 0.0f); 69 | glVertex4f(1.0f, 3.0f, 0.0f, 1.0f); 70 | 71 | GLfloat VertexArr3[] = {1.0f, 3.0f, 0.0f}; 72 | glVertex3fv(VertexArr3); 73 | } 74 | 75 | /* 76 | glVertex2f(-0.5, 0.5) ; 77 | 这一句的意思就是给出一个点,其x坐标和y坐标分别为-0.5和0.5。在本节的例子中, 78 | 你可以认为坐标原点在窗口的中心,x轴向右为正, y轴向上为正。窗口绘图区的高度度和宽度都是1。 79 | */ 80 | 81 | //========================================================================= 82 | // 83 | // 线段的绘制 84 | // 85 | //========================================================================= 86 | void myDisplay_LINES(void) // 画个矩形 87 | { 88 | glClear(GL_COLOR_BUFFER_BIT); 89 | 90 | glBegin(GL_LINES); 91 | glVertex2f(-0.5,-0.5); // 点P1 92 | glVertex2f(-0.5,0.5); // 点P2 93 | 94 | glVertex2f(0.5,0.5); // 点P3 95 | glVertex2f(0.5,-0.5); // 点P4 96 | 97 | glVertex2f(-0.5,0.5); // 点P2 98 | glVertex2f(0.5,0.5); // 点P3 99 | 100 | glVertex2f(-0.5,-0.5); // 点P1 101 | glVertex2f(0.5,-0.5); // 点P4 102 | 103 | glEnd(); 104 | glFlush(); 105 | } 106 | 107 | 108 | 109 | /* 线段的绘制 110 | glBegin函数的参数是告诉程序对后面给出的点集进行什么操作。这个参数的常见取值有: 111 | (1) GL_POINTS:画出单个的点,点之间没有任何连线。 112 | (2) GL_LINES:两个点一组,每组之间一条线段。 113 | (3) GL_LINE_STRIP:用给出的点画出一条连续折线,除最后一个点外。每一个点都是下一段的起点。 114 | (4) GL_LINE_LOOP:用给出的点画出一个闭合折线。也就是在GL_LINE_STRIP的基础上再把最后一个点和第一个点连起来。 115 | */ 116 | 117 | void myDisplay_LINE_STRIP(void) // 画个矩形 118 | { 119 | glClear(GL_COLOR_BUFFER_BIT); 120 | 121 | glBegin(GL_LINE_STRIP); 122 | glVertex2f(-0.5,-0.5); // 点P1 123 | glVertex2f(-0.5,0.5); // 点P2 124 | glVertex2f(0.5,0.5); // 点P3 125 | glVertex2f(0.5,-0.5); // 点P4 126 | glVertex2f(-0.5,-0.5); 127 | 128 | glEnd(); 129 | glFlush(); 130 | } 131 | 132 | void myDisplay_LINE_LOOP(void) // 画个矩形 133 | { 134 | glClear(GL_COLOR_BUFFER_BIT); 135 | 136 | glBegin(GL_LINE_LOOP); 137 | glVertex2f(-0.5,-0.5); // 点P1 138 | glVertex2f(-0.5,0.5); // 点P2 139 | glVertex2f(0.5,0.5); // 点P3 140 | glVertex2f(0.5,-0.5); // 点P4 141 | 142 | glEnd(); 143 | glFlush(); 144 | } 145 | 146 | //========================================================================= 147 | // 148 | // 绘制凸多边形 149 | // 150 | //========================================================================= 151 | 152 | /* 153 | glBegin函数还提供了以下几个参数来画出凸多边形。所谓凸多边形,是指它要满足如下性质: 154 | 把该多边形任意一边向两方无限延长成为一条直线后,其他各边均在此直线的同侧。 155 | 156 | (1)GL_POLYGON,这个参数用后面的顶点列表画出一个凸多边形。 157 | 158 | 默认情况下,opengl在画多边形时,会自动用指定颜色(默认为白色)填充其内部。 159 | 如果想只显示多边形的边线而不填充其内部,可以在glBegin函数之前加上一句 160 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 这个语句的作用告诉opengl只画出边框,但不填充内部。 161 | */ 162 | void myDisplay_GL_POLYGON(void) // 画个矩形 163 | { 164 | glClear(GL_COLOR_BUFFER_BIT); 165 | 166 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //glPolygonMode()指定了如何绘制面的方式,GL_LINE为只画线,GL_FILL则是默认的填充。 167 | 168 | glBegin(GL_POLYGON); 169 | glVertex2f(-0.5,-0.5); // 点P1 170 | glVertex2f(-0.5,0.5); // 点P2 171 | glVertex2f(0.5,0.5); // 点P3 172 | glVertex2f(0.5,-0.5); // 点P4 173 | glVertex2f(0.9,-0.8); // 点P5 174 | 175 | glEnd(); 176 | glFlush(); 177 | } 178 | 179 | /* 180 | (2)GL_TRIANGLES ,将每3个顶点为一组画出一个三角形,如果剩余的顶点个数不足3个则忽略。 181 | 运行效果如下所示: 182 | */ 183 | 184 | void myDisplay_GL_TRIANGLES(void) // 画三角形, 185 | { 186 | glClear(GL_COLOR_BUFFER_BIT); 187 | 188 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 189 | 190 | glBegin(GL_TRIANGLES ); 191 | glVertex2f(-0.5,-0.5); // 点P1 192 | glVertex2f(-0.5,0.5); // 点P2 193 | glVertex2f(0.5,0.5); // 点P3 194 | 195 | glVertex2f(0.5,-0.5); // 点P4 196 | glVertex2f(0.5,-0.8); // 点P4 197 | glVertex2f(0.7,-0.9); // 点P4 198 | 199 | glEnd(); 200 | glFlush(); 201 | } 202 | 203 | /* 204 | (3)GL_TRIANGLES_STRIP 于GL_LINE_STRIP类似,画出顶点列表中所有由【任意连续三点】所组成的三角形。 205 | */ 206 | void myDisplay_GL_TRIANGLE_STRIP(void) // 画三角形, 207 | { 208 | glClear(GL_COLOR_BUFFER_BIT); 209 | 210 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 211 | 212 | glBegin(GL_TRIANGLE_STRIP); 213 | glVertex2f(-0.5,-0.5); // 点P1 214 | glVertex2f(-0.5,0.5); // 点P2 215 | glVertex2f(0.5,0.5); // 点P3 216 | 217 | glVertex2f(0.5,-0.5); // 点P4 218 | glVertex2f(0.9,-0.8); // 点P4 219 | 220 | glEnd(); 221 | glFlush(); 222 | } 223 | 224 | /* 225 | (4)GL_TRIANGLE_FAN 所有三角形都以第一点为顶点,另外两个顶点为连续顶点。 226 | */ 227 | void myDisplay_GL_TRIANGLE_FAN(void) // 画三角形, 228 | { 229 | glClear(GL_COLOR_BUFFER_BIT); 230 | 231 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 232 | 233 | glBegin(GL_TRIANGLE_FAN); 234 | glVertex2f(-0.5,-0.5); // 点P1 235 | glVertex2f(-0.5,0.5); // 点P2 236 | glVertex2f(0.5,0.5); // 点P3 237 | 238 | glVertex2f(0.5,-0.5); // 点P4 239 | glVertex2f(0.9,-0.8); // 点P4 240 | glVertex2f(0.6,-0.7); // 点P4 241 | 242 | glEnd(); 243 | glFlush(); 244 | } 245 | 246 | /* 247 | (5) GL_QUADS:每4个顶点为一组画出一个四边形。如果剩余点数不足4个,则忽略。 248 | */ 249 | 250 | /* 251 | (6) GL_QUAD_STRIP:每两个顶点为一组,任意连续的两组画出一个四边形。如果有8个点为P1到P8,则最后画出3个4边形 252 | 253 | P1P2P3P4, 254 | 255 | P3P4P5P6, 256 | 257 | P5P6P7P8。 258 | */ 259 | 260 | 261 | // 画矩形 262 | /* 263 | opengl还提供了一个直接画矩形的函数系列 264 | 265 | glRect[sifd],中括号里的字符代表参数类型 266 | 267 | s:short 短整型 268 | 269 | i:integet 整型 270 | 271 | f:float浮点型 272 | 273 | d:double 双精度型。 274 | 275 | 例如下面的代码画出一个矩形框,左下角为(-0.5,-0.5),右上角为(0.5, 0.5) 276 | */ 277 | void myDisplay_Rect(void) 278 | { 279 | glClear(GL_COLOR_BUFFER_BIT); //清除颜色 280 | glRectf(-0.9f,-0.9f,0.5f,0.5f); //画一个矩形[左下角 + 右上角] 281 | glFlush(); //让前面的命令立即执行而不是在缓冲区,与fflush(stdout)作用类似 282 | } 283 | -------------------------------------------------------------------------------- /排序算法/01-冒泡排序.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 01-冒泡排序 4 | /* 5 | 冒泡排序(Bubble Sort)是一种【交换排序】,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。 6 | 冒泡的实现在细节上可以有很多种变化。下面我们将实现3种不同的冒泡实现代码,来讲解冒泡排序的思想。这里,我们就先来看看比较容易理解的一段。 7 | */ 8 | 9 | // __________________________0 基础函数 __________________________ 10 | // 【交换函数】-C语言版:利用指针来交换值。 11 | void swip(int * i, int * j) 12 | { 13 | int temp = *i; 14 | *i = *j; 15 | *j = tmep; 16 | } 17 | swap(&a,&b); 18 | 19 | // 【交换函数】-C++ 版:利用引用来交换值。优点:效率高,代码易读。 20 | void swip(int &i, int &j) 21 | { 22 | int temp = i; 23 | i = j; 24 | j = tmep; 25 | } 26 | swap(a,b); 27 | 28 | // 【交换函数】-C++ 版-模板:可以针对不同类型,不局限于 int 类型。 29 | template 30 | void swip(type &a, type &b) 31 | { 32 | type temp = a; 33 | a = b; 34 | b = tmep; 35 | } 36 | swap(a,b); 37 | 38 | 39 | 40 | 41 | // 判断函数-用来决定是否需要交换的【比较函数】 42 | enum COMPARE{ 43 | COMPARE_E_BIG_TO_SMALL = 0, // 把数组按从大到小排列 44 | COMPARE_E_SMALL_TO_BIG = 1, // 把数组按从小到大排列 45 | } 46 | 47 | template 48 | int compare(const type &v1, const type &v2) // 比较函数,不需要修改值,所以加上 const 49 | { 50 | if(v1>v2) return COMPARE_E_SMALL_TO_BIG; 51 | if(v1i;j--) /* 注意j是从后往前循环 */ 93 | { 94 | if( array[j]i; j--) 129 | { 130 | /***满足条件,交换元素***/ 131 | if(array[j] i && j < length; j += add) 188 | { 189 | /***满足条件,交换元素***/ 190 | if(array[j] left; --j ) 227 | { 228 | if(array[j-1] > array[j]) 229 | { 230 | swap(array[j-1], array[j]); 231 | } 232 | } 233 | ++left; 234 | } 235 | } 236 | 237 | 238 | // __________________________ 奇偶排序 __________________________ 239 | /* 240 | 在并行计算排序中,每个处理器对应处理一个值,并仅有与左右邻居的本地互连。所有处理器可同时与邻居进行比较、交换操作,交替以奇-偶、偶-奇的顺序。 241 | 该算法由Habermann在1972年最初发表并展现了在并行处理上的效率。 242 | */ 243 | 244 | void cd_sort1(int array[], int length) 245 | { 246 | bool bok = false; 247 | while( bok == false ) 248 | { 249 | bok = true; 250 | for(i = 0; i 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {7F06B8D0-01B3-4040-8090-1C8A33B952EA} 23 | Win32Proj 24 | text1 25 | 8.1 26 | 27 | 28 | 29 | Application 30 | true 31 | v140 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v140 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v140 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v140 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | 78 | 79 | false 80 | 81 | 82 | false 83 | 84 | 85 | 86 | Use 87 | Level3 88 | Disabled 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Use 100 | Level3 101 | Disabled 102 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 103 | true 104 | 105 | 106 | Console 107 | true 108 | 109 | 110 | 111 | 112 | Level3 113 | Use 114 | MaxSpeed 115 | true 116 | true 117 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | true 124 | true 125 | 126 | 127 | 128 | 129 | Level3 130 | Use 131 | MaxSpeed 132 | true 133 | true 134 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 135 | true 136 | 137 | 138 | Console 139 | true 140 | true 141 | true 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | Create 154 | Create 155 | Create 156 | Create 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/Template/Template.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {F9F262F7-BF3D-4433-B6E4-4727F7D03BA1} 23 | 8.1 24 | Win32Proj 25 | ConsoleApplication1 26 | Template 27 | 28 | 29 | 30 | Application 31 | true 32 | v140 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v140 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v140 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v140 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | F:\回退位置-VS存储sdf和ipch的临时文件【需要定期清理】\输出目录\$(ProjectName)\$(Configuration)\ 75 | F:\回退位置-VS存储sdf和ipch的临时文件【需要定期清理】\输出目录\$(ProjectName)\$(ProjectName)\$(Configuration)\ 76 | true 77 | 78 | 79 | true 80 | 81 | 82 | false 83 | 84 | 85 | false 86 | 87 | 88 | 89 | Use 90 | Level3 91 | Disabled 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | Use 103 | Level3 104 | Disabled 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | Use 117 | MaxSpeed 118 | true 119 | true 120 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 121 | true 122 | 123 | 124 | Console 125 | true 126 | true 127 | true 128 | 129 | 130 | 131 | 132 | Level3 133 | Use 134 | MaxSpeed 135 | true 136 | true 137 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 138 | true 139 | 140 | 141 | Console 142 | true 143 | true 144 | true 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | Create 154 | Create 155 | Create 156 | Create 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /Computational Geometry/[Code( )]ConvexHull/ConvexHullStudy/proj.win32/ConvexHullStudy.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | NotUsing 24 | 25 | 26 | Create 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {7BAD2AD7-B32F-4FF4-B2DE-5B3258683592} 39 | 8.1 40 | Win32Proj 41 | ConvexHullStudy 42 | 43 | 44 | 45 | Application 46 | true 47 | v140 48 | Unicode 49 | 50 | 51 | Application 52 | false 53 | v140 54 | true 55 | Unicode 56 | 57 | 58 | Application 59 | true 60 | v140 61 | Unicode 62 | 63 | 64 | Application 65 | false 66 | v140 67 | true 68 | Unicode 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | F:\回退位置-VS存储sdf和ipch的临时文件【需要定期清理】\输出目录\$(ProjectName)\$(Configuration)\ 90 | F:\回退位置-VS存储sdf和ipch的临时文件【需要定期清理】\输出目录\$(ProjectName)\$(ProjectName)\$(Configuration)\ 91 | true 92 | 93 | 94 | true 95 | 96 | 97 | false 98 | 99 | 100 | false 101 | 102 | 103 | 104 | Use 105 | Level3 106 | Disabled 107 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | $(ProjectDir)../Classes;$(ProjectDir)proj.win32;%(AdditionalIncludeDirectories) 110 | 111 | 112 | Console 113 | true 114 | 115 | 116 | 117 | 118 | Use 119 | Level3 120 | Disabled 121 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | 128 | 129 | 130 | 131 | Level3 132 | Use 133 | MaxSpeed 134 | true 135 | true 136 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 137 | true 138 | 139 | 140 | Console 141 | true 142 | true 143 | true 144 | 145 | 146 | 147 | 148 | Level3 149 | Use 150 | MaxSpeed 151 | true 152 | true 153 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 154 | true 155 | 156 | 157 | Console 158 | true 159 | true 160 | true 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /BOOK/《算法竞赛入门经典》/Chapter06-栈和队列.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | //#include "Chapter06.h" 4 | 5 | 6 | // 第06章 栈和队列 7 | /* 8 | 线性结构看似简单,却是重要的算法和数据结构的基础。例如,环状结构也经常转化为线性结构————只需要从某个元素处把环切断,就变成了链。 9 | */ 10 | 11 | // __________________________6.1 栈和队列__________________________ 12 | // 6.1.1 卡片游戏 13 | /* 14 | 桌上有一叠牌,从第一张牌开始从上往下依次编号为 1~n.当至少还剩两张牌时进行以下操作: 15 | 把第一张牌扔掉,然后把新的第一张放到整叠牌的最后。 16 | 输入n,输出每次扔掉的牌,以及最后剩下的牌。 17 | 样例输入:7 18 | 样例输出:1 3 5 7 4 2 6 19 | */ 20 | 21 | /* 22 | 这些牌像不像是在排队?每次从排头拿到两个,其中第二个再次排到尾部。我们把这种数据结构称为队列(queue). 23 | 或者说得更加学术一点:FIFO表。其中FIFO表示先进先出(First In,First Out),符合我们在日常生活中的排队。 24 | 用一个数组 queue 来实现这个队列,再设两个指针 front 和 rear . 25 | */ 26 | 27 | const int MAXN = 50; 28 | int queue[MAXN]; 29 | 30 | int max_num; 31 | 32 | cin>>max_num; 33 | 34 | for (int i = 0; i 53 | using namespace std; 54 | */ 55 | 56 | queue q; 57 | 58 | int max_num; 59 | cin>>max_num; 60 | 61 | for (int i = 0; iC 和 C->B 83 | */ 84 | 85 | /* 86 | 分析:在中转站C中,车厢符合后进先出的原则,称为LIFO表。其中 LIFO代表 Last In First Out 87 | 它还有一个更常用的名称————栈。由于只有一端生长,实现栈时只需要一个数组stack和栈顶指针(始终指向栈顶元素)。 88 | 89 | 没看懂这到题的样例输出,好奇怪。反正能理解 栈 就可以了。 90 | */ 91 | 92 | 93 | 94 | 95 | // __________________________6.2 链表__________________________ 96 | /* 97 | 在多数情况下,线性表都可以用数组轻松实现,但事情并不总是这么简单。本节介绍链表,并用它和数组进行比较。 98 | */ 99 | 100 | 101 | // 6.2.1 初步分析 102 | /* 103 | 例题:6-1 移动小球 【P109/241】 104 | 你有一些小球,从左到右依次编号为1,2,3,...,n, 如下图所示 105 | 106 | 然后题主 用 数组实现了一遍 这到题的大概,最后得出结论,如果是数组的话, 107 | 把元素从最左边移动到最右边,则中间的所有元素都会全部移动一下,非常浪费时间。 108 | */ 109 | 110 | 111 | // 6.2.2 链式结构 112 | /* 113 | 第2种方法是强调小球之间的相对顺序,而非绝对顺序。我们用left[i]和right[i]分别表示 114 | 编号为 i 的小球左边和右边的小球编号(如果是0,表示不存在)。则移动过程可以分成两个步骤: 115 | 把X移出序列;把X重新插入序列。 116 | */ 117 | 118 | 119 | 120 | // 6.2.3 对比测试 121 | /* 122 | 如何进行对比测试呢?首先需要数据,而且是大量数据。为此,需要编写数据生成器。 123 | */ 124 | 125 | #include // rand()和srand()需要 126 | #include // time()需要 127 | srand(time(NULL)); // 初始化随机数种子,只初始化一次 128 | 129 | int random = rand()%2; 130 | /* 131 | 核心函数是 stdlib.h 中的 rand(),它生成一个闭区间[0,RAND_MAX]的均匀随机整数 132 | (均匀的含义是:该区间内每个整数被产生的概率相同) 133 | */ 134 | 135 | 136 | // 6.2.4 随机数发生器 137 | /* 138 | 139 | */ 140 | 141 | // __________________________6.3 二叉树__________________________ 142 | /* 143 | 二叉树 的递归定义如下:二叉树要么为空,要么由根结点(root),左子树(left subtree) 和 右子树(right subtree)组成, 144 | 而左子树和右子树分别是一棵二叉树。 145 | 注意,在计算机中,树一般是“倒置”的————根在上,叶子在下。 146 | */ 147 | 148 | // 6.3.1 小球下落[P114/241] 149 | /* 150 | 有一棵二叉树,最大深度为D,且所有叶子的深度都相同。所有结点从上到下从左到右编号为1,2,3。。。 151 | 在结点1处放一个小球,它会往下落,每个内结点上都有一个开关,初始全部关闭,当每次有小球落到一个开关时,它的状态都会改变。 152 | 当小球到达一个内结点时,如果该结点上的开关关闭,则往左走,否则往右走,直到走到叶子结点。 153 | 154 | 一些小球从结点1处依次开始下落,最后一个小球将会落到哪里呢?输入叶子深度D和小球个数I,输出第I个小球最后所在的叶子编号。 155 | 假设I不超过整棵树的叶子个数。 156 | D<=20; 157 | I<=20^2-1; 158 | */ 159 | 160 | /* 161 | 分析,不难发现:对于一个结点k,它的左儿子,右儿子的编号分别是2k和2k+1。 162 | 163 | 题中题-01:证明上面这个论点: 对于一个结点k,它的左儿子,右儿子的编号分别是2k和2k+1。 164 | 提示-01 先证明出最左边的那个节点,这样这个节点的右边所有节点都可以不用证明了,显而易见的规律。 165 | 提示-02 观察最左边的节点的规律,可以发现这个现象:最左节点的编号都是 当前行 节点的个数。 166 | 提示-03 我们现在需要证明的是:最左节点的编号都是 当前行 节点的个数。 167 | 提示-04 最左边节点的编号 = 前面 所有节点数总和 + 1 168 | 提示-05 2^2 = 2^(1) + 2^(0) + 1; 169 | 2^3 = 2^2 + 2^(1) + 2^(0) + 1; 170 | 2^n = 2^(n-1) + 2^(n-2) + 2^(n-3) + ... + 2^(0) + 1; 171 | 提示-06 然后上面这个等式 很容易 证明的吧。 172 | */ 173 | 174 | int max_num,deph; 175 | 176 | const long MAXN = 6000; 177 | int tree[MAXN]; 178 | 179 | enum{ 180 | close, 181 | open 182 | }; 183 | 184 | int whcihNode,nextNode; 185 | 186 | while (true) 187 | { 188 | cin>>deph; 189 | cin>>max_num; 190 | 191 | // 初始化小球开关 192 | for ( int i = 0; i>deph; 241 | cin>>max_num; 242 | 243 | lastMaxNum = max_num - 1; 244 | lastStage = close; 245 | nextNode = 1; 246 | for (int i = 0;ihave_value = 0; 297 | u->left = u->right = NULL; 298 | } 299 | return u; 300 | }; 301 | 302 | // 在 read_input 中调用的 addnode 函数。它按照移动序列行走,目标不存在时调用 newnode 来创建新节点。 303 | void addnode(int v,char* s) 304 | { 305 | int n = strlen(s); 306 | Node* u = root; 307 | for (int i = 0;ileft == NULL)u->left = newnode(); // 节点不存在,建立新节点 312 | u = u->left; 313 | }else if ( s[i] == 'R' ) 314 | { 315 | if (u->left == NULL)u->right = newnode(); // 节点不存在,建立新节点 316 | u = u->right; 317 | } 318 | } 319 | if (u->have_value)FAILED = 1; 320 | u->v = v; 321 | u->have_value = 1; 322 | } 323 | 324 | // 按照层次顺序遍历这棵树。我们使用一个序列来完成这个任务,初始时只有一个根节点,然后每次取出一个节点,就把它的左右儿子(如果有)放进队列。 325 | int n = 0,ans[MAXN]; 326 | queue q; 327 | int bfs() 328 | { 329 | q.push(root) 330 | while ( !q.empty() ) 331 | { 332 | Node* u = q.front(); 333 | if (!u->have_value)return 0; 334 | 335 | ans[n++] = u->v; 336 | if(u->left != NULL)q.push(u->left); 337 | if(u->right!= NULL)q.push(u->right); 338 | q.pop(); 339 | } 340 | return 1; 341 | } 342 | /* 343 | 这样遍历二叉树的方法称为宽度优先遍历(Breadth-First Search,BFS)。 344 | 后面我们将看到,BFS在显示图和隐式图算法中扮演着重要的角色。 345 | */ 346 | 347 | // 释放一棵二叉树的代码,以免内存泄露。 348 | void remove_tree(Node* u) 349 | { 350 | if ( u == NULL )return; 351 | remove_tree(u->left); 352 | remove_tree(u->right); 353 | free(u); 354 | } 355 | 356 | 357 | 358 | // 6.3.3 二叉树重建[p105(120/241)] 359 | /* 360 | 对于二叉树T,可以递归定义它的前序遍历,中序遍历 和 后序遍历 如下: 361 | 362 | 首先,我们看看 前序,中序,后序遍历的特性: 363 | 前序遍历: 364 | 1.访问根节点 365 | 2.前序遍历左子树 366 | 3.前序遍历右子树 367 | 中序遍历: 368 | 1.中序遍历左子树 369 | 2.访问根节点 370 | 3.中序遍历右子树 371 | 后序遍历: 372 | 1.后序遍历左子树 373 | 2.后序遍历右子树 374 | 3.访问根节点 375 | */ 376 | 377 | /* 378 | 输入一棵二叉树的前序遍历和中序遍历序列,输出它的后序遍历序列 379 | 样例输入: 380 | DBACEGF ABCDEFG 381 | BCAD CBAD 382 | 样例输出: 383 | ACBFGED 384 | CDAB 385 | */ 386 | 387 | /* 388 | 分析:前序遍历第1个访问的就是根节点,因此只需在中序遍历中找到它,就知道左右子树的前序遍历和后序遍历了。 389 | 这样,可以编写一个递归程序: 390 | 它的作用是根据一个长度为 n 的前序序列 s1 和中序序列 s2,构造一个长度为n的后序序列。 391 | */ 392 | 393 | void build(int n,char* s1,char* s2,char* s) 394 | { 395 | if(n<=0)return; 396 | int p = strchr(s2,s1[0]) - s2; 397 | build(p,s1+1,s2,s); 398 | build(n-p-1,s1+p+1,s2+p+1,s+p); 399 | s[n-1] = s1[0]; 400 | } 401 | 402 | while( scanf("%s%s",s1,s2) == 2 ) 403 | { 404 | int n = strlen(s1); 405 | build(n,s1,s2,ans); 406 | ans[n]='\0'; 407 | printf("%s\n",ans); 408 | } 409 | 410 | 411 | // __________________________6.4 图__________________________ 412 | 413 | // 6.4.1 黑白图像[p107(122/241)] 414 | /* 415 | 输入一个n*n的黑白图像(1表示黑色,0表示白色),任务是统计其中八连块的个数。 416 | 如果两个黑格子有公共边或者公共顶点,就说它们属于同一个八连块。 417 | 如图6-11所示的图形有3个八连块。 418 | 419 | 分析: 用递归求解:从每个黑格子除非,递归访问它所有的相邻黑格。 420 | 类似 扫雷 那个游戏。以后有空再来自己敲代码,很类似的。 421 | */ 422 | int mat[MAXN][MAXN],vis[MAXN][MAXN]; 423 | void dfs(int x,int y) 424 | { 425 | if(!mat[x][y] || vis[x][y])return; 426 | 427 | vis[x][y] = 1; 428 | for(int i = -1;i<2;i++) // 递归访问周围8个格子+自己。 429 | { 430 | for(int j = -1;j<2;j++) 431 | { 432 | dfs(x+i,y+j); 433 | } 434 | } 435 | } 436 | 437 | // 这个程序在哪里判断出界的?在输入之前,在迷宫的外面加上一圈虚拟的白格子 438 | memset(mat,0,sizeof(mat)); // 所有格子都初始化为白色,包括周围一圈的虚拟格子 439 | memset(vis,0,sizeof(vis)); // 所有格子都没有访问过 440 | 441 | for(int i = 0; i q; 494 | q.push(root) 495 | 496 | while ( !q.empty() ) 497 | { 498 | Node now = q.front(); 499 | for(int i = -1;i<2;i++) // 循环访问周围8个格子+自己。 500 | { 501 | for(int j = -1;j<2;j++) 502 | { 503 | int newx = now.x+i; 504 | int newy = now.y+j; 505 | if( mat[newx][newy] && (!vis[newx][newy]) ) 506 | { 507 | Node round; 508 | round.x = newy; 509 | round.x = newy; 510 | q.push(round); 511 | } 512 | } 513 | } 514 | vis[now.x][now.y] = 1; 515 | q.pop(); 516 | } 517 | } 518 | 519 | // 6.4.2 走迷宫【P108(123/241)】 520 | /* 521 | 一个网格迷宫由n行m列的单元格组成,每个单元格要么是空地(用1来表示),要么是障碍物(用0来表示)。 522 | 你的任务是找一条从起点到终点的最短移动序列,其中UDLR分别表示往上,下,左,右移动到相邻单元格。 523 | 任何时候都不能在障碍格中,也不能走到迷宫之外。起点和终点保证是空地。n,m<=100。 524 | 525 | 分析: 还记得二叉树的BFS吗?结点的访问顺序恰好是他们到根结点距离从小到大的顺序。 526 | 类似地,也可以用BFS来按照到起点的距离顺序遍历迷宫图。 527 | 举个例子:假定起点在左上角,我们就从左上角开始用BFS遍历迷宫图,逐步计算出它到每个结点的最短路距离(图6-12(a)), 528 | 以及这些最短路径上每个结点的“前一个结点”(图6-12(b))。 529 | 530 | 注意,如果把图 (图6-12(b)) 中的箭头理解成“指向父亲的指针”,那么迷宫中的格子就变成了一棵树————除了起点之外, 531 | 每个结点恰好有一个父亲。 532 | 533 | 图的BFS几乎与二叉树的BFS一样,但需要避免重复访问一个结点。下面的代码用标记vis[x][y]记录格子(x,y)是否被走过,和DFS一样。 534 | */ 535 | 536 | // 这下面写的啥???! 537 | int q[MAXN*MAXN]; 538 | void bfs(int x,int y) 539 | { 540 | int front = 0;rear= 0, d, u; 541 | u = x*m+y; 542 | 543 | } 544 | 545 | // 6.4.3 拓(tuo)扑(pu)序列【P111(126/241)】 546 | /* 547 | 假设有n个变量,还有m个二元组(u,v),分别表示 变量u小于v.那么,所有变量从小到大排列起来应该是什么样子的呢? 548 | 例如有4个变量a,b,c,d,若已知a0) 89 | { 90 | int test = SomeFunc(n-1, retIdx); 91 | test--; 92 | ... 93 | return test; 94 | } 95 | ... 96 | return 0; 97 | } 98 | 99 | 100 | // Conversion to Iterative function -转化为迭代函数 101 | int SomeFuncLoop(int n, int &retIdx) 102 | { 103 | // (First rule) 104 | struct SnapShotStruct { 105 | int n; // - parameter input 106 | int test; // - local variable that will be used 107 | // after returning from the function call 108 | // - retIdx can be ignored since it is a reference. 109 | int stage; // - Since there is process needed to be done 110 | // after recursive call. (Sixth rule) 111 | }; 112 | ... 113 | } 114 | 115 | /*第二步 116 | 1 在函数的开头创建一个局部变量,这个值扮演了递归函数的返回函数角色。它相当于为每次递归调用保存一个临时值,因为C++函数只能有一种返回类型, 117 | 如果递归函数的返回类型是void,你可以忽略这个局部变量。如果有缺省的返回值,就应该用缺省值初始化这个局部变量。 118 | */ 119 | 120 | // Recursive Function "Second rule" example 121 | int SomeFunc(int n, int &retIdx) 122 | { 123 | ... 124 | if(n>0) 125 | { 126 | int test = SomeFunc(n-1, retIdx); 127 | test--; 128 | ... 129 | return test; 130 | } 131 | ... 132 | return 0; 133 | } 134 | 135 | // Conversion to Iterative Function 136 | int SomeFuncLoop(int n, int &retIdx) 137 | { 138 | // (First rule) 139 | struct SnapShotStruct { 140 | int n; // - parameter input 141 | int test; // - local variable that will be used 142 | // after returning from the function call 143 | // - retIdx can be ignored since it is a reference. 144 | int stage; // - Since there is process needed to be done 145 | // after recursive call. (Sixth rule) 146 | }; 147 | 148 | // (Second rule) 149 | int retVal = 0; // initialize with default returning value 150 | 151 | ... 152 | // (Second rule) 153 | return retVal; 154 | } 155 | 156 | /*第三步 157 | 创建一个栈用于保存“Snapshot”结构体类型变量 158 | */ 159 | // Recursive Function "Third rule" example 160 | 161 | // Conversion to Iterative Function 162 | int SomeFuncLoop(int n, int &retIdx) 163 | { 164 | // (First rule) 165 | struct SnapShotStruct { 166 | int n; // - parameter input 167 | int test; // - local variable that will be used 168 | // after returning from the function call 169 | // - retIdx can be ignored since it is a reference. 170 | int stage; // - Since there is process needed to be done 171 | // after recursive call. (Sixth rule) 172 | }; 173 | 174 | // (Second rule) 175 | int retVal = 0; // initialize with default returning value 176 | 177 | // (Third rule) 178 | stack snapshotStack; 179 | ... 180 | // (Second rule) 181 | return retVal; 182 | } 183 | 184 | /*第四步 185 | 创建一个新的”Snapshot”实例,然后将其中的参数等初始化,并将“Snapshot”实例压入栈 186 | */ 187 | // Recursive Function "Fourth rule" example 188 | 189 | // Conversion to Iterative Function 190 | int SomeFuncLoop(int n, int &retIdx) 191 | { 192 | // (First rule) 193 | struct SnapShotStruct { 194 | int n; // - parameter input 195 | int test; // - local variable that will be used 196 | // after returning from the function call 197 | // - retIdx can be ignored since it is a reference. 198 | int stage; // - Since there is process needed to be done 199 | // after recursive call. (Sixth rule) 200 | }; 201 | 202 | // (Second rule) 203 | int retVal = 0; // initialize with default returning value 204 | 205 | // (Third rule) 206 | stack snapshotStack; 207 | 208 | // (Fourth rule) 209 | SnapShotStruct currentSnapshot; 210 | currentSnapshot.n= n; // set the value as parameter value 211 | currentSnapshot.test=0; // set the value as default value 212 | currentSnapshot.stage=0; // set the value as initial stage 213 | 214 | snapshotStack.push(currentSnapshot); 215 | 216 | ... 217 | // (Second rule) 218 | return retVal; 219 | } 220 | 221 | 222 | /* 第五步 223 | 写一个while循环,使其不断执行直到栈为空。在while循环的每一次迭代过程中,弹出”Snapshot“对象。 224 | */ 225 | // Recursive Function "Fifth rule" example 226 | 227 | // Conversion to Iterative Function 228 | int SomeFuncLoop(int n, int &retIdx) 229 | { 230 | // (First rule) 231 | struct SnapShotStruct { 232 | int n; // - parameter input 233 | int test; // - local variable that will be used 234 | // after returning from the function call 235 | // - retIdx can be ignored since it is a reference. 236 | int stage; // - Since there is process needed to be done 237 | // after recursive call. (Sixth rule) 238 | }; 239 | // (Second rule) 240 | int retVal = 0; // initialize with default returning value 241 | // (Third rule) 242 | stack snapshotStack; 243 | // (Fourth rule) 244 | SnapShotStruct currentSnapshot; 245 | currentSnapshot.n= n; // set the value as parameter value 246 | currentSnapshot.test=0; // set the value as default value 247 | currentSnapshot.stage=0; // set the value as initial stage 248 | snapshotStack.push(currentSnapshot); 249 | // (Fifth rule) 250 | while(!snapshotStack.empty()) 251 | { 252 | currentSnapshot=snapshotStack.top(); 253 | snapshotStack.pop(); 254 | ... 255 | } 256 | // (Second rule) 257 | return retVal; 258 | } 259 | 260 | /*第六步 261 | 将当前阶段一分为二(针对当前只有单一递归调用的情形)。第一个阶段代表了下一次递归调用之前的情况, 262 | 第二阶段代表了下一次递归调用完成并返回之后的情况(返回值已经被保存,并在此之前被累加)。 263 | 如果当前阶段有两次递归调用,就必须分为3个阶段。 264 | 阶段1:第一次调用返回之前, 265 | 阶段2:阶段1执行的调用过程。 266 | 阶段3:第二次调用返回之前。 267 | 如果当前阶段有三次递归调用,就必须至少分为4个阶段。 268 | 依次类推。 269 | */ 270 | // Conversion to Iterative Function 271 | int SomeFuncLoop(int n, int &retIdx) 272 | { 273 | // (First rule) 274 | struct SnapShotStruct { 275 | int n; // - parameter input 276 | int test; // - local variable that will be used 277 | // after returning from the function call 278 | // - retIdx can be ignored since it is a reference. 279 | int stage; // - Since there is process needed to be done 280 | // after recursive call. (Sixth rule) 281 | }; 282 | // (Second rule) 283 | int retVal = 0; // initialize with default returning value 284 | // (Third rule) 285 | stack snapshotStack; 286 | // (Fourth rule) 287 | SnapShotStruct currentSnapshot; 288 | currentSnapshot.n= n; // set the value as parameter value 289 | currentSnapshot.test=0; // set the value as default value 290 | currentSnapshot.stage=0; // set the value as initial stage 291 | snapshotStack.push(currentSnapshot); 292 | // (Fifth rule) 293 | while(!snapshotStack.empty()) 294 | { 295 | currentSnapshot=snapshotStack.top(); 296 | snapshotStack.pop(); 297 | // (Sixth rule) 298 | switch( currentSnapshot.stage) 299 | { 300 | case 0: 301 | ... // before ( SomeFunc(n-1, retIdx); ) 302 | break; 303 | case 1: 304 | ... // after ( SomeFunc(n-1, retIdx); ) 305 | break; 306 | } 307 | } 308 | // (Second rule) 309 | return retVal; 310 | } 311 | 312 | 313 | /*第七步 314 | 根据阶段变量stage的值切换到相应的处理流程并处理相关过程。 315 | */ 316 | // Conversion to Iterative Function 317 | int SomeFuncLoop(int n, int &retIdx) 318 | { 319 | // (First rule) 320 | struct SnapShotStruct { 321 | int n; // - parameter input 322 | int test; // - local variable that will be used 323 | // after returning from the function call 324 | // - retIdx can be ignored since it is a reference. 325 | int stage; // - Since there is process needed to be done 326 | // after recursive call. (Sixth rule) 327 | }; 328 | 329 | // (Second rule) 330 | int retVal = 0; // initialize with default returning value 331 | 332 | // (Third rule) 333 | stack snapshotStack; 334 | 335 | // (Fourth rule) 336 | SnapShotStruct currentSnapshot; 337 | currentSnapshot.n= n; // set the value as parameter value 338 | currentSnapshot.test=0; // set the value as default value 339 | currentSnapshot.stage=0; // set the value as initial stage 340 | 341 | snapshotStack.push(currentSnapshot); 342 | 343 | // (Fifth rule) 344 | while(!snapshotStack.empty()) 345 | { 346 | currentSnapshot=snapshotStack.top(); 347 | snapshotStack.pop(); 348 | 349 | // (Sixth rule) 350 | switch( currentSnapshot.stage) 351 | { 352 | case 0: 353 | // (Seventh rule) 354 | if( currentSnapshot.n>0 ) 355 | { 356 | ... 357 | } 358 | ... 359 | break; 360 | case 1: 361 | // (Seventh rule) 362 | currentSnapshot.test = retVal; 363 | currentSnapshot.test--; 364 | ... 365 | break; 366 | } 367 | } 368 | // (Second rule) 369 | return retVal; 370 | } 371 | 372 | 373 | /*第八步 374 | 如果递归有返回值,将这个值保存下来放在临时变量里面,比如retVal。当循环结束时,这个临时变量的值就是整个递归处理的结果。 375 | */ 376 | // Conversion to Iterative Function 377 | int SomeFuncLoop(int n, int &retIdx) 378 | { 379 | // (First rule) 380 | struct SnapShotStruct { 381 | int n; // - parameter input 382 | int test; // - local variable that will be used 383 | // after returning from the function call 384 | // - retIdx can be ignored since it is a reference. 385 | int stage; // - Since there is process needed to be done 386 | // after recursive call. (Sixth rule) 387 | }; 388 | // (Second rule) 389 | int retVal = 0; // initialize with default returning value 390 | // (Third rule) 391 | stack snapshotStack; 392 | // (Fourth rule) 393 | SnapShotStruct currentSnapshot; 394 | currentSnapshot.n= n; // set the value as parameter value 395 | currentSnapshot.test=0; // set the value as default value 396 | currentSnapshot.stage=0; // set the value as initial stage 397 | snapshotStack.push(currentSnapshot); 398 | // (Fifth rule) 399 | while(!snapshotStack.empty()) 400 | { 401 | currentSnapshot=snapshotStack.top(); 402 | snapshotStack.pop(); 403 | // (Sixth rule) 404 | switch( currentSnapshot.stage) 405 | { 406 | case 0: 407 | // (Seventh rule) 408 | if( currentSnapshot.n>0 ) 409 | { 410 | ... 411 | } 412 | ... 413 | // (Eighth rule) 414 | retVal = 0 ; 415 | ... 416 | break; 417 | case 1: 418 | // (Seventh rule) 419 | currentSnapshot.test = retVal; 420 | currentSnapshot.test--; 421 | ... 422 | // (Eighth rule) 423 | retVal = currentSnapshot.test; 424 | ... 425 | break; 426 | } 427 | } 428 | // (Second rule) 429 | return retVal; 430 | } 431 | 432 | 433 | /* 434 | 第九步 435 | 如果递归函数有“return”关键字,你应该在while循环里面用“continue”代替。如果return了一个返回值,你应该在循环里面保存下来(步骤8), 436 | 然后continue。大部分情况下,步骤九是可选的,但是它能帮助你避免逻辑错误。 437 | */ 438 | // Conversion to Iterative Function 439 | int SomeFuncLoop(int n, int &retIdx) 440 | { 441 | // (First rule) 442 | struct SnapShotStruct { 443 | int n; // - parameter input 444 | int test; // - local variable that will be used 445 | // after returning from the function call 446 | // - retIdx can be ignored since it is a reference. 447 | int stage; // - Since there is process needed to be done 448 | // after recursive call. (Sixth rule) 449 | }; 450 | // (Second rule) 451 | int retVal = 0; // initialize with default returning value 452 | // (Third rule) 453 | stack snapshotStack; 454 | // (Fourth rule) 455 | SnapShotStruct currentSnapshot; 456 | currentSnapshot.n= n; // set the value as parameter value 457 | currentSnapshot.test=0; // set the value as default value 458 | currentSnapshot.stage=0; // set the value as initial stage 459 | snapshotStack.push(currentSnapshot); 460 | // (Fifth rule) 461 | while(!snapshotStack.empty()) 462 | { 463 | currentSnapshot=snapshotStack.top(); 464 | snapshotStack.pop(); 465 | // (Sixth rule) 466 | switch( currentSnapshot.stage) 467 | { 468 | case 0: 469 | // (Seventh rule) 470 | if( currentSnapshot.n>0 ) 471 | { 472 | ... 473 | } 474 | ... 475 | // (Eighth rule) 476 | retVal = 0 ; 477 | 478 | // (Ninth rule) 479 | continue; 480 | break; 481 | case 1: 482 | // (Seventh rule) 483 | currentSnapshot.test = retVal; 484 | currentSnapshot.test--; 485 | ... 486 | // (Eighth rule) 487 | retVal = currentSnapshot.test; 488 | 489 | // (Ninth rule) 490 | continue; 491 | break; 492 | } 493 | } 494 | // (Second rule) 495 | return retVal; 496 | } 497 | 498 | 499 | /* 第十步 500 | 为了模拟下一次递归函数的调用,你必须在当前循环函数里面再生成一个新的“Snapshot”结构体作为下一次调用的快照,初始化其参数以后压入栈,并“continue”。 501 | 如果当前调用在执行完成后还有一些事情需要处理,那么更改它的阶段状态“stage”到相应的过程,并在new Snapshot压入之前,把本次的“Snapshot”压入。 502 | */ 503 | 504 | // Recursive Function "Tenth rule" example 505 | int SomeFunc(int n, int &retIdx) 506 | { 507 | ... 508 | if(n>0) 509 | { 510 | int test = SomeFunc(n-1, retIdx); 511 | test--; 512 | ... 513 | return test; 514 | } 515 | ... 516 | return 0; 517 | } 518 | 519 | // Conversion to Iterative Function 520 | int SomeFuncLoop(int n, int &retIdx) 521 | { 522 | // (First rule) 523 | struct SnapShotStruct { 524 | int n; // - parameter input 525 | int test; // - local variable that will be used 526 | // after returning from the function call 527 | // - retIdx can be ignored since it is a reference. 528 | int stage; // - Since there is process needed to be done 529 | // after recursive call. (Sixth rule) 530 | }; 531 | // (Second rule) 532 | int retVal = 0; // initialize with default returning value 533 | // (Third rule) 534 | stack snapshotStack; 535 | // (Fourth rule) 536 | SnapShotStruct currentSnapshot; 537 | currentSnapshot.n= n; // set the value as parameter value 538 | currentSnapshot.test=0; // set the value as default value 539 | currentSnapshot.stage=0; // set the value as initial stage 540 | snapshotStack.push(currentSnapshot); 541 | // (Fifth rule) 542 | while(!snapshotStack.empty()) 543 | { 544 | currentSnapshot=snapshotStack.top(); 545 | snapshotStack.pop(); 546 | // (Sixth rule) 547 | switch( currentSnapshot.stage) 548 | { 549 | case 0: 550 | // (Seventh rule) 551 | if( currentSnapshot.n>0 ) 552 | { 553 | // (Tenth rule) 554 | currentSnapshot.stage = 1; // - current snapshot need to process after 555 | // returning from the recursive call 556 | snapshotStack.push(currentSnapshot); // - this MUST pushed into stack before 557 | // new snapshot! 558 | // Create a new snapshot for calling itself 559 | SnapShotStruct newSnapshot; 560 | newSnapshot.n= currentSnapshot.n-1; // - give parameter as parameter given 561 | // when calling itself 562 | // ( SomeFunc(n-1, retIdx) ) 563 | newSnapshot.test=0; // - set the value as initial value 564 | newSnapshot.stage=0; // - since it will start from the 565 | // beginning of the function, 566 | // give the initial stage 567 | snapshotStack.push(newSnapshot); 568 | continue; 569 | } 570 | ... 571 | // (Eighth rule) 572 | retVal = 0 ; 573 | 574 | // (Ninth rule) 575 | continue; 576 | break; 577 | case 1: 578 | // (Seventh rule) 579 | currentSnapshot.test = retVal; 580 | currentSnapshot.test--; 581 | ... 582 | // (Eighth rule) 583 | retVal = currentSnapshot.test; 584 | // (Ninth rule) 585 | continue; 586 | break; 587 | } 588 | } 589 | // (Second rule) 590 | return retVal; 591 | } --------------------------------------------------------------------------------