数据结构(学习中)

预先知识:(C语言)

1、指针

地址:内存单元的编号

指针:指针就是地址,地址就是指针

           指针变量:就是一个变量,这个变量存储了一个非负整数,即存储了内存单元的编号的变量,所有指针变量只占4个字节(32位机器来说)

基本类型的指针

指针和数组

#include <stdio.h>

int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};
    
    int *p = a; 
    /* int *p; p = a 
        数组名也是一个变量,一个指向数组第一个元素的指针变量,存放了第一个元素的内存空间地址
        将数组名给指针变量p,意味着p指向数组的第一个元素(真正指向第一个元素的第一个字节单元,因为这是个整型指针变量,所以可以理解为指向第一个元素)
        a = p => 第一个元素的内存空间地址
        所以 *p就是第一个元素的内存空间 *(p+1) 是第二个元素的内存空间,因为p = a,所以a[0] = p[0]
    */

    printf("p = %p\n", p);
    
    printf("*p = %d\n", *p);
    
    printf("p[0] = %d\n", p[0]);
    
    printf("*(p+1) = %d\n", *(p+1));
    
    printf("*p+1 = %d\n", (*p + 1) );
    
    return 0;
}

传递给被调函数的实参,在被调函数中修改后,在主调函数中能响应到 ,这里可以说是主调函数中的静态内存在被调函数中进行了修改,关键在于:在主调函数中的实参是否想要在被调函数中被修改,如果想被修改,就需要传实参地址给被调函数

#include <stdio.h>

void func(int *);

int main(void)
{
    int p = 1;
    func(&p);   
    /* 主调函数中要使用取地址符&来获取整型p(对4个字节的内存空间命名为p,没有其他实际意义)的第一个字节地址,传递到func中 
    */
    printf("p = %d\n",p);
}

void func(int *pt)
{
    *pt = 10; 
    /* pt是一个整型指针变量,虽然它指向p的第一个字节地址 ,但对剩余的3个字节也有访问权限, 所以*pt应该是代表了4个字节单元空间 即*pt = p,这样对内存进行操作了,主调函数中的变量自然就会被改变
    */
}

2、结构体

1、定义的末位分号不能省

2、数据类型是 【struct 名称】整体是一个数据类型

3、‘.’成员操作符,结构体变量.成员;‘->’成员操作符,结构体指针变量->成员

4、与基本数据类型不同,不能算术运算和逻辑运算,允许赋值操作

#include <stdio.h>

struct ST1
{
    char name[200] ;
    int age;
};  //末位的;不能省略

int main(void)
{
    struct ST1 st = {"zhangsan", 20};
    struct ST1 st2 = {"wangwu", 22};
    struct ST1 *sp ;
    sp = &st;

    //定义结构体变量,使用'.'成员操作符获取结构体的成员变量值
    printf("name = %s\n", st.name);
    printf("age = %d\n", st.age);

    //定义结构体指针变量,使用'->'成员操作符获取结构体的成员变量值
    printf("name = %s\n", sp->name);
    printf("age = %d\n", sp->age);

    /*   
        sp = &st,sp保存了结构体变量st的地址,这个地址是结构体的第一个字节的内存空间的地址
        *sp 就是结构体的字节空间(准确来讲是结构体第一个字节空间,因为sp是这个结构体的指针,所以后面空间也能被方访问),所以 *sp = st  ===> (*sp).name = st.name  ,为了书写方便,
        就把(*sp).name 替换成了 sp->name,这个是C语言开发人员人为约定的。
    */

    // st + st2 这个操作是不允许的,同样减法,乘法,除法都不能操作,只允许赋值
    printf("name = %s\n", st2.name);
    printf("age = %d\n", st2.age);

    st2 = st;
    printf("name = %s\n", st2.name);
    printf("age = %d\n", st2.age);

    return 0;
}

3、动态内存分配和释放

静态内存分配:由申明好的数据类型,系统自动分配指定好的类型内存分配数量,这个可以在被调函数中修改它的内存值

malloc() free()

跨函数使用内存,只能通过动态分配内存才能保证内存被函数外使用,什么时候释放,取决于什么时候使用free函数

在被调用函数中国动态分配内存,供主调函数进行使用和修改

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

int declare_memory(int *p , int memory_size);

