为什么不推荐递归
递归的调试难度奇高,就决定了实际项目中很少用递归。
而且递归确实运行效率低,因为函数一层一层调用存在调用栈,
在切换到更深层的函数时要产生断点,为了保证回来时继续运行,
必须保存现在所处函数的各种状态,回来时恢复状态,这样一层层下去性能损失就不断增加。
大量开辟在栈区的内存 ,直到每一层的递归结束或整个递归结束才释放 且这个内存空间可能呈几何级数增加, 空间效率不佳, 有可能会栈溢出
而要知道什么是尾递归, 首先得指到什么是尾调用
. . .
只谈一下单链表, 链表实在是太重要, 是前面两篇说算法博客的基础, 了解了其应用和衍生, 再去了解其本身就有动力了
typedef struct Node |
头结点的优点:
include <stdio.h> |
运行结果:3 2 1
0 3 2 1
提示:没有 0 的为无头结点的头插法输出结果,有 0 的为有头结点的头插法的输出结果
. . .
使用SVN在管理代码的时候免不了进行代码的合并和还原,特别是当前版本的修改发现有重大问题的时候,还原是避免不了的,那么究竟应该怎样操作呢?
使用SVN查看文件或目录的日志的时候,右键单击日志记录会弹出下面这个界面,今天我们来着重了解一下被红圈标记的三个选项——“Update item to this version”,“Revert to this version”,“Revert changes from this version”,这三个选项对于刚接触SVN的人确实不太好区分,一开始我也搞不懂,直到亲自试验一下才搞清楚这三个选项的用法。
在讲解这三个选项的作用之前,我们还是先来假定一个使用情景,假设我们的项目文件一共有8个版本,它版本号分别是1,2,3,4,5,6,7,8。
这个选项的作用是将文件版本更新到对应所选的版本(当然内容也修改到了相应的版本)。如果我们是在版本4这里点击“Update item to this version”,表示5~8版本所作的修改全部作废,这个文件的历史回退到了版本4那个时代,但是需要注意的是,此时文件的版本是4,并不是最新的。我们知道SVN工具中如果文件不是最新版本就无法上传,所以说这个功能只是用来暂时还原一下版本,来查询某个问题的,不能将还原后的文件上传。这个特别是当你服务器启动不了了, 把版本退回一个可以启动版本的情况
这个选项的作用是将文件的内容更新到对应的版本,版本号没有发生变化。如果我们是在版本4这里点击“Revert to this version”,表示5~8版本所作的修改全部被还原,此时svn里会有5-8被还原的文件改动可以提交, 此时文件和版本4的文件一模一样,但需要注意的是这项操作相当于我们把版本4这个文件拷贝了一份赋值给了当前目录下的文件,此时的文件版本还是8,并且是可以提交的,提交以后文件的版本变成了9,增加了一个新的版本,虽然这个版本和版本4的内容是一样的。
这个选项的作用是将对应版本的修改还原,文件的版本号不发生变化,相当于在当前本版本上剔除某些版本所作的改变。如果我们是在版本4这里点击“Revert changes from this version”,表示版本4所作的修改被抹杀了,只剩下除版本4以外的7个修改了,但是此时文件是可以上传的,并且会生成新的版本9,只是版本9只包括除版本4以外的7次修改。这个选项是可以选择多个版本的,如果我们选择4,5,6,7这四个版本点击“Revert changes from this revision”,那么这几次修改都会被抹杀。如果我们选择5,6,7,8这四个版本点击“Revert changes from this revision”,表示取消这几个版本的修改,实际上和在版本4这里点击“Revert to this version”的作用是一样的。
|
会打印什么?
答案: 打印1之后崩溃
真正的原因是:
因为对于非虚成员函数,C++这门语言是静态绑定的。这也是C++语言和其它语言Java, Python的一个显著区别。以此下面的语句为例:somenull->foo();这语句的意图是:调用对象somenull的foo成员函数。如果这句话在Java或Python等动态绑定的语言之中,编译器生成的代码大概是:找到somenull的foo成员函数,调用它。
(注意,这里的找到是程序运行的时候才找的,这也是所谓动态绑定的含义:运行时才绑定这个函数名与其对应的实际代码。有些地方也称这种机制为迟绑定,晚绑定。)但是对于C++。为了保证程序的运行时效率,C++的设计者认为凡是编译时能确定的事情,就不要拖到运行时再查找了。
所以C++的编译器看到这句话会这么干:
所以到了运行时,由于foo()函数里面并没有任何需要解引用somenull指针的代码,所以真实情况下也不会引发segment fault。这里对成员函数的解析,和查找其对应的代码的工作都是在编译阶段完成而非运行时完成的,这就是所谓的静态绑定,也叫早绑定。正确理解C++的静态绑定可以理解一些特殊情况下C++的行为。
求 a 和 b 的最大公约数
int measure(int a, int b) |
在如下7*5的棋盘中,请计算从A移动到B一共有多少走法?要求每次只能向上或向右移动一格,并且不能经过P。(答案为492)
给定一个M*N的格子或棋盘,从左下角走到右上角的走法总数(每次只能向右或向上移动一个方格边长的距离)
运用动态规划来解答 :
我们可以把棋盘的左下角看做二维坐标的原点(0,0),把棋盘的右上角看做二维坐标(M,N)(坐标系的单位长度为小方格的变长)
用f(i,j)表示移动到坐标f(i,j)的走法总数,其中0=<i,j<=n,设f(m,n)代表从坐标(0,0)到坐标(m,n)的移动方法,则
f(m,n)=f(m-1,n)+f(m,n-1).
于是状态f(i,j)的状态转移方程为:
f(i,j)=f(i-1,j)+f(i,j-1) if i,j>0
f(i,j)=f(i,j-1) if i=0
f(i,j)=f(i-1,j) if j=0
初始情况就为:f(0,0)=0, f(0,1)=1, f(1,0)=1,这个问题可以在时间O(n^2),空间O(n^2)内求解,非递归解.
所以答案为 492 =
SumWaysOfMoveOnChessBoard(7, 5) - SumWaysOfMoveOnChessBoard(3, 3) * SumWaysOfMoveOnChessBoard(7 - 3, 5 - 3) |
int SumWaysOfMoveOnChessBoard_Recursion(int m, int n) |
int SumWaysOfMoveOnChessBoard_NonRecursion_RawArray(int m, int n) |
大数加法思路 :
模拟小学列竖式
9 8
+ 2 1
-------------
(1)(1)(9)
大数乘法思路 :
模拟乘法累加 - 改进
简单来说,方法二就是先不算任何的进位,也就是说,将每一位相乘,相加的结果保存到同一个位置,到最后才计算进位。
例如:计算98×21,步骤如下
9 8
× 2 1
-------------
(9)(8) <---- 第1趟: 98×1的每一位结果
(18)(16) <---- 第2趟: 98×2的每一位结果
-------------
(18)(25)(8) <---- 这里就是相对位的和,还没有累加进位
这里唯一要注意的便是进位问题,我们可以先不考虑进位,当所有位对应相加,产生结果之后,再考虑。
从右向左依次累加,如果该位的数字大于10,那么我们用取余运算,在该位上只保留取余后的个位数,而将十位数进位(通过模运算得到)累加到高位便可,循环直到累加完毕。
void BigIntAddition(char* bigIntA, char* bigIntB) |
问题:有两个字符串str和str2,求出两个字符串中最长公共子串长度。
比如:str=acbcbcef,str2=abcbced,则str和str2的最长公共子串为bcbce,最长公共子串长度为5。
算法思路:
1、把两个字符串分别以行和列组成一个二维矩阵。
2、比较二维矩阵中每个点对应行列字符中否相等,相等的话值设置为1,否则设置为0。
3、通过查找出值为1的最长对角线就能找到最长公共子串。
针对于上面的两个字符串我们可以得到的二维矩阵如下:
从上图可以看到,str和str2共有5个公共子串,但最长的公共子串长度为5。
为了进一步优化算法的效率,我们可以再计算某个二维矩阵的值的时候顺便计算出来当前最长的公共子串的长度,
即某个二维矩阵元素的值由 item[i][j]=1
演变为 item[i][j]=1 +item[i-1][j-1]
,这样就避免了后续查找对角线长度的操作了。修改后的二维矩阵如下:
故状态转移方程
X[i] == Y[j],dp[i][j] = dp[i-1][j-1] + 1
X[i] != Y[j],dp[i][j] = 0
int LongestCommonSubstring(char* strA, char* strB) |
队列(queue)是一种是相对于栈的一种数据结构,它是先进先出(First In First Out)。
它只可以在尾部添加元素。
双端队列(deque double ended queue(双端队列))是一种相对于队列的一种数据结构。它可以在尾部和头部插入、移除和获取。
通过他们各自的成员函数我们能一目了然的看出区别
stack<Type> s
: 定义一个stack的变量 queue<Type> M
: 定义一个queue的变量 deque<Type> c
: 定义一个deque的变量接着上一篇, 上一篇主要说了各种排序算法, 但对几个常用的数据结构还未提及,所以这一篇主要讲二叉树, 二叉树已经包括很多链表的知识了。所有代码都是测试过的, 可以直接撸.
这里不举太多数字方面的东西, 我们直接看图, 直观感性的认识满二叉树和完全二叉树:
有一点性质需要牢记:具有n个结点的完全二叉树的最大高度为log2n+1
二叉树的二叉链式存储方案的代码表示:
typedef struct BinaryTreeNode |
. . .