1. 数据结构

数据:程序的操作对象,用于描述客观事物。数据不仅仅包括整型、实型等数值类型,还包括字符、声音、图像和视频等非数值类型。
数据具备两个特性:

  • 可以输入到计算机中。
  • 可以被计算机处理。

数据元素:组成数据的有一定意义的基本单位。比如在人类中,人就是数据元素,在畜类中,牛、羊、马、鸡等就是数据元素。
数据项:一个数据元素可以由若干个数据项组成。比如人的名字、性别、年龄等都是数据项。
数据项是数据不可分割的最小数据单位。
数据对象:是性质相同的数据元素的集合,是数据的子集。类似于数组。
数据结构:是相互之间存在一种或多种特定关系的数据元素的集合,也可以理解为数据对象中数据元素之间的关系。
未命名文件.png

//声明一个结构体类型
struct Person{     //一种数据结构
    char *name;     //数据项--名字
    char *title;    //数据项--职称
    int  age;       //数据项--年龄
};

int main(int argc, const char * argv[]) {
   
    struct Person person;     //数据元素;
    struct Person pArray[10]; //数据对象;
    
    person.age = 18;       //数据项
    person.name = "Daneil";    //数据项
    person.title = "developer";  //数据项
    
    printf("姓名:%s\n",person.name);
    printf("年龄:%d\n",person.age);
    printf("职称:%s\n",person.title);
    return 0;
}

2. 逻辑结构和物理结构

2.1 逻辑结构

逻辑结构:是指数据对象中数据元素之间的相互关系。逻辑结构分为以下四种:
1. 集合结构
集合结构:集合中的数据元素除了同属于一个集合外,他们之间就没有其他关系了。如下图:
Screen Shot 2020-04-04 at 10.34.33 PM.png
2. 线性结构
线性结构:线性结构中的数据元素之间是一对一的关系。如下图:
Screen Shot 2020-04-04 at 10.39.36 PM.png
3. 树形结构
树形结构:树形结构中的数据元素之间存在一对多的层次关系。如下图:
Screen Shot 2020-04-04 at 10.45.09 PM.png
4. 图形结构
图形结构:图形结构的数据元素是多对多的关系。如下图:
Screen Shot 2020-04-04 at 10.51.32 PM.png

2.2 物理结构

物理结构是指数据的逻辑结构在计算机中的存储形式,数据元素的存储形式分为两种:顺序存储和链式存储。
1. 顺序存储结构
顺序存储结构是把数据元素存放在地址连续的存储单元里,其数据元素间的逻辑关系和物理关系是一致的。如下图:
Screen Shot 2020-04-04 at 11.03.00 PM.png
2. 链式存储结构
链式存储结构是把数据元素存储在任意存储单元里,这组存储单元可以是连续的,也可以是不连续的。如下图:
Screen Shot 2020-04-04 at 11.09.37 PM.png
逻辑结构是面向问题的,而物理结构是面向计算机的,其基本的目标就是将数据及其逻辑关系存储到计算机内存中。

3. 算法

3.1 算法的定义

算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。通俗的讲,算法就是描述解决问题的方法。

3.2 算法的特性

1. 输入输出:算法具有零个或多个输入,至少有一个或多个输出。
2. 有穷性:算法在执行完有限步骤之后,自动结束而不会出现无限循环,并且每个步骤在可接受的时间内完成。
3. 确定性:算法的每一步都具有确定的含义,不会出现二义性。
4. 可行性:算法的每一步都必须是可行的,每一步都能通过执行有限的次数完成。

3.3 算法的设计要求

1. 正确性:算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求,能够得到问题的正确答案。
2. 可读性:算法设计的另一个目的是为了便于阅读、理解和交流。
3. 健壮性:当输入数据不合法时,算法也能够做出相应的处理,而不是产生异常或莫名其妙的结果。
4. 时间效率高和存储量低:设计算法应该满足时间效率高和低存储量的需求。

3.4 算法的时间复杂度

3.4.1 大O阶表示法

大O表示法规则:

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

3.4.2 常用算法复杂度

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+1+1+1+1 = 7 O(1)
void testSum2(int n){
    int sum = 0;                //执行1次
    sum = (1+n)*n/2;            //执行1次
    sum = (1+n)*n/2;            //执行1次
    sum = (1+n)*n/2;            //执行1次
    sum = (1+n)*n/2;            //执行1次
    sum = (1+n)*n/2;            //执行1次
    printf("testSum2:%d\n",sum);//执行1次
}
//x=x+1; 执行1次
void add(int x){
    x = x+1;
}

2. 线性阶

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

//1+(n+1)+n+1 = 3+2n -> O(n)
void testSum3(int n){
    int i,sum = 0;               //执行1次
    for (i = 1; i <= n; i++) {   //执行n+1次
        sum += i;                //执行n次
    }
    printf("testSum3:%d\n",sum);  //执行1次
}

3. 对数阶

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

4. 平方阶

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

5. 立方阶

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次
            }
        }
    }
}

Screen Shot 2020-04-05 at 10.37.47 PM.png
常用的时间复杂度所耗费的时间从小到大依次是:
Screen Shot 2020-04-05 at 10.48.26 PM.png

3.5 最坏情况和平均情况

最坏情况运行时间是一种保证,那就是运行时间将不会再坏了,通常,除非特别指定,我们提到的运行时间都是最坏情况的运行时间。
平均运行时间是所有情况中最有意义的,因为它是期望的运行时间。

3.6 算法的空间复杂度

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

  1. 寄存本身的指令
  2. 常数
  3. 变量
  4. 输入
  5. 对数据进行操作的辅助空间

在考量算法的空间复杂度,主要考虑算法执行时所需要的辅助空间。

// 问题: 数组逆序,将一维数组a中的n个数逆序存放在原数组中.`
int main(int argc, const char * argv[]) {
  
   int n = 10;
   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;
   }
   
   //算法实现(2)
   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];
   }
   return 0;
}

很显然,算法实现(2)的空间复杂度大于算法实现(1)的空间复杂度。

4. 总结

数据结构与算法概述.png


Daniel_Coder
7 声望1 粉丝