数据结构与算法入门C语言 (一) 概述

数据结构与算法入门C语言

  • 数据结构概述
    • 一、数据结构定义
    • 二、算法定义
    • 三、 衡量算法的标准
    • 四、 数据结构的地位
    • 五、预备知识
      • 所有的指针变量只占4个字节,用第一个字节的地址表示整个变量的地址
      • 如何通过函数修改实参的值
      • 结构体使用概述
      • 动态内存的分配和释放

笔记来源于郝斌老师数据结构与算法视频,博主学习后 纯手打,侵删。

数据结构概述

一、数据结构定义

如何把现实中大量而复杂的问题以特定的数据类型特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找、删除某个元素,对所有元素排序)而执行的相应操作,这个操作也叫算法。
特定的数据类型: 个体元素
特定的存储结构:个体和个体之间的关系

数据结构=个体+个体的关系
算法=对存储数据的操作

二、算法定义

算法:解题的方法和步骤

三、 衡量算法的标准

1.时间复杂度
大概程序执行的次数,而非执行时间。
2.空间复杂度
算法执行过程中大概所占用的最大内存。
3.难易程度
4.健壮性

四、 数据结构的地位

数据结构是软件心中最核心的课程

程序=数据结构+数据的操作+可以被计算机执行的语言

1.什么是堆?什么是栈?
很多人以为是内存里的2块儿空间,一块儿叫堆,一块叫栈,其实不对。实际上是指内存分配的方法不同的2种方式。如果是压栈出栈的方式分配和释放的内存就叫栈内存。如果是以堆排序分配的内存就叫堆内存。

**注意:数据结构里是没有堆这个概念的,堆是什么?是分配内存的一种方式,而不是存储数据的一种结构。

2.函数调用,如何调用呢?

压栈和出栈。

  • 按时间存储的东西得有个顺序吧,按顺序存储的结构就是队列
  • 编译原理得学树。
  • 数据库就是数据结构得简化版,讨论得问题差不多,解决得问题更狭窄了
  • 程序=数据的存储+数据的操作+可被计算机执行的语言。
  • 数据结构很重要,难度大,学完很难做出东西来,学它是练内功。

五、预备知识

1.指针

  • 指针的重要性
    指针是C语言的灵魂

  • 定义

    • 地址
      内存单元的编号
      从0开始的非负整数
      范围:0-FFFFFFFF【0~4G-1】
  • 指针
    指针就是地址,地址就是指针。
    指针的本质是一个操作受限的非负整数

  • 指针变量
    指针变量是存放内存单元地址(编号)的变量

  • 指针的分类
    1.基本类型的指针

#include 

int main(void)
{
     
    int *p; //p是个变量名字,int * 表示该P变量只能存储int类型变量的地址
    int i = 10;
    int j;

    //(1)❌此时p还没有被赋值,里面是个垃圾值,这个垃圾值很可能正好是某个变量的地址
    //所以应该在使用 *p 之前给p赋值地址:p = &i;
    j = *p;

    //(2)❌ 给垃圾值地址的变量赋值一个新值,垃圾值应不受你控制的,随意改内存很危险 。
    //若是 先写了p = &i; 则下面等价于 i = i;
    *p = i;
}
#include 

change_arr(int *p, int len)
{
     
    p[0] = -1; //p[0] = *(p + 0) = *p
    p[2] = -9; //p[2] = *(p + 2) = *(a + 2) =     	a[2]
}

int main(void)
{
     
    int a[5] = {
     1, 2, 3, 4, 5};
    printf("%d\n", a);
    printf("%d\n", a + 1); //#####70      int型占4个字节
    printf("%d\n", a + 2); //#####74
    printf("%d\n", a + 3); //#####78
    a + n = addr(a) + n * sizeof(typeof(a))
                              change_arr(a, 5);
    printf("%d\n", a[0]); // -1
    printf("%d\n", a[2]); //-9
}

所有的指针变量只占4个字节,用第一个字节的地址表示整个变量的地址

#include 
int main(void)
{
     
    doueble arr[5] = {
     1.1, 2.2, 3.3, 4.4, 5.5};
    double *p = &arr[0];
    printf("%p\n", p); // %p以16进制的形式输出地址(012FF5C)

    p = &arr[1];
    printf("%p\n", p); // %p以16进制的形式输出地址(012FF64)64-5C=8字节
    //int 类型的话差4个,因为int 是4字节长度
    return 0;
}

如何通过函数修改实参的值

无论要通过函数改写什么类型的值,只需要传递它的地址就行了。

#include 
int main(void)
{
     
    int i = 10;
    int *p = &i; // int * p; p = & i;
    printf("%p\n", p);
    f(&p);
    printf("%p\n", p);
    return 0;
}

