宝塔服务器面板,一键全能部署及管理,送你10850元礼包,点我领取

在编程过程中

一个伙伴说我敲代码又难看又慢

怎么办?

今天大雄介绍几个编程诀窍

让你的代码快速上升

for循环

1

初始化for循环变量

在c语言中,经常这样使用for语句。

for (英制=0; Istrlen(s; I ) )

这个看起来很完美,代码也很漂亮。 让我们来看看另一种写法:

for (英制=0,林制(s ) ) s; i len; I ) )

唯一的不同是,后者用len变量保存字符串s的长度,在判断条件时直接比较I和len。

在第二种方法中,通过使用额外的变量len,无需在每次条件判断时重复执行函数strlen(s )。 特别是在for循环体的语句少、字符串长的情况下,如果将字符串的长度设为n,函数的执行复杂性设为o ) n,则函数的执行会花费时间。

在很多leetcode主题中,两种不同的写法所需的执行时间有很大不同。

同样地,在c、Java中,该写法for(intI=0; I S .长度(; I )也不推荐使用。

c编译时为length ) )有些编译器将函数替换为内联或特定变量,但在Java中也替换为“属性”,但许多伙伴倾向于使用后者。

有趣的是,在Python语法中,for循环是这样表示的:

财富(LEN ) s ) )

由此,就不会重复地求出字符串s的长度,从而获得具有意义感的高性能。

2

变量定义位置(是for循环内部还是外部) ) ) ) ) ) ) ) ) ) )。

//内部for(inti=0; i 10; I (字符串=秒;//外部字符串; for (英制=0; i 10; I () { s=秒}; }

如果在内部定义,则必须为每个循环重新定义string变量s。 也就是说,每个循环都需要调用构造函数和析构函数。定义只需要在外部为每个循环调用拷贝构造函数。

通常,建议在外部定义大对象,以提高运行效率,在内部定义小对象,从而提高程序的可读性。

基本运算和函数

1

乘以2 (或2的整数次方),或者除以2 )或2的整数次方)时,请尽量用位运算替换。

2

将除法的使用抑制在最小限度(条件判断时将if(a==b/c )置换为if ) a*c==b )等,能够适当地变换为乘法运算。 除法需要更多的移位和转换操作,通常需要两倍于相应乘法的时间)

3

虽然大多使用=、-=、*=、/=等复合运算符,但以加上一为例,按照效率从高到低的顺序(I、i =1、i=i 1 ) )

4

请学习swap、max、min、sort、qsort、ati、stoi等紧凑的库函数。 这些都很容易使用,比一般人写的代码更有效率。

inline、常数、修饰符

inline将函数内联,编译器将函数主体代码“复制&amp”到函数调用目标; 建议“粘贴”。 在函数主体较短且函数调用频繁的情况下,可以有效避免函数调用引起的内存开销(因为每次调用函数系统都会生成很多额外的变量)。

const不仅可以防止修改的变量发生变化,还可以提高程序的稳定性,同时使编译器能够更好地优化代码。

举个例子,如果用常数修饰某个常数,程序中使用该常数的所有地方都会被该值代替,从而不会浪费时间读取地址。

限定返回类型和参数类型将作为引用传递,从而避免对象赋值结构所需的时间和内存。

缓存(缓存)和寄存器) register )

不仅是CPU,寄存器和缓存的访问速度也最高。 一般来说,我不建议自己定义寄存器变量或控制数据缓存。 编译器会自动将常用数据的一部分放入缓存和寄存器。

但是,了解编译器控制数据的根据对编程也非常有帮助。

一般来说,存储在寄存器/缓存中的数据的优先顺序是用register修饰的变量、循环控制变量、auto局部变量、静态变量、用户自己分配的存储器数据。

迭代器(迭代器)。

1

访问容器中的元素时,请尽量使用迭代程序,而不是下标和指针。

首先,迭代程序访问元素类似于指针,但不需要根据下标值来计算下标访问的地址,从而在循环中节省了大量时间。

接着,堆栈

代器作为指针一种延拓,能更好的代表并操作其所指的对象,而在下标访问中我们往往用一个int值pos来表示pos下标下的元素,没有面向对象编程的直观。

