`
talentluke
  • 浏览: 604745 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

循环优化与链表是否一个闭环

 
阅读更多

 

经典的判断链表是否有环:

方法一:有个经典的算法就是解决这个问题的,好象是叫快慢法.他的原理是,如果A,B两人从同一地点出发,B的速度大于A,那么如果存在一个环的话,B和A肯定是能再见面的.

C/C++ code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ool IsLoop( link* head)
{
   link* s = head; //移动缓慢的指针
   link* f = head; //移动快速的指针
 
    
   do
   {
      if( s == NULL ) return (false );
      s = s->next; //一次向前移动一个
      
      if( f== NULL ) return (false );
      f = f->next;
     
      if( f== NULL ) return (false );
      f = f->next; //一次向前移动2个
   }while( f!= s);
 
   return (f!=NULL);
}
 
方法二:按照顺序读取链表,然后没读取一个就记录下当前位置和当前位置指向的下一个位置,然后判断当前位置的下一个位置是否前面已读取过。当然,要自己写一个辅助结果去记录。

优化下面这段代码,并说明原因
 for(int i =0; i < 1000; i++)
    for (int j=0; j < 100; j++)
       for (int k = 0; k < 10; k++)
           fun(i, j, k);

for (int k = 0; k < 10; k++){
    for (int j=0; j < 100; j++){
        for(int i =0; i < 1000; i++){
           fun(i, j, k);
        }
   }
}



虽然循环次数相同,但是层与层之间的切换次数减少,节省了栈的切换频率,可以提升效率。

 

 这个问题的主要原因是CPU内部的指令执行机制。现在,基本上CPU内部都有分支指令预测,就是当执行(现在大多将这一阶段提前到预取指令时执行)到转移指令时,都会直接从分支目标缓存(BTB)中取出目标指令的地址,然后将要执行的指令提前预取到CPU的指令预取指令队列中。这样,显然大大提高了效率。举个例子,一个10次的一层循环在执行时,除了在第一次和最后一次会预测错误外,其他8次都会预取成功,避免了执行转移指令时重新取出新指令造成的时间浪费。 

所以,当有两层循环,外层循环数为A,内层为B,A远大于B,那么最终造成的预测错误数为A*2+2,而如果外层数为B,内层数为A,预测错误数为B*2+2,显然后者要节省更多时间,而且这个时间是很可观的。A比B越大,这个时间差越明显。 

 

代码优化原则

*************************************************************************************************

优化原则:

最最首先要考虑的问题是:我选择的核心算法是否可以改进,然后考虑算法的实现是否完整,有没有与原始算法存在出入的地方,最
后优化代码,如下:

1.优化最为核心的代码-->1.最为常用的函数,2.计算量很大的函数,3.实现核心算法的函数

2.优化嵌套for循环,尽量减少循环的层次,尽量让小循环在外层,大循环在内层,尽量不要在for循环内部执行if语句,尽量不在

  for循环内部创建临时变量(在外面创建临时变量),尽量不重复计算相同的表达式(用临时变量存储),例如下面的代码就不好
 
  for(int i = 0;i<100;i++)
  {
      a = (c*d/f-e)*2;
      b = (c*d/f-e)*2;
  }
  上面的代码中“(c*d/f-e)*2”就是重复计算,我们可以将它改为:
  int temp = (c*d/f-e)*2;
  for(int i = 0;i<100;i++)
  {
      a = temp;
      b = temp;
  }
  在我的印象中:赋值运算比四则运算要块!上面的优化策略虽然提高了速度,但是可能造成代码很难理解!需要权衡,当然建议尽  
  量优化,同时增加必要的注释!
 

3.优化if语句,如果是单条件,尽量让表达式简单,例如:“if(condition == true){...}”应该改为“if(condition){...}”

  如果是多条件,尽量优化这些条件排放的顺序,因为:(条件1 && 条件2)语句中
  
  当 条件1 不成立的时候是不会判断 条件2 的,(条件1 || 条件2)语句中,当 条件1 成立的时候是不会判断 条件2 的!

4.优化return语句,尽量不产生临时变量然后返回,这样计算量会加大。如果可以,尽量直接返回表达式。例如:不要出现如下代码 
 
  int a;
  a = b+c;
  return a;

  将上面的代码直接改为:return a+b;可以提高速度!

  又例如:不要出现如下的代码:

  if(condition)
     return true;
  else
     return false;

  应该直接改为:return condition;

5.优化所有的 *2 或 /2语句,改为左移或者右移。当然编译器如果能自动将 *2 或者 /2 转化为左移或者右移就另当别论!

6.优化所有的 a = a + b;语句,改为 a += b;

7.如果可以,尽量以 “引用传递” 的方式将参数传入函数体,尽量以“引用传递”的方式返回结果!

8.检查所有的 new 语句,一定要有配套的 delete 语句与之对应!防止因为没有及时释放内存而导致内存泄漏!

9.优化指针,for循环中如果有指针操作,如果可能,尽量通过移动指针来直接对内存中的数据进行处理,而不要采用指针首地址+偏
  移量的方式来访问数据,简单点儿就是,p[i] = a;是可以优化的,例如:

  int *p = &a[0];
  for(int i = 0;i<100;i++)
  {
      p[i] = 1;
      //本质是 *(p+i) = 1;   一个加法运算,一个赋值运算 
  }
  上面的代码中p指针没有变化,其实“p[i] = 1” 会转化为“ *(p+i) = 1”;
  
  所以上面的代码可以这样改:

  int *p;
  for(p = &a[0];p-&a[0]<100;p++)
  {
      *p = 1;
  }
  优化后的代码比源代码计算次数少了一次!也就是原始代码的三分之二!

****************************************************************************************************************

分享到:
评论

相关推荐

    约瑟夫环代码,建立一个具有n个链结点的循环链表。

    单向循环链表是一种特殊的数据结构,每个节点包含一个指向下一个节点的指针,最后一个节点的指针指向头节点,形成闭环。 #### 三、代码解析 1. **数据结构定义**: ```c typedef struct node { int num; // ...

    双向循环链表在 LINUX kernel 中的实现

    双向循环链表是一种特殊的链表类型,其中每个节点都包含前驱和后继两个指针,并且整个链表形成了一个闭环。这种设计使得链表可以从任意方向进行遍历,并且支持高效的插入和删除操作。在 Linux 内核中,为了减少内存...

    数据结构:单向循环链表源码

    与普通单向链表不同,循环链表的最后一个节点的指针不是空的,而是指向链表的第一个节点,形成一个闭合的环。这种设计使得遍历链表更为方便,因为没有明显的"结束"标志。 **单向循环链表特性** 1. **无头结点**:与...

    C语言 循环单链表的简单操作

    循环单链表与普通单链表的主要区别在于最后一个节点的指针不再为NULL,而是指向链表的第一个节点,形成了一个环状结构。这种结构在处理循环数据流或避免特殊边界条件时非常有用。 首先,我们需要理解链表的基本概念...

    C语言循环链表

    循环链表是链表的一种特殊形式,在这种链表中,最后一个节点的指针不是指向`NULL`,而是回指到链表的第一个节点,形成一个闭环。 #### 二、代码解析与实现 ##### 1. 结构体定义 ```c typedef struct node { int ...

    Java的循环单链表及其测试程序

    在Java编程中,循环单链表是一种特殊的数据结构,它扩展了传统的单链表概念,使得链表的最后一个元素指向第一个元素,形成了一个无尽的循环。这种数据结构在处理循环逻辑或者需要快速访问链表头尾的情况时非常有用。...

    线性链表查询算法

    循环链表是线性链表的一种特殊形式,其特点是尾节点的指针不为空,而是指向头节点,形成了一个闭环。这种结构使得遍历整个链表变得更为简单,因为不需要额外判断是否到达链表末尾。 **3.2 查询算法** 查询算法是...

    C++语言实现一个多级文件目录管理系统,采用链表的数据结构。.zip

    4. 循环链表:链表的最后一个节点的指针指向链表的第一个节点,形成一个闭环。 在这个系统中,每个节点可能代表一个文件或目录,包含以下信息: - 文件/目录名:标识该节点的唯一名称。 - 类型:指示是文件还是目录...

    linux链表详解

    - **循环链表**:链表的最后一个节点的指针指向第一个节点,形成闭环,便于从任一节点开始遍历整个链表。 #### Linux内核中的链表实现 在Linux内核中,链表的实现主要集中在`include/linux/list.h`头文件中,采用...

    820数据结构背诵冲刺笔记1

    与非循环链表不同的是,循环链表的尾节点通过其指针域指向链表的头结点,从而形成一个闭环。这种结构使得链表的操作更加多样,特别是对于循环单链表,其判空条件变得简单,只需检查头结点的指针是否指向自己。在循环...

    约瑟夫环(用链表实现的,简单易懂,可用直接运行)

    循环链表是一种特殊的链表形式,其中最后一个节点的指针不是指向空值,而是指向链表的第一个节点,从而形成一个闭环。这样可以方便地处理循环遍历等问题。 #### 知识点四:代码解析 1. **头文件导入**: ```c #...

    Linux内核中链表和散列表的实现原理揭秘

    - **循环性**:链表的第一个节点的`prev`指针指向最后一个节点,最后一个节点的`next`指针指向第一个节点,形成闭环。 **初始化:** 双向循环链表通常通过`INIT_LIST_HEAD`宏进行初始化: ```c static inline void ...

    59、1334:【例2-3】围圈报数(A).pdf

    - **定义**:循环链表是一种特殊的链表形式,其中最后一个节点的next指针不是指向null,而是指向链表的第一个节点,形成了一个闭环。 - **适用场景**:在“围圈报数”问题中,由于所有人围成一个圈,因此循环链表...

    c语言数据结构语言数据结构

    - 循环链表是一种特殊类型的链表,其中最后一个节点的指针指向头节点,形成闭环。这种结构便于遍历整个列表,同时,通过设置尾指针,可以快速访问到链表的末尾,从而提高效率。 #### 双向链表 - 双向链表中每个节点...

    实战应用Java算法分析与设计(链表、二叉树、哈夫曼树、图、动态规划)视频

    循环链表的最后一个节点指向链表的头结点,形成一个闭环。循环链表简化了一些操作,如遍历整个链表时无需记录链表尾节点。 #### 三、二叉树 **1. 二叉树基础** 二叉树是一种非线性的数据结构,其中每个节点最多有...

    约瑟夫问题的设计与实现

    单向循环链表是一种特殊的链表形式,其中最后一个节点的指针指向链表的第一个节点,形成一个闭环。这种结构非常适合处理需要循环遍历的场景,例如本问题中的人员报数并离开的情况。 - **类型定义**:定义单向循环...

    数据结构课程设计约瑟夫环

    总结来说,约瑟夫环问题是一个极好的数据结构教学实例,它涵盖了链表操作、循环数据结构、算法设计与优化等多个方面。通过实践,学生不仅能掌握基本的数据结构知识,还能锻炼逻辑思维和问题解决能力。在进行课程设计...

    数据结构_线性表_试题

    在单循环链表中,链表的最后一个节点的next指针指向头节点,形成闭环。因此,判断一个节点是否为链尾节点的条件是其next指针是否指向头节点,即`p-&gt;next==h`。 #### 6. 构建单链表的时间复杂度 构建一个包含n个节点...

    数据结构c版(1)-线性表文章代码汇总

    这种链表每个节点不仅有指向下一个节点的指针,还有一个指向前一个节点的指针,形成一个闭环。双向链表提供了前向和后向遍历的能力,使得插入和删除更加灵活。"双向带头循环列表.zip"文件中将展示如何在C语言中实现...

Global site tag (gtag.js) - Google Analytics