void f(int *q)
{
     
    *q = (int *)0 x FFFFFFFF;
}

结构体使用概述

  • 1.为什么需要结构体

为了表示一些复杂的数据,而普通的基本类型变量无法满足需求。

  • 2.什么叫结构体

结构体是用户根据实际需要自己定义的复合数据类型。结构体给人感觉模拟事物模拟的不彻底(没有办法)但是仍然有好处;以算法为核心,血手结构。算法在面向过程的结构语言里最好。而面向对象的语言算法就不是其核心了。

  • 3.如何使用结构体

    两种方式:
               struct Student st  = {1000,"LeeQiang",20};
               struct Student  * pst  = &st;
    
#include<stdio.h> struct Student
{
     
    int sid;
    char name[20];
    int age;
};
int min(void)
{
     
    struct Student st = {
     1,“LeeQiang” , 20};
    printf("%d %s %d\n", st.sid, st.name, st.age);
    //第一种访问方式
    st.sid = 99;
    // st.name = "LeeSi" // ❌error
    strcpy(st.name, "LeeSi");
    st.age = 28;
    printf("%d %s %d\n", st.sid, st.name, st.age);

    struct Student *pst; //第二种方式最常用
    pst = &st;
    pst->age = 22; // pst->age等价于 (*pst).age 而(*pst).age=st.age 所以pst->age = st.age;
} //这种方式好,因为只需要4个字节的参数变量

void printStudent2(struct Student *st)
{
     
    printf("%d %s %d\n", st.sid, st.name, st.age);
}

结构体的2种变量方式
1.st.age
2.pst ->age //pst 所指向的结构体变量中的age这个成员;

注意事项
1.结构体变量不能相互之间加减乘除,但能相互赋值。
2.普通结构体变量和结构体变量指针变量作为函数传参问题。

动态内存的分配和释放

假设构造一个 int 型的一维数组
int len; int * pArr = (int *)malloc(sizeof(int *)len)

  • 本语句分配了两块内存,一块内存是动态分配的的总共len个字节;另一个是静态分配的,是pArr变量本身所占的内存总共4个字节。
  • malloc 只有一个 int 型的 形参,表示要求系统分配的字节数。
  • malloc 函数的功能是请求系统分配 len个字节的内存空间,如果分配成功,则返回第一个字节。如果返回不成功,则返回NULL。
  • malloc 函数能且只能返回第一个字节的地址,所以我们需要把这个无任何意义的第一个字节的地址(俗称干地址)转化为一个有实际意义的地址,因此,malloc函数前面必须加强制类型转换(数据类型*),表示把这个无实际意义的第一个字节的地址转化为相应类型的地址。
  • free(*pArr)表示把所指向的内存释放掉。pArr本身的内存是静态的,不能由程序员手动释放,只能在pArr变量所在的函数运行终止时有系统自动释放。
  • 跨函数使用内存
    • 静态内存不可跨函数使用。

    • 静态内存在函数执行期间可以被其他函数所使用。

    • 静态内存在函数执行完毕之后就不能在被其他函数使用。

    • 动态内存可以跨函数使用

    • 动态内存在函数执行完毕之后仍可以被其他函数使用,除非使用free()方法动态分配的内存

#include 
#include 
int main(void)
{
     
    int a[5] = {
     4, 10, 2, 8, 6};
    int len;
    printf("请输入需要分配的数组长度:len = ");
    scanf("%d", &len);
    int *pArr = (int *)malloc(szieof(int *) len);
    //* pArr = 4; //类似于 a[0] = 4;
    // pArr[1] = 10; //类似于 a[1] = 10;
    // printf("%d%d\n",*pArr,pArr[1]);
    //我们可以把pARR 当做一个普通数组来使用
    for (int i = 0; i < len; ++i)
        scanf("%d", &pArr[i]);
    for (i = 0; i < len; ++i;)
        printf("%d\n", *(pArr + i));
    free(pArr); // 把pArr所代表的动态分配的20个字节的内存释放
    return 0}
#include 
struct Student
{
     
    int sid;
    int age;
};

struct Student *CreateStudent(void);
void ShowStudent(struct Student *);

int main(void)
{
     
    struct Student *pS;
    pS = CreateStudent();
    ShowStudent(ps);
    return 0;
}

void ShowStudent(struct Student *pS)
{
     
    printf("%d%d\n", pst->sid, pst->age);
}

struct Student *CreateStudent(void)
{
     
    struct Student *p = (struct Student *)malloc(sizeof(struct Student));
    p->sid = 99;
    p->age = 88;
    return p;
}

你可能感兴趣的:(数据结构与算法入门笔记,算法,数据结构,c算法,算法导论)