数据结构和算法-入门篇

1,079 阅读10分钟

目录:

引言
数据结构的概念
数据结构基本术语
算法的概念
算法的特性
算法的概念

一:引言:

数据结构和算法都是当年大学我们学习的课程了,当是学的时候对这些东西还是很懵,因为觉得很抽象,没有实际操作经验。而如今我们作为一名程序猿,每天时时刻刻都和数据打交道,对数据结构也越来越有感觉,也通过一些算法解决了不少程序的问题,了解学习它的重要性,那么从今天起,我们就一起系统的回顾下数据结构和算法的内容,先从基本的数据结构开始:

二:数据结构的概念

(一)什么是数据结构

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。

(二)学习数据结构的意义

了解数据结构,我们可以合理布局自己的代码,突破程序性能瓶颈,可以使我们的程序更健壮更高效。

(三)常见的数据结构

(1)集合结构

集合结构的里面的元素关系是孤立的
(2)线性结构
线性结构里面的元素关系:一对一。常用的线性结构有:数组、线性表、栈、队列、字符串等等。
(3)树形结构


树形结构中的数据元素的关系:一对多。常见的树形结构: 二叉树、B树、huffman等。
(4)图形结构

图形结构中的数据元素的关系:多对多。常见的图形结构:邻近矩阵。

(四)数据结构的物理结构

(一)物理结构:
是指数据的逻辑结构在计算机中的存储形式。也就是说,无论数据的逻辑结构是怎么样的,最终还是要存放在内存中,要遵循计算机的物理结构。存储结构有两种:
(1)顺序存储结构:
顺序存储结构:是把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的。 常见的有数组的顺序存储,那么他的数据结构和物理结构就是一致的,在内存中,数据彼此相邻,一个接一个的排放在一起。如图所示:

(2)链式存储结构

链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。他最大的特点是不需要提前开辟一块连续的空间,在插入数据的时候比顺序存储要简单。但是缺点是,查找元素比较复杂。

三:数据结构基本术语

(1)数据:

所有能输入到计算机中,且能被计算机处理的符号的集合,它是计算机能操作的对象的总称,如数字、文字和特定符号等;例如:学生基本情况表中每一个学生的信息,包含学号,姓名,性别,...,我们称之为记录,整张表信息我们称为数据。

(2)、数据对象

是性质相同的数据元素的集合,是数据的子集。例如:三年二班所有的学生集合。

(3)数据元素

它是数据的基本单位,如学生情况表中每一记录就是一个数据元素,在有些情况下数据元素也称为元素、节点、顶点等。一个数据元素可以由若干个数据项组成,数据项是具有独立含义的最小数据单位,也称为字段或域。例如:三年二班每一个学生。

(3)数据项

它相当于每个数据存储的内容,比如说,三年二班学生的年龄,性别等。 是性质相同的数据元素的集合,是数据的子集。

他们的关系如下图所示:

四:算法的概念

算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。

四:算法的概念

一个算法应该具有以下五个重要的特征:

(1)有穷性(Finiteness)

算法的有穷性是指算法必须能在执行有限个步骤之后终止;

(2)确切性(Definiteness)

算法的每一步骤必须有确切的定义;

(3)输入项(Input)

一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;

(4)输出项(Output)

一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;

(5)可行性(Effectiveness)

算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)。

四:算法的评估

如何评估一个算法的优劣?
算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)。

(1)时间复杂度

算法的时间复杂度是指执行算法所需要的计算工作量。一般来说,计算机算法是问题规模n 的函数f(n),算法的时间复杂度也因此记做。 T(n)=Ο(f(n)) 因此,问题的规模n 越大,算法执行的时间的增长率与f(n) 的增长率正相关,称作渐进时间复杂度(Asymptotic Time Complexity) 。

(2)空间复杂度

算法的空间复杂度是指算法需要消耗的内存空间。其计算和表示方法与时间复杂度类似,一般都用复杂度的渐近性来表示。同时间复杂度相比,空间复杂度的分析要简单得多。

(3)正确性

算法的正确性是评价一个算法优劣的最重要的标准。

(4)可读性

算法的可读性是指一个算法可供人们阅读的容易程度。

(5)健壮性

健壮性是指一个算法对不合理数据输入的反应能力和处理能力,也称为容错性。

