数据结构介绍

一、数据结构的概述

1.1 定义  

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

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

1.2 算法

1.2.1 什么是算法

算法:解题的方法和步骤

 

算法是特定问题求解步骤的描述,在计算机中变现为指令的有限序列,算法是独立存在的一种解决问题的方法和思想

对于算法而言,重要的不是语言,而是思想。

1.2.2 衡量算法的标准

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

1.2.3 数据结构的地位

数据结构是软件中最核心的课程
程序 = 数据的存储+数据的操作+可以被计算机执行的语言(已经提供)

1.3 数据结构和算法的区别

  数据结构主要讲述的是数据的组织形式, 换句话说, 就是要怎样吧这些数据存储起来, 所以有 数组,链表,堆,栈,树,图,这些是数据结构的重点。
  算法则注重的是思想,比如链表中的元素怎么排序,怎么在当前的存储解耦股中找到最大的数和最小的数等等。说白了就是解决问题的思想,所以才会有排序算法,分治思想,贪心思想,动态规划这样的算法。

二、预备知识

2.1 指针

2.1.1 指针的重要性

指针是 C语言的灵魂

2.1.2 定义

1.地址:内存单元的编号

2.指针:指针就是地址,地址就是指针;
指针变量是存放内存单元地址变量;
指针的本质是一个操作受限的非负整数;

2.1.3 分类

1. 基本类型的指针
2. 指针与数组的关系

2.1.4 代码

// 1. 指针赋值,将i的值通过指针变量p赋值给j
#include <stdio.h> 
int main(void) 
{
    int *p;   // p是个变量名字, int * 表示该p变量只能存储int类型变量的地址 
    int i = 10;
    int j;
    p = &i;
    j = *p;   // *p 就是i , 等价于 j = i
    printf("%d\n", j); 
    return 0;
} 
// 2. 函数调用, 修改主函数的值

#include <stdio.h>
void f(int * p) //p是个变量名字, int * 表示该p变量只能存储int类型变量的地址
{
    *p = 100;
}  

int main(void) 
{
    int i = 9;
    f(&i);
    printf("i = %d\n", i); 
    return 0;
} 
// 3. 数组取值 

#include <stdio.h>

int main(void)
{
    int a[5] = {1,2,3,4,5};
    // a[3] == *(3+a);
    printf("%p\n", a+1);
    printf("%p\n", a+2);
    printf("%p\n", *a+3); // *a + 3 等价于 a[0] + 3
    return 0;
 } 
// 4. 函数修改实参的值 

#include <stdio.h>

void Show_Array(int *p, int len)
{
     p[2] = -1;   // p[0]==*p,  p[2] == *(p + 2) == *(a + 2)
//     p[i]就是主函数的a[i] 

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

int main(void)
{
    int a[5] = {1,2,3,4,5};
    Show_Array(a, 5); // a等价于&a[0], $a[0]本身就是int *类型 
    printf("%d\n", a[2]); 
    return 0;
 } 
// 5. 指向指针的指针, 修改指针变量指向地址的地址 

# include <stdio.h>
void f( int **p);

int main(void)
{
    int i =9;
    int *p = &i;   // 等价于 int *p;  p = &i;
    printf("%p\n", &i);
    printf("%p\n", p);  // %p为输出地址
    f(&p); 
    printf("%p\n", &i);
    printf("%p\n", p);
} 

void f(int **q)
{
    *q = (int *)0xFFFFFFFF;  // *q == p
} 

2.2 结构体

2.2.1 为什么出现结构体

为了表示一些复杂的数据,而普通的基本类型变量无法满足要求;结构体是用户根据实际需要自己定义的复合数类型;

2.2.2 如何使用结构域体

// 1. 结构体 使用与赋值 第一种方式

#include <stdio.h>
#include <string.h>
struct Student
{
    int sid;
    char name[200];
    int age;
};  // 分号不能省略 

int main(void)
{    
    struct Student st = {1000, "wangyong", 20};
    printf("%d, %s, %d\n", st.sid, st.name, st.age);
    
    st.sid = 99;
    //st.name = "wangyong" // error
    strcpy(st.name, "wangyong123");
    st.age = 25;
    printf("%d, %s, %d\n", st.sid, st.name, st.age);
    //printf("%d, %s, %d\n", st); error
    return 0;
 } 
// 2. 结构体 使用与赋值 第二种方式

#include <stdio.h>

struct Student
{
    int sid;
    char name[200];
    int age;
};  // 分号不能省略 

int main(void)
{
    struct Student st = {1000, "wangyong", 20};
    
    st.sid = 99; // 第一种方式
    struct Student * pst;
    pst = &st;
    pst->sid = 99;  //表示pst所指向的结构体变量的sid这个成员。 pst->sid 等价于 (*pst).sid, 而(*pst).sid等价于st.sid,所以 pst->sid 等价于 st.sid 
    return 0;
} 
// 3. 结构体 通过函数给解耦固体赋值

# include <stdio.h>
# include <string.h>
void f(struct Student * pst);
void g(struct Student st);
void g2(struct Student *pst);
struct Student {
    int sid;
    char name[200];
    int age;
}; 

int main(void)
{
    struct Student st;  // 已经为st分配了内存
    f(&st);
    printf("%d, %s, %d\n", st.sid, st.name, st.age);
    g(st);
    g2(&st);
    return 0;

}

void f(struct Student * pst)
{
    (*pst).sid = 99;
    strcpy(pst->name, "wangyong"); 
    pst->age = 22;
}

// 这种方式耗时间, 耗内存
void g(struct Student st)
{
    printf("%d, %s, %d\n", st.sid, st.name, st.age);
}

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

2.2.3 注意事项

结构体变量不能加减乘除, 但是可以赋值
普通结构体变量结构体指针作为函数传参的问题

2.3 动态内存的分配和释放

// 1. 动态分配内存

# include <stdio.h>
#include <malloc.h>

int main (void)
{
    int a[5] = {4,10,2,8,6}
    printf("请输入需要分配的数组的长度:len=");
    int len;
    scanf("%d", &len);
    int *pArr = (int *)malloc(sizeof(int) * len);
   
    //    *pArr = 4;  // 类似于 a[0] = 4;
    //    pArr[1] = 10; // 类似于 a[1] = 10;
    //    printf("%d, %d", *pArr, pArr[1]); 

    // 可以把pArr当做一个普通数组来使用
    for (int i =0; i<len; i++) 
    {
        scanf("%d", &pArr[i]);
    } 
    for (int i =0; i<len; i++) 
    {
        printf("%d\n", pArr[i]);
    } 
    free(pArr); // 把pArr所代表的的动态分配的20个字节内存给释放 
    return 0;
 }
// 2.      跨函数使用内存

#include <stdio.h>
#include <malloc.h>
struct Student 
{
    int sid;
    int age;    
}; 

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

void showStudent(struct Student *pst)
{
    printf("%d, %d\n",pst->sid, pst->age);
}
int main(void)
{
    struct Student * ps;
    ps = CreateStudent();
    showStudent(ps);
    return 0;
 } 

 

posted on 2020-04-21 23:51  软饭攻城狮  阅读(354)  评论(0编辑  收藏  举报

导航