精华内容
下载资源
问答
  • 算法描述

    千次阅读 2019-09-22 17:40:02
    (2)描述算法的工具:算法可用自然语言、框图或高级程序设计语言进行描述。自然语言简单但易产生二义性;框图直观但擅长表达数据的组织结构;而高级程序设计语言则较为准确、严谨,但因需考虑细节问题而显得相对...

    1.算法、语言、程序的关系

    首先分析数据结构中算法、语言和程序的关系。

    (1)算法:描述数据对象之间的关系(包括数据逻辑关系、存储关系描述)。

    (2)描述算法的工具:算法可用自然语言、框图或高级程序设计语言进行描述。自然语言简单但易产生二义性;框图直观但不擅长表达数据的组织结构;而高级程序设计语言则较为准确、严谨,但因需考虑细节问题而显得相对繁琐。

    (3)程序是算法在计算机中的实现(与所用计算机及所用语言有关)。程序设计的实质是对实际问题选择一种好的数据结构,加之设计一个好的算法,而好的算法在很大程度上取决于描述实际问题的数据结构。

    2.设计实现算法过程的步骤

    (1)找出与求解有关的数据元素之间的关系(建立结构关系)

    (2)确定在某一数据对象上所施加的运算。

    (3)考虑数据元素的存储表示。

    (4)选择描述算法的语言。

    (5)设计实现求解的算法,并用程序语言加以描述。

    3.描述算法的语言选择

    高级语言描述算法具有严格、准确的优点,但用于描述算法,也有语言细节过多的弱点,为此可采用类语言形式。所谓类语言,是指接近于高级语言而又不是严格的高级语言,它具有高级语言的一般语句,撇掉语言中的细节,以便把注意力集中在算法处理步骤本身的描述上。

    传统的描述语言是采用Pascal语言,由于该语言语法规范严谨,非常适合于数据结构。在Windows环境下,又出现了一系列功能强大且面向对象的程序开发工具,如visual C++、Borland C++、Visual Basic等。近年来在计算机科学研究、系统开发、教学以及应用开发中,C语言的使用范围越来越广,C语言成为许多学校计算机专业与非计算机专业必修的高级程序设计语言。C语言类型丰富,执行效率高。

     

    展开全文
  • 算法描述

    千次阅读 2020-04-13 21:34:45
    <center>算法描述<...流程图是一种传统的算法表示法,用不同的图框代表不同性质的操作,用流程线来知识执行方向。直观形象,易于理解。 (1)、流程图符号 正确性食指所写的算法安祖具体...

    <center>算法的描述</center>

    注:本文为学习《C语言从入门到精通》时,对部分章节的总结

    1、自然语言

    人们日常使用的语言,通俗易懂,但用来描述较为复杂的算法时,不是很方便。

    2、流程图

    流程图是一种传统的算法表示法,用不同的图框代表不同性质的操作,用流程线来知识执行方向。直观形象,易于理解。

    (1)、流程图符号

    正确性食指所写的算法应能安祖具体问题的要求,即对任何合法的输入,都会得到正确的结果。

    流程图使用一些图框来表示各种操作。

    a.起止框:标识算法的开始和结束;

    b.输入/输出框:标识数据任何种类的输入或输出;

    c.流程线:表示控制流的流线;

    d.判断框:用于对一个给定的条件进行判断,根据条件成立与否决定如何执行后续操作;

    e.处理框:表示在过程的一个单独的步骤;

    f.注释框:用于说明一些事项;

    g.连接点:用于将画在不同地方的流程线连接起来。

     

    :从键盘输入3个数并分别赋值给a、b、c,要求从大到小的顺序将它们打印出来。

     

    (2)、3种基本结构

    a.顺序结构:简单的线性结构;

    b.选择结构:也称为分支结构,必须至少包含一个判断框;

    c.循环结构:反复的执行一系列操作,直到条件不成立时才终止循环。按照判断条件出现的位置,可分为当型循环和直到型循环。

    3、N-S流程图

    在普通流程图的基础上去掉了流程线。

    (1)、顺序结构

     

    (2)、选择结构

     

    (3)、循环结构

    a.当型循环:

     

    b.直到型循环:

     

    展开全文
  • 算法描述---伪代码

    千次阅读 2017-11-23 14:13:40
     算法描述是指对设计出的算法,用一种方式进行详细的描述,以便与人交流。描述可以使用自然语言、伪代码,也可使用程序流程图,但描述的结果必须满足算法的五个特征。  使用自然语言描述算法显然很有吸引力,但是...

     算法描述

       算法描述是指对设计出的算法,用一种方式进行详细的描述,以便与人交流。描述可以使用自然语言、伪代码,也可使用程序流程图,但描述的结果必须满足算法的五个特征。

      使用自然语言描述算法显然很有吸引力,但是自然语言固有的不严密性使得要简单清晰的描述算法变得很困难。因此,使用伪代码来描述算法是一个很好的选择。

      算法的特征

    1. 输入:一个算法必须有零个或以上输入量。
    2. 输出:一个算法应有一个或以上输出量,输出量是算法计算的结果。
    3. 明确性:算法的描述必须无歧义,以保证算法的实际执行结果是精确地符合要求或期望,通常要求实际运行结果是确定的。
    4. 有限性:依据图灵的定义,一个算法是能够被任何 图灵完备系统模拟的一串运算,而图灵机器只有有限个状态、有限个输入符号和有限个转移函数(指令)。而一些定义更规定算法必须在有限个步骤内完成任务。
    5. 有效性:又称可行性。能够实现,算法中描述的操作都是可以通过已经实现的基本运算执行有限次来实现。

      伪代码

      伪代码是自然语言和类编程语言组成的混合结构。它比自然语言更精确,描述算法很简洁;同时也可以很容易转换成计算机程序。虽然如此,但计算机科学家们从来就没有对伪代码的形式达成共识,不同作者的教材会设计他们自己的“方言”(伪代码)。幸运的是,这些伪代码都十分相似,任何熟悉一门现代变成语言的人都完全能够理解。

      使用伪代码描述算法可以让程序员很容易将算法转换成程序,同时还可以避开不同程序语言的语法差别,如Pascal语言使用“:=”作为赋值,使用“=”作为比较;又如C/C++的赋值使用“=”,而判断相等的比较则是用“==”。

      常用的微带关键词含义如下表所示:

     

    伪代码含义C/C++语言
    缩进程序块{}
    / /行注释/ /
    赋值=
    =比较运算——等于==
    比较运算——不等于!=
    比较运算——小于或等于< =
    比较运算——大于或等于>=
    for i←1 to n doFor循环for(i=1;i⇐n;i++){}
    for i←n downto 1 doFor循环for(i=n;i>=1;i–){}
    while i<n doWihle循环while(i<n){}
    do while i<nDo-While循环do {} while(i<n)
    repeat until i<nRepeat循环
    if i<n elseIf-Else语句if(i<n){} else {}
    return函数返回值return
    A[0..n-1]数组定义int A[n-1]
    A[i]引用数组A[i]
    SubFun()函数调用SubFun()
     

     

     

     

     

     

     

     

     

     

     

     

     

    伪代码(Pseudocode)是一种算法描述语言。使用伪代码的目的是为了使被描述的算法可以容易地以任何一种编程语言(Pascal,C,Java,etc)实现。因此,伪代码必须结构清晰、代码简单、可读性好,并且类似自然语言。 介于自然语言与编程语言之间。

      它以编程语言的书写形式指明算法的职能。相比于程序语言(例如Java, C++,C, Dephi 等等)它更类似自然语言。它是半角式化、不标准的语言。我们可以将整个算法运行过程的结构用接近自然语言的形式(这里,你可以使用任何一种你熟悉的文字,中文,英文 等等,关键是你把你程序的意思表达出来)描述出来. 使用伪代码, 可以帮助我们更好的表述算法, 不用拘泥于具体的实现.

      人们在用不同的编程语言实现同一个算法时意识到,他们的实现(注意:这里是实现,不是功能)很不同。尤其是对于那些熟练于不同编程语言的程序员要理解一个(用其他编程语言编写的程序的)功能时可能很难,因为程序语言的形式限制了程序员对程序关键部分的理解。这样伪代码就应运而生了。

      当考虑算法功能(而不是其语言实现)时,伪代码常常得到应用。计算机科学在教学中通常使用虚拟码,以使得所有的程序员都能理解。

      综上,简单的说,让人便于理解的代码。不依赖于语言的,用来表示程序执行过程,而不一定能编译运行的代码。在数据结构讲算法的时候用的很多。 

    语法规则

      例如,类Pascal语言的伪代码的语法规则是: 在伪代码中,每一条指令占一行(else if,例外)。指令后不跟任何符号(Pascal和C中语句要以分号结尾)。书写上的“缩进”表示程序中的分支程序结构。这种缩进风格也适用于if-then-else语句。用缩进取代传统Pascal中的begin和end语句来表示程序的块结构可以大大提高代码的清晰性;同一模块的语句有相同的缩进量,次一级模块的语句相对与其父级模块的语句缩进。

        算法的伪代码语言在某些方面可能显得不太正规,但是给我们描述算法提供了很多方便,并且可以使我们忽略算法实现中很多麻烦的细节。通常每个算法开始时都要描述它的输入和输出,而且算法中的每一行都给编上号码,在解释算法的过程中会经常使用算法步骤中的行号来指代算法的步骤。算法的伪代码描述形式上并不是非常严格,其主要特性和通常的规定如下:
            1) 算法中出现的数组、变量可以是以下类型:整数、实数、字符、位串或指针。通常这些类型可以从算法的上下文来看是清楚的,并不需要额外加以说明。
            2) 在算法中的某些指令或子任务可以用文字来叙述,例如,"设x是A中的最大项",这里A是一个数组;或者"将x插入L中",这里L是一个链表。这样做的目的是为了避免因那些与主要问题无关的细节使算法本身杂乱无章。
            3) 算术表达式可以使用通常的算术运算符(+,-,*,/,以及表示幂的^)。逻辑表达式可以使用关系运算符=,≠,<,>,≤和≥,以及逻辑运算符与(and),或(or),非(not)。
            4) 赋值语句是如下形式的语句:a<-b 。
    这里a是变量、数组项,b是算术表达式、逻辑表达式或指针表达式。语句的含义是将b的值赋给a。
            5) 若a和b都是变量、数组项,那么记号a<->b 表示a和b的内容进行交换。
            6) goto语句具有形式
                                            goto label(goto标号)
    它将导致转向具有指定标号的语句。
            7) 条件语句有以下两种形式:
                                                if c then s或者 
                                                   if c then s
                                                      else s′
    这里c是逻辑表达式,s和s′是单一的语句或者是被括在do和end之间的语句串。对于上述两种形式,假若c为真,则s被执行一次。假若c为假,则在第一种形式中,if语句的执行就完成了,而在第二种形式中,执行s′。在所有的情况下,控制就进行到了下一个语句,除非在s或s′中的goto语句使控制转向到其它地方。
             8) 有两种循环指令:while和for。
             while语句的形式是
                                                  while c do  
                                                        s
                                                      end
    这里c是逻辑表达式,而s是由一个或更多个语句组成的语句串。当c为真时,执行s。在每一次执行s之前,c都被检查一下;假若c为假,控制就进行到紧跟在while语句后面的语句。注意,当控制第一次达到while语句时,假若c为假,则s一次也不执行。 
           for语句的形式是
                                          for var init to limit by incr do
                                                            s
                                                          end
    这里var是变量,init、limit和incr都是算术表达式,而s是由一个或多个语句组成的语句串。初始时,var被赋予init的值。假若incr≥0,则只要var≤limit,就执行s并且将incr加到var上。(假若incr<0,则只要var≥limit,就执行s并且将incr加到var上)。incr的符号不能由s来该改变。
          9) exit语句可以在通常的结束条件满足之前,被用来结束while循环或者for循环的执行。exit导致转向到紧接在包含exit的(最内层)while或者for循环后面的一个语句。
         10) return用来指出一个算法执行的终点;如果算法在最后一条指令之后结束,它通常是被省略的;它被用得最多的场合是检测到不合需要的条件时。return的后面可以紧接被括在引号的信息。
          11) 算法中的注释被括在/* */之中。诸如read和output之类的各种输入或者输出也在需要时被用到。
         

    伪代码实例

      伪代码只是像流程图一样用在程序设计的初期,帮助写出程序流程。简单的程序一般都不用写流程、写思路,但是复杂的代码,最好还是把流程写下来,总体上去考虑整个功能如何实现。写完以后不仅可以用来作为以后测试,维护的基础,还可用来与他人交流。但是,如果把全部的东西写下来必定可能会让费很多时间,那么这个时候可以采用伪代码方式。比如:

      IF 九点以前 THEN

         do 私人事务;

      ELSE 9点到18点 THEN

      工作;

      ELSE

      下班;

      END IF

      这样不但可以达到文档的效果,同时可以节约时间. 更重要的是,使结构比较清晰,表达方式更加直观.

     

      下面介绍一种类Pascal语言的伪代码的语法规则。

      在伪代码中,每一条指令占一行(else if 例外,),指令后不跟任何符号(Pascal和C中语句要以分号结尾);

      书写上的“缩进”表示程序中的分支程序结构。这种缩进风格也适用于if-then-else语句。用缩进取代传统Pascal中的begin和end语句来表示程序的块结构可以大大提高代码的清晰性;同一模块的语句有相同的缩进量,次一级模块的语句相对与其父级模块的语句缩进; 

      在伪代码中,通常用连续的数字或字母来标示同一即模块中的连续语句,有时也可省略标号。

      符号△后的内容表示注释;

      在伪代码中,变量名和保留字不区分大小写,这一点和Pascal相同,与C或C++不同;

      在伪代码中,变量不需声明,但变量局部于特定过程,不能不加显示的说明就使用全局变量;

      赋值语句用符号←表示,x←exp表示将exp的值赋给x,其中x是一个变量,exp是一个与x同类型的变量或表达式(该表达式的结果与x同类型);多重赋值i←j←e是将表达式e的值赋给变量i和j,这种表示与j←e和i←e等价。

      例如:

      x←y

      x←20*(y+1)

      x←y←30

      以上语句用C分别表示为:

      x = y;

      x = 20*(y+1);

      x = y = 30;

      选择语句用if-then-else来表示,并且这种if-then-else可以嵌套,与Pascal中的if-then-else没有什么区别。

      例如:

      if (Condition1)

      then [ Block 1 ]

      else if (Condition2)

      then [ Block 2 ]

      else [ Block 3 ]

      循环语句有三种:while循环、repeat-until循环和for循环,其语法均与Pascal类似,只是用缩进代替begin - end;

     

      例如:

      1. x ← 0

      2. y ← 0

      3. z ← 0

      4. while x < N

      1. do x ← x + 1

      2. y ← x + y

      3. for t ← 0 to 10

      1. do z ← ( z + x * y ) / 100

      2. repeat

      1. y ← y + 1

      2. z ← z - y

      3. until z < 0

      4. z ← x * y

      5. y ← y / 2

       上述语句用C或C++来描述是:

      x = y = z = 0;

      while( z < N )

      {

      x ++;

      y += x;

      for( t = 0; t < 10; t++ )

      {

      z = ( z + x * y ) / 100;

      do {

      y ++;

      z -= y;

      } while( z >= 0 );
         }
      z = x * y;

      }

      y /= 2;

      数组元素的存取有数组名后跟“[下标]”表示。例如A[j]指示数组A的第j个元素。符号“ …”用来指示数组中值的范围。

      例如:

      A[1…j]表示含元素A[1], A[2], … , A[j]的子数组;

      复合数据用对象(Object)来表示,对象由属性(attribute)和域(field)构成。域的存取是由域名后接由方括号括住的对象名表示。

      例如:

      数组可被看作是一个对象,其属性有length,表示其中元素的个数,则length[A]就表示数组A中的元素的个数。在表示数组元素和对象属性时都要用方括号,一般来说从上下文可以看出其含义。

      用于表示一个数组或对象的变量被看作是指向表示数组或对象的数据的一个指针。对于某个对象x的所有域f,赋值y←x就使f[y]=f[x],更进一步,若有f[x]←3,则不仅有f[x]=3,同时有f[y]=3,换言之,在赋值y←x后,x和y指向同一个对象。

      有时,一个指针不指向任何对象,这时我们赋给他nil。

      函数和过程语法与Pascal类似。

      函数值利用 “return (函数返回值)” 语句来返回,调用方法与Pascal类似;过程用 “call 过程名”语句来调用;

      例如:

      1. x ← t + 10

      2. y ← sin(x)

      3. call CalValue(x,y)

      参数用按值传递方式传给一个过程:被调用过程接受参数的一份副本,若他对某个参数赋值,则这种变化对发出调用的过程是不可见的。当传递一个对象时,只是拷贝指向该对象的指针,而不拷贝其各个域。

    展开全文
  • 算法学习总结(2)——温故十大经典排序算法

    万次阅读 多人点赞 2019-08-29 14:57:51
    、什么是排序算法 1.1、排序定义 对序列对象根据某个关键字进行排序。 1.2、排序术语 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b...

    一、什么是排序算法

    1.1、排序定义

    对一序列对象根据某个关键字进行排序。

    1.2、排序术语

    稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
    不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
    内排序:所有排序操作都在内存中完成;
    外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
    时间复杂度: 一个算法执行所耗费的时间。
    空间复杂度运行完一个程序所需内存的大小。

    1.3、算法总结

    (注意:n指数据规模;k指“桶”的个数;In-place指占用常数内存,不占用额外内存;Out-place指占用额外内存

    1.4、算法分类

    1.5、比较和非比较的区别

    常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。冒泡排序之类的排序中,问题规模为n,又因为需要比较n次,所以平均时间复杂度为O(n²)。在归并排序、快速排序之类的排序中,问题规模通过分治法消减为logN次,所以时间复杂度平均O(nlogn)。比较排序的优势是,适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。计数排序、基数排序、桶排序则属于非比较排序。非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组arr,计算arr[i]之前有多少个元素,则唯一确定了arr[i]在排序后数组中的位置。非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度O(n)非比较排序时间复杂度底,但由于非比较排序需要占用空间来确定唯一位置。所以对数据规模和数据分布有一定的要求。

    二、冒泡排序(Bubble Sort)

    冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 

    2.1、算法描述

    • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
    • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
    • 针对所有的元素重复以上的步骤,除了最后一个;
    • 重复步骤1~3,直到排序完成。

    2.2、动图演示

    2.3、代码实现

    /**
     * 冒泡排序
     *
     * @param array
     * @return
     */
    public static int[] bubbleSort(int[] array) {
    	if (array.length == 0)
    		return array;
    	for (int i = 0; i < array.length; i++)
    		for (int j = 0; j < array.length - 1 - i; j++)
    			if (array[j + 1] < array[j]) {
    				int temp = array[j + 1];
    				array[j + 1] = array[j];
    				array[j] = temp;
    			}
    	return array;
    }

    2.4、算法分析

    最佳情况:T(n) = O(n)   最差情况:T(n) = O(n2)   平均情况:T(n) = O(n2)

    三、选择排序(Selection Sort)

    表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 

    3.1、算法描述

    n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:

    • 初始状态:无序区为R[1..n],有序区为空;
    • 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
    • n-1趟结束,数组有序化了。

    3.2、动图演示

    3.3、代码实现

    /**
     * 选择排序
     * @param array
     * @return
     */
    public static int[] selectionSort(int[] array) {
    	if (array.length == 0)
    		return array;
    	for (int i = 0; i < array.length; i++) {
    		int minIndex = i;
    		for (int j = i; j < array.length; j++) {
    			if (array[j] < array[minIndex]) //找到最小的数
    				minIndex = j; //将最小数的索引保存
    		}
    		int temp = array[minIndex];
    		array[minIndex] = array[i];
    		array[i] = temp;
    	}
    	return array;
    }

    3.4、算法分析

    最佳情况:T(n) = O(n2)  最差情况:T(n) = O(n2)  平均情况:T(n) = O(n2)

    四、插入排序(Insertion Sort)

    插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间

    4.1、算法描述

    一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

    • 从第一个元素开始,该元素可以认为已经被排序;
    • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
    • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
    • 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
    • 将新元素插入到该位置后;
    • 重复步骤2~5。

    4.2、动图演示

    4.3、代码实现

    /**
     * 插入排序
     * @param array
     * @return
     */
    public static int[] insertionSort(int[] array) {
    	if (array.length == 0)
    		return array;
    	int current;
    	for (int i = 0; i < array.length - 1; i++) {
    		current = array[i + 1];
    		int preIndex = i;
    		while (preIndex >= 0 && current < array[preIndex]) {
    			array[preIndex + 1] = array[preIndex];
    			preIndex--;
    		}
    		array[preIndex + 1] = current;
    	}
    	return array;
    }

    4.4、算法分析

    最佳情况:T(n) = O(n)   最坏情况:T(n) = O(n2)   平均情况:T(n) = O(n2)

    五、希尔排序(Shell Sort)

    希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

    5.1、算法描述

    我们来看下希尔排序的基本步骤,在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2...1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

    • 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
    • 按增量序列个数k,对序列进行k 趟排序;
    • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

    5.2、过程演示

    5.3、代码实现

    /**
     * 希尔排序
     *
     * @param array
     * @return
     */
    public static int[] ShellSort(int[] array) {
    	int len = array.length;
    	int temp, gap = len / 2;
    	while (gap > 0) {
    		for (int i = gap; i < len; i++) {
    			temp = array[i];
    			int preIndex = i - gap;
    			while (preIndex >= 0 && array[preIndex] > temp) {
    				array[preIndex + gap] = array[preIndex];
    				preIndex -= gap;
    			}
    			array[preIndex + gap] = temp;
    		}
    		gap /= 2;
    	}
    	return array;
    }

    5.4、算法分析

    最佳情况:T(n) = O(nlog2 n)  最坏情况:T(n) = O(nlog2 n)  平均情况:T(n) =O(nlog2n) 

    六、归并排序(Merge Sort)

    和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。 

    6.1、算法描述

    • 把长度为n的输入序列分成两个长度为n/2的子序列;
    • 对这两个子序列分别采用归并排序;
    • 将两个排序好的子序列合并成一个最终的排序序列。

    6.2、动图演示

    6.3、代码实现

    /**
     * 归并排序
     *
     * @param array
     * @return
     */
    public static int[] MergeSort(int[] array) {
    	if (array.length < 2) return array;
    	int mid = array.length / 2;
    	int[] left = Arrays.copyOfRange(array, 0, mid);
    	int[] right = Arrays.copyOfRange(array, mid, array.length);
    	return merge(MergeSort(left), MergeSort(right));
    }
    /**
     * 归并排序——将两段排序好的数组结合成一个排序数组
     *
     * @param left
     * @param right
     * @return
     */
    public static int[] merge(int[] left, int[] right) {
    	int[] result = new int[left.length + right.length];
    	for (int index = 0, i = 0, j = 0; index < result.length; index++) {
    		if (i >= left.length)
    			result[index] = right[j++];
    		else if (j >= right.length)
    			result[index] = left[i++];
    		else if (left[i] > right[j])
    			result[index] = right[j++];
    		else
    			result[index] = left[i++];
    	}
    	return result;
    }

    6.4、算法分析

    最佳情况:T(n) = O(n)  最差情况:T(n) = O(nlogn)  平均情况:T(n) = O(nlogn)

    七、快速排序(Quick Sort)

    快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

    7.1、算法描述

    快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

    • 从数列中挑出一个元素,称为 “基准”(pivot);
    • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
    • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

    7.2、动图演示

    7.3、代码实现

    /**
     * 快速排序方法
     * @param array
     * @param start
     * @param end
     * @return
     */
    public static int[] QuickSort(int[] array, int start, int end) {
    	if (array.length < 1 || start < 0 || end >= array.length || start > end) return null;
    	int smallIndex = partition(array, start, end);
    	if (smallIndex > start)
    		QuickSort(array, start, smallIndex - 1);
    	if (smallIndex < end)
    		QuickSort(array, smallIndex + 1, end);
    	return array;
    }
    /**
     * 快速排序算法——partition
     * @param array
     * @param start
     * @param end
     * @return
     */
    public static int partition(int[] array, int start, int end) {
    	int pivot = (int) (start + Math.random() * (end - start + 1));
    	int smallIndex = start - 1;
    	swap(array, pivot, end);
    	for (int i = start; i <= end; i++)
    		if (array[i] <= array[end]) {
    			smallIndex++;
    			if (i > smallIndex)
    				swap(array, i, smallIndex);
    		}
    	return smallIndex;
    }
    
    /**
     * 交换数组内两个元素
     * @param array
     * @param i
     * @param j
     */
    public static void swap(int[] array, int i, int j) {
    	int temp = array[i];
    	array[i] = array[j];
    	array[j] = temp;
    }

     


    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==7.4、算法分析

    最佳情况:T(n) = O(nlogn)   最差情况:T(n) = O(n2)   平均情况:T(n) = O(nlogn) 

    八、堆排序(Heap Sort)

    堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

    8.1、算法描述

    • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
    • 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
    • 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

    8.2、动图演示

     

    8.3、代码实现

    //声明全局变量,用于记录数组array的长度;
    static int len;
    /**
     * 堆排序算法
     *
     * @param array
     * @return
     */
    public static int[] HeapSort(int[] array) {
    	len = array.length;
    	if (len < 1) return array;
    	//1.构建一个最大堆
    	buildMaxHeap(array);
    	//2.循环将堆首位(最大值)与末位交换,然后在重新调整最大堆
    	while (len > 0) {
    		swap(array, 0, len - 1);
    		len--;
    		adjustHeap(array, 0);
    	}
    	return array;
    }
    /**
     * 建立最大堆
     *
     * @param array
     */
    public static void buildMaxHeap(int[] array) {
    	//从最后一个非叶子节点开始向上构造最大堆
    	for (int i = (len/2 - 1); i >= 0; i--) { //感谢 @让我发会呆 网友的提醒,此处应该为 i = (len/2 - 1) 
    		adjustHeap(array, i);
    	}
    }
    /**
     * 调整使之成为最大堆
     *
     * @param array
     * @param i
     */
    public static void adjustHeap(int[] array, int i) {
    	int maxIndex = i;
    	//如果有左子树,且左子树大于父节点,则将最大指针指向左子树
    	if (i * 2 < len && array[i * 2] > array[maxIndex])
    		maxIndex = i * 2;
    	//如果有右子树,且右子树大于父节点,则将最大指针指向右子树
    	if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex])
    		maxIndex = i * 2 + 1;
    	//如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。
    	if (maxIndex != i) {
    		swap(array, maxIndex, i);
    		adjustHeap(array, maxIndex);
    	}
    }

    8.4、算法分析

    最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(nlogn) 平均情况:T(n) = O(nlogn)

    九、计数排序(Counting Sort)

    计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。计数排序(Counting sort)是一种稳定的排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。

    9.1、算法描述

    • 找出待排序的数组中最大和最小的元素;
    • 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
    • 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
    • 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

    9.2、动图演示

    9.3、代码实现

    /**
     * 计数排序
     *
     * @param array
     * @return
     */
    public static int[] CountingSort(int[] array) {
    	if (array.length == 0) return array;
    	int bias, min = array[0], max = array[0];
    	for (int i = 1; i < array.length; i++) {
    		if (array[i] > max)
    			max = array[i];
    		if (array[i] < min)
    			min = array[i];
    	}
    	bias = 0 - min;
    	int[] bucket = new int[max - min + 1];
    	Arrays.fill(bucket, 0);
    	for (int i = 0; i < array.length; i++) {
    		bucket[array[i] + bias]++;
    	}
    	int index = 0, i = 0;
    	while (index < array.length) {
    		if (bucket[i] != 0) {
    			array[index] = i - bias;
    			bucket[i]--;
    			index++;
    		} else
    			i++;
    	}
    	return array;
    }

    9.4、算法分析

    当输入的元素是n 个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。最佳情况:T(n) = O(n+k)  最差情况:T(n) = O(n+k)  平均情况:T(n) = O(n+k)

    十、桶排序(Bucket Sort)

    桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排。

    10.1、算法描述

    • 人为设置一个BucketSize,作为每个桶所能放置多少个不同数值(例如当BucketSize==5时,该桶可以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放100个3);
    • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
    • 对每个不是空的桶进行排序,可以使用其它排序方法,也可以递归使用桶排序;
    • 从不是空的桶里把排好序的数据拼接起来。 

    注意,如果递归使用桶排序为各个桶排序,则当桶数量为1时要手动减小BucketSize增加下一循环桶的数量,否则会陷入死循环,导致内存溢出。

    10.2、图片演示

    10.3、代码实现

    /**
     * 桶排序
     * 
     * @param array
     * @param bucketSize
     * @return
     */
    public static ArrayList<Integer> BucketSort(ArrayList<Integer> array, int bucketSize) {
    	if (array == null || array.size() < 2)
    		return array;
    	int max = array.get(0), min = array.get(0);
    	// 找到最大值最小值
    	for (int i = 0; i < array.size(); i++) {
    		if (array.get(i) > max)
    			max = array.get(i);
    		if (array.get(i) < min)
    			min = array.get(i);
    	}
    	int bucketCount = (max - min) / bucketSize + 1;
    	ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount);
    	ArrayList<Integer> resultArr = new ArrayList<>();
    	for (int i = 0; i < bucketCount; i++) {
    		bucketArr.add(new ArrayList<Integer>());
    	}
    	for (int i = 0; i < array.size(); i++) {
    		bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i));
    	}
    	for (int i = 0; i < bucketCount; i++) {
    		if (bucketSize == 1) { // 如果带排序数组中有重复数字时  感谢 @见风任然是风 朋友指出错误
    			for (int j = 0; j < bucketArr.get(i).size(); j++)
    				resultArr.add(bucketArr.get(i).get(j));
    		} else {
    			if (bucketCount == 1)
    				bucketSize--;
    			ArrayList<Integer> temp = BucketSort(bucketArr.get(i), bucketSize);
    			for (int j = 0; j < temp.size(); j++)
    				resultArr.add(temp.get(j));
    		}
    	}
    	return resultArr;
    }

    10.4、算法分析

    桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。 最佳情况:T(n) = O(n+k)   最差情况:T(n) = O(n+k)   平均情况:T(n) = O(n2)  

    十一、基数排序(Radix Sort)

    基数排序也是非比较的排序算法,对每一位进行排序,从最低位开始排序,复杂度为O(kn),为数组长度,k为数组中的数的最大的位数;基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。

    11.1、算法描述

    • 取得数组中的最大数,并取得位数;
    • arr为原始数组,从最低位开始取每个位组成radix数组;
    • 对radix进行计数排序(利用计数排序适用于小范围数的特点);

    11.2、动图演示

    11.3、代码实现

    /**
     * 基数排序
     * @param array
     * @return
     */
    public static int[] RadixSort(int[] array) {
    	if (array == null || array.length < 2)
    		return array;
    	// 1.先算出最大数的位数;
    	int max = array[0];
    	for (int i = 1; i < array.length; i++) {
    		max = Math.max(max, array[i]);
    	}
    	int maxDigit = 0;
    	while (max != 0) {
    		max /= 10;
    		maxDigit++;
    	}
    	int mod = 10, div = 1;
    	ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>();
    	for (int i = 0; i < 10; i++)
    		bucketList.add(new ArrayList<Integer>());
    	for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {
    		for (int j = 0; j < array.length; j++) {
    			int num = (array[j] % mod) / div;
    			bucketList.get(num).add(array[j]);
    		}
    		int index = 0;
    		for (int j = 0; j < bucketList.size(); j++) {
    			for (int k = 0; k < bucketList.get(j).size(); k++)
    				array[index++] = bucketList.get(j).get(k);
    			bucketList.get(j).clear();
    		}
    	}
    	return array;
    }

    11.4、算法分析

    最佳情况:T(n) = O(n * k)   最差情况:T(n) = O(n * k)   平均情况:T(n) = O(n * k)。基数排序有两种方法:MSD 从高位开始进行排序 LSD 从低位开始进行排序 。基数排序 vs 计数排序 vs 桶排序。这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:

    • 基数排序:根据键值的每位数字来分配桶
    • 计数排序:每个桶只存储单一键值
    • 桶排序:每个桶存储一定范围的数值
    展开全文
  • 我应该使用哪种机器学习算法

    千次阅读 2018-12-16 14:56:05
    当面对各种各样的机器学习算法时,初学者提出的个典型问题是“我应该使用哪种算法?” 问题的答案因许多因素而异,包括: 数据的大小,质量和特性。 可用的计算时间。 任务的紧迫性。 您想要对数据做什么。 ...
  • 算法

    万次阅读 2018-02-08 00:13:09
    算法(Algorithm)是指解题方案的准确而完整的描述,是系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果算法有...
  • 用流程图描述算法

    万次阅读 多人点赞 2018-07-18 08:41:11
    流程图就是一种描述算法的图形化描述,用流程图可以清晰地描述算法的思路和过程。通过本篇的学习,你将了解到如何用流程图来描述算法。】   流程图是算法的图形化描述。俗话说:一张图胜过千言万语,用流程图...
  • 一种面向高维数据的集成聚类算法

    万次阅读 2015-08-01 21:23:24
    本文提出了一种面向高维数据的聚类集成算法。该方法针对高维数据的特点,先用分层抽样的方法结合信息增益对每个特征簇选择合适数量比较重要的特征的生成新的具代表意义的数据子集,然后用基于链接的方法对数据子集上...
  • 漫水填充算法描述

    千次阅读 2015-07-07 16:22:52
    漫水填充算法描述 1.1、种子填充算法 种子填充算法是从多边形区域内部的一点开始,由此出发找到区域内的所有像素。 种子填充算法采用的边界定义是区域边界上所有像素具有某个特定的颜色值,区域内部所有...
  • Clock_pro算法描述

    千次阅读 2013-06-18 20:09:54
    Clock_pro算法描述 、算法主要思想  Clock_pro算法与LIRS算法采用相同的原则,它使用页面的再次使用距离,而不是页面的新近访问时间,来决定替换哪个页面。当个页面被访问时,该页面的再次使用距离是上次...
  • Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。为了提升可理解性,Raft 将一致性算法...
  • A*算法算法导言

    万次阅读 2019-09-03 23:20:50
    问题场景、结点图、Dijkstra算法、BFS算法、存在障碍的情况、取长补短、A*算法
  • 数据挖掘算法——常用分类算法总结

    万次阅读 多人点赞 2019-06-17 10:55:22
    常用分类算法总结分类算法总结NBC算法LR算法SVM...分类算法的分类过程就是建立一种分类模型来描述预定的数据集或概念集,通过分析由属性描述的数据库元组来构造模型。分类的目的就是使用分类对新的数据集进行划分...
  • 一种基于陀螺仪传感器的准确计步器算法  A Gyroscope Based Accurate Pedometer Algorithm 作者:一种基于陀螺仪传感器的准确计步器算法 A Gyroscope Base
  • 八大排序算法

    万次阅读 多人点赞 2012-07-23 16:45:18
    排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,不能容纳全部的排序记录,在排序过程中需要访问外存。 我们这里说说八大排序就是内部排序。 当n较大,则...
  • 当然8只是个大概的划分,是个“仁者见仁、智者见智”的问题。 1.1 枚举算法思想 知识点讲解:光盘:视频讲解第2章枚举算法思想.avi 枚举算法思想的最大特点是,在面对任何问题时它会...
  • Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。为了提升可理解性,Raft 将一致性算法...
  • 热衷于用代码摆平一切的我几乎将这本教科书上的每种算法都实现了遍,这种重复劳动虽然意义不大,但是收获很多,特别是丢弃了多年的数学又重新回到了脑袋中,算是最大的收获吧。尽管已经毕业多年了,但是每次回顾...
  • 针对基于密度的DBSCAN聚类算法及其改进算法在全局参数Eps与MinPts选择上需人工干预以及区域查询方式过程复杂和查询易丢失对象等不足,提出一种改进的参数自适应以及区域快速查询的密度聚类算法。根据KNN分布与数学...
  • 排序算法

    万次阅读 多人点赞 2017-12-06 17:10:51
    常见排序算法一般分为以下几:  (1)非线性时间比较类排序:交换类排序(快速排序和冒泡排序)、插入类排序(简单插入排序和希尔排序)、选择类排序(简单选择排序和堆排序)、归并排序(二路归并排序和多路...
  • 迪杰斯特拉(Dijkstra)算法描述及其正确性证明

    万次阅读 多人点赞 2014-09-06 23:03:52
    Dijkstra算法描述及其正确性证明
  • 机器学习9常用算法

    千次阅读 多人点赞 2017-12-18 22:26:07
    、常见算法分类 分类方法KNN 逻辑斯蒂回归(logiscic) 决策树 朴素贝叶斯 支持向量机SVC 回归方法KNN 普通线性回归(linear) 岭回归(ridge) lasso回归 决策树 支持向量机SVR 聚类方法 K均值算法(K-means) (无...
  • SIFT算法详解

    万次阅读 多人点赞 2012-04-28 21:40:36
    尺度不变特征变换匹配算法详解 Scale Invariant Feature Transform(SIFT) Just For Fun zdd zddmail@gmail.com 对于初学者,从David G.Lowe的论文到实现,有许多鸿沟,本文帮你跨越。 1、SIFT综述 尺度不变...
  • 算法算法实现

    千次阅读 多人点赞 2017-02-14 17:59:55
    算法是一切程序设计的灵魂和基础。 很多开发者都知道“程序=数据结构+算法”这个著名的公式,简单点来说,我更喜欢表单... 算法是解决实际问题的一种精确描述方法、算法是对特定问题的求解步骤的一种精确描述方法。
  • 操作系统调度算法

    千次阅读 2018-10-23 16:53:38
    先来先服务(FCFS)调度算法一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们...
  • Paxos 算法详解(

    万次阅读 多人点赞 2020-05-24 13:52:59
    提到分布式算法,就不得提 Paxos 算法,在过去几十年里,它基本上是分布式共识的代 名词,因为当前最常用的批共识算法都是基于它改进的。比如,Fast Paxos 算法、 Cheap Paxos 算法、Raft 算法、ZAB 协议等等。 ...
  • 数据结构:是指相互之间存在一种或多种特定关系的数据元素的集合用计算机存储、组织数据的方式。数据结构分别为逻辑结构、(存储)物理结构和数据的运算三个部分。 为什么要学数据结构? 首先,因为数据结构作为...
  • 优化算法——遗传算法

    万次阅读 多人点赞 2015-05-10 17:09:28
    与遗传算法的第次接触 遗传算法的基本概念 基本定义 遗传算法的基本流程 遗传算法过程中的具体操作 参数的编码 二进制编码 Gray编码 实数编码 有序编码 初始群体的设定 适应度函数的计算 遗传操作设计 选择...
  • 元启发式算法:蝙蝠算法BA

    千次阅读 多人点赞 2019-06-18 14:51:27
    目录 、算法 1. 定义 2. 步骤 3. 特点 二、蝙蝠 1.描述 2.先进技能-声纳 1) 回声定位-Acoustics of Echolocation 2) 行为参数化分析 ... 3....3.算法模型建立之算法描述 4.算法方程 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 374,056
精华内容 149,622
关键字:

哪一种不能用于算法的描述