再次,迭代器为我们访问各种容器(数组,vector,list,map,queue,deque,set …)中的元素提供了统一的方法,其作用类似于“语法糖”,让编程更加简单、方便。

2

另外在使用迭代器的自增和自减运算符需要注意,iterator++,和++iterator的效率有天壤之别。两种自增方式的运算符重载如下:

iterator & operator++(){  // 前增 ++*this; return (*this);}iterator operator++(int){  // 后增 iterator temp = *this; ++*this; return (temp);}

后增(iterator++)相对于前增(++iterator)创建了一个临时迭代器temp,并将其返回,而前增直接返回原来迭代器的引用。在for循环中的频繁自增操作中,创建临时迭代器temp,以及返回temp时调用的复制构造函数所需的时间不容忽视。

vector容器

vector容器毫无疑问是C++STL使用最为频繁的容器了,当然这个强大容器的使用也包含了很多的小技巧。

1

在适当时候使用emplace和emplace_back函数来替代insert和push_back函数。

它们之间的区别很明显,insert和push_back函数参数是vector容器里面的模板对象,而带emplace的函数参数是模板对象的构造函数的参数。

这意味着后者将模板对象插入到vector容器的过程中不用先生成好对象,而是可以直接利用参数构造。

当然如果模板对象已经是生成好的,那就没有必要用emplace函数了。

在很多循环递归迭代中,往往需要反复向vector容器中添加对象,这时候额外构造一个对象所需要的时间和空间就不容忽视了,因此这是一个vector进阶用法的好trick。

2

vector容器的底层实现是数组,并且在当元素大于最大容量的时候会重新生成一个更大的数组,将原来数组中的对象复制构造到新数组中。

由于要重新分配大量内存以及反复调用复制构造函数,这对时间和空间的开销是巨大的。

为了减少内存的重新分配,我们可以适当的估计我们需要保存的元素数量,并在vector初始化的时候指定其capacity。

这种方法很直接但也有其缺点,就是我们往往很难在开始的时候就估计准确我们要保存的元素数量(如果能,我们就直接用数组得了)。

一个很好的解决办法是:将vector中保存的元素改为指针,指针指向我们真正想要保存的对象。

由于指针相对于其所指向的对象来说占用内存很小,而且在复制的时候不用调用复制构造函数,因此以上提到的一些缺点都能很好的克服。

事实上,对于能够熟练控制内存分配的老码农来说,这种vector + 指针的方式是十分完美的。

if条件判断

在进入讨论之前,我们先思考下面这个例子:

一个班的数学成绩如下:74、76、78、94、97、68、77、65、54、89…,总共有50个数据。

要求用程序将分数为优秀(>=80)、良好(>=70)、及格(>=60)、不及格(>=0)四个分数段。

for 所有学生分数 if 分数 < 60   归为不及格段 else if 分数 < 70   归为及格段 else if 分数 < 80   归为良好段 else    归为优秀段

这个伪代码逻辑没有问题,但是就这个数据来看这段代码运行效率糟透了。

由于这个班的数学成绩绝大多数是良好和优秀,而这个程序需要三次if判断才能将分数归为良好,三次if判断加上一个else才能将分数归为优秀,所以绝大多数前两个if判断是不必要的。

我们将if判断语句的顺序变换下:

for 所有学生分数 if 分数 >= 80   归为优秀段 else if 分数 >= 70   归为良好段 else if 分数 >= 60   归为及格段 else    归为不及格段

在这个伪代码中绝大多数分数都在前两个if语句中完成了分段。两者的时间效率相差巨大,实际运行也发现,前者是后者运行时间的两倍多。

switch分支判断

switch语句的底层实现主要有三种方式:转换为if else 语句,跳转表,树形结构。

当分支比较小时,编译器倾向于转换为if else语句,当分支比较多,分支范围很广时,用树形结构,当分支数量不算多,分支范围紧凑时,用跳转表。

跳转表的底层实现是数组映射,对条件转换的效率为O(1),相比于另外两种方式优势明显,因此我们应该尽量控制分支的数量,以及让各个分支的int型数据紧凑。

这些技巧小伙伴们都了解了吗?

#你在编程过程中还有什么小技巧#

有趣的编程代码(自学代码)-冯金伟博客园

更多干货关注公众号【老九学堂】(づ ̄3 ̄)づ╭❤~