int main(void)
{
    int * p;  //这个整型指针用于指向申请的内存空间的地址

    /* 
        使用&为了能够在主调函数中响应修改后实参的值
        p是 int *类型
        &p 就是 int **类型了,所以被调函数中形参的类型也要对应
        如果使用declare_memory(p, 12),而被调函数的形参使用int *类型,这样和一般函数的实参修改        一样,不能将被调函数修改后的值传递到主调函数中了
    */
    if(!declare_memory(&p, 12))  
    {
        printf("内存申请失败");
        return 0;
    }

    //在这里释放declare_memory函数申请的内存空间,如果一直不释放,要么等到main函数执行结束,要        么就会内存泄漏,
    free(p);
    
    return 0;
}

int declare_memory(int **p , int memory_size)
{
    // 这里的*p == 主调函数中的p了
    *p = (int *)malloc(sizeof(int)*memory_size);
    if(*p == NULL)
    {
        return 0;
    }
    return 1;
}

数据结构:

将现实中大量而复杂的问题转换为数据,保存到内存中,这个就是数据结构,以及在此基础上实现某些功能而执行的相应的操作,这个操作就是算法

算法:

狭义的算法与数据的存取方式密切相关

广义的算法与数据的存储无关

泛型:利用某种技术达到这个效果(不同的数据存储方式,执行的操作是一样的)

时间复杂度和空间复杂度

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

 

1、线性结构:第一个元素只有后继元素,最后一个元素只有前驱元素,其余元素都有前驱元素和后继元素

基本操作:

1、初始化  int init(&L)

2、销毁    int destroy(&L)

3、重置    int clear(&L)

4、判空    bool isEmpty(&L)

5、获取已存元素数量 void listLen(&L,int * len)

6、获取指定索引的元素 int getElem(&L, int index,ElemType *e)

7、获取给定值并满足给定关系的第一个元素(这里的一个形参是函数类型) int locateElem(&L, ElemType elem, (int)(*comp)(ElemType , ElemType) )

8、获取一个元素的前驱 int getPriorElem(&L, ElemType elem, ElemType *pre_)

9、获取一个元素的后继 int getNextElem(&L, ElemType elem , ElemType *next_)

10、插入一个元素 int insertElem(&L, int index, ElemType elem)

11、删除一个元素 int deleteElem(&L, int index, ElemType *del_elem)

12、根据给定关系改变元素的值(这里同样有一个形参是函数类型)int traverse(&L, (void)(*update)(ElemType *))

连续存储:数组 ,操作代码

离散存储:链表,操作代码循环链表代码(待更新)

        首节点(第一个存有有效元素的结点),尾节点(最后一个存有有效元素的结点),头结点(首结点前的一个结点),头指针(指向头结点的指针),尾指针(指向尾结点的指针)

一般链表结构图

线性结构的常见应用:栈和队列

结构如下,

typedef int ElemType;

typedef struct Node
{
    ElemType data;
    struct Node *next;
}Node,*PNode;

typedef struct stack
{
    PNode base; //栈基地址,栈底指针
    PNode pop;  //栈顶指针
    int len;  //栈长度,方便操作
}STACK;

typedef struct queue
{
    PNode front;
    PNode rear;
}QUEUE;

栈:只允许在栈顶插入元素和删除元素 ,链栈代码顺序栈代码(待更新)

队列:只允许在队列头删除元素,在队列尾插入元素,静态队列代码(待更新),链式队列(待更新)

        静态队列一般是循环队列,容易操作,入队就把队尾向上加,出队把队头向上加,如果队头或队尾到达空间上限,就置0,一是为了不浪费空间,二是不用将队列元素整体向上或向下移动

        链式队列就和一般链表操作相同,只是只能在head结点加入,在tail结点删除

链式结构和顺序结构除了操作细节不一样之外,广义上说是完全一样的结构类型

2、非线性结构

二叉数:前序遍历(根,左,右),中序遍历(左,根,右),后序遍历(左,右,根),层次遍历(从根向下,一层一层的,每层从左到右)

3、查找和排序

折半查找

排序:

        冒泡

        插入

        选择

        归并

        快速

 

猜你喜欢

转载自blog.csdn.net/yimo_5288/article/details/84863246