五:算法时间复杂度和空间复杂度的计算

(一)通常我们会使用大O表示法来衡量时间和空间的复杂度

大O表示法的定义:
在进行算法分析时,语句的总执行次数T(n)是关于问题规模n的函数,进而分析T(n)随着n变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,即为T(n) = O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。 大写O( )来体现算法时间复杂度的记法,我们称之为大O记法。

其规则如下:
(1). 用常数1取代运行时间中所有常数 3->1 O(1) (2). 在修改运行次数函数中,只保留最高阶项 n^3+2n^2+5 -> O(n^3) (3). 如果在最高阶存在且不等于1,则去除这个项目相乘的常数 2n^3 -> n^3

(二)常见时间复杂度术语以及使用大O的表示方法

(1).常数阶

/* 1. 常数阶时间复杂度计算 O(1) */
//1+1+1 = 3 O(1)
void testSum1(int n){
    int sum = 0;                //执行1次
    sum = (1+n)*n/2;            //执行1次
    printf("testSum1:%d\n",sum);//执行1次
}

结果是:1+1+1 = 3 我们用1来取代时间中的所有常数,其结果为O(1)
(2).线性阶

//x=x+1; 执行n次 O(n)
void add2(int x,int n){
    for (int i = 0; i < n; i++) {
        x = x+1;
    }
}

上述循环体内部的代码执行了n次,所以用大O表示为O(n)
(2).对数阶

/*2的x次方等于n x = log2n  ->O(logn)*/
void testA(int n){
    int count = 1;         //执行1次
    //n = 10
    while (count < n) {
        count = count * 2;
    }
}

while循环体执行的次数为x次,2x次方等于n,x= log2n,去掉2n中的常数,最终表示为O(logn)
(3).平方阶

//x=x+1; 执行n*n次 ->O(n^2)
void add3(int x,int n){
    for (int i = 0; i< n; i++) {
        for (int j = 0; j < n ; j++) {
            x=x+1;
        }
    }
}

我们可以看到函数内部有两个循环,一共执行了n*n次,所以表示为O(n^2)
(4).立方阶

void testB(int n){
    int sum = 1;                         //执行1次
    for (int i = 0; i < n; i++) {        //执行n次
        for (int j = 0 ; j < n; j++) {   //执行n*n次
            for (int k = 0; k < n; k++) {//执行n*n*n次
                sum = sum * 2;          //执行n*n*n次
            }
        }
    }
}

函数内部三层for循环,总共执行n^3次,所以表示为O(n^3)
(5).指数阶
顾名思义,就是会在内部嵌套多次循环,除非n很小,要不我们一般在算法中不考虑,会产生巨大的消耗!

(三)空间复杂度

算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记做: S(n) = n(f(n)),其中n为问题的规模,f(n)为语句关于n所占存储空间的函数。 程序空间复杂度的影响因素有:

  • 寄存本身的指令
  • 常数
  • 变量
  • 输入
  • 对数据进行操作的辅助空间
    而我们在考量算法的空间复杂度,主要考虑算法执行时所需要的辅助空间.例如下面的代码:
 int n = 5;
    int a[10] = {1,2,3,4,5,6,7,8,9,10};
    
    //算法实现(1)
    int temp;
    for(int i = 0; i < n/2 ; i++){
        temp = a[i];
        a[i] = a[n-i-1];
        a[n-i-1] = temp;
    }

    for(int i = 0;i < 10;i++)
    {
        printf("%d\n",a[i]);

    }

上面面算法仅仅通过使用了一个临时变量temp的空间,与问题规模n大小无关,所以其空间复杂度为S(1)

 int b[10] = {0};
    for(int i = 0; i < n;i++){
        b[i] = a[n-i-1];
    }
    for(int i = 0; i < n; i++){
        a[i] = b[i];
    }
    for(int i = 0;i < 10;i++)
    {
        printf("%d\n",a[i]);
        
    }

而在上述算法中,我们需要开辟一个int行的数组b其大小为n,所以空间复杂度为S(n)

六:总结

今天的文章就介绍到这里,我们对数据结构和算法有了一个初步的认识,下个篇章我会给大家介绍线性表的顺序存储链式存储,敬请期待。