数据结构杂谈

数据结构杂谈

在学习数据结构时,绝大多数人都能看懂算法,但是上手写代码却是一头雾水,这个问题在我刚开始学习数据结构的时候给我带来了很大的困扰,差点就放弃了,总结下来就是对于一些开头的定义和结构体定义,以及指针的使用不熟悉。本文将会详细讲解上述三个问题。


定义

  1. #define
    利用 #define 将一个标识符定义为一个字符串
    例如 #define PI 3.14159265就是我们最常见的宏定义。代表PI就是3.141559265。
  2. const
    const 在实际编程中用得并不多,const 是 constant 的缩写,意思是“恒定不变的”!它是定义只读变量的关键字,或者说 const 是定义常变量的关键字。说const定义的是变量,但是又相当于常量,说他定义的是常量又有变量的属性,所以叫常变量。
    用const定义的变量的值是不允许改变的,常用来表示最大长度,或者某个重复使用的量。const int MAX = 255
  3. 定义数据类型
    为了程序在修改数据类型的时候不必逐行更改,将会使用typedef对一个数据类型进行定义。
    例如typedef int ElementType给int整形起了个别名叫做ELementType,需要更改数据类型的时候只需要更改int就可以了。
  4. typedef struct
    相信很多人在数据结构学习当中发现结构体的定义都是这样的:
typedef struct ListNode {
    ElementType  Element; 
    struct ListNode *Next;
}Node, *PNode;

并且在声明结构体变量的时候是Node N或者是PNode P,难道不应该是struct ListNode L,其实都是可以的,但是ListNode L会报错,这是因为ListNode是相当于一个标识符,而不是一个结构体,只有struct 和 ListNode 合在一起才表示一个结构体类型。那么为什么Node N,PNode P可以呢?这是因为typedef给这个结构体起了新的名字PNode以及Node。


指针

在指针方面我主要介绍指针的定义,&和*符号以及指针在函数中的应用。

  1. 指针的定义
int p; //这是一个普通的整型变量  
int *p; //P是一个整型数据的指针  
int p[3]; //P是一个由整型数据组成的数组 
int *p[3]; //P是一个由返回整型数据的指针所组成的数组  
int (*p)[3];//P是一个指向由整型数据组成的数组的指针  
int **p; //指针所指向的元素是指针,该指针所指向的元素是整型数据,也称为二级指针,用的很少。 

在定义一个指针的时候,从变量名处起,根据运算符优先级结合,其中仅代表变量是一个指针。**
指针的值是指针本身储存的数值,这个数值会被当作一个地址,指针所指向的内存去就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针类型)的一片内存。
2. 指针的运算
在数据结构当中很少用到,简单介绍一下,对于指针也可以进行运算int *p = a[0];p++这样p会指向a[1],为什么不是地址的值+1呢,实际上在做指针运算时编译器会吧指针的值加上sizeof(指针类型),对于上述例子指针p实际上被加了4。
3. 运算符&和*
&取地址运算符,*是间接运算符。
&a的运算结果是该变量的内存地址,也可以说是一个指针。
*p的运算结果就是p指向的东西。

int a = 5;
int b;
int *p;
int *pp;
p = &a; //p指向a,把a的地址赋值给p
*p = 24;//p指向的本来是5,改成24,相当于变量a等于24,因为p所指向的和变量a是同一块内存地址
*pp = &p;//pp二级指针指向p。
**pp = 34;//把变量a,指针p所指向的内存地址存放的数据改为34。

这些就是指针的基本用法。
4. 指针的应用
指针具体的应用更多的体现在结构体和函数上。

typedef struct ListNode {
    ElementType  Element; 
    struct ListNode *Next;
}Node, *PNode;

其中PNode就是一个结构体指针,访问其内容的时候就可以用->对其成员进行访问,那为什么要用结构体指针而不是直接使用结构体呢?因为通常我们不会把一个数据结构的操作放在一起,而是以函数的形式分开操作,保证了程序的封装性和模块化,在使用函数时,参数往往是指针,因为指针指向的是地址,所以在函数内对结构体的更改就可以保存下来,如果是普通结构体,则只有一个返回值,并不会对整体的数据结构进行修改,具体实例我将会在之后的博客具体介绍。
关于指针在函数中的应用我将会在函数部分进行介绍。


函数

对于函数在数据结构中的使用,大多数的返回值都是void,因为往往需要一个函数对整个数据结构进行修改,并不需要返回值。我在这里具体说明关于函数参数和返回值的问题。
如果函数使用参数,则需要声明接收参数的变量,这些变量称为函数的形式参数,在函数退出时将会销毁。

当调用函数的时候有两种向函数传递参数的方式:

  • 传值调用:该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
  • 引用调用:通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。
    在数据结构中往往都是引用调用,都是对实参本身进行操作,

函数的返回值可以是一种数据类型的值也可以是一个指针,返回值为指针大多数用于初始化数据结构时使用,如创建链表:

PNode CreateList(void) {
    int len ;    //    用于定义链表长度
    int val ;    //    用于存放节点数值
    PNode PHead = (PNode)malloc(sizeof(Node));    //    创建分配一个头节点内存空间
//头节点相当于链表的哨兵,不存放数据,指向首节点(第一个节点)
    if (PHead == NULL)    //    判断是否分配成功
    {
        printf("空间分配失败 \n");
        exit(-1);
    }

    PNode PTail = PHead;    //    链表的末尾节点,初始指向头节点
    PTail->Next = NULL;    //    最后一个节点指针置为空
    printf("请输入节点个数:");
    scanf("%d", &len);        //    输入节点个数
    for (int i = 0; i < len; i++) {
        PNode pNew = (PNode)malloc(sizeof(Node));    //    分配一个新节点
        if (pNew == NULL) {
            printf("分配新节点失败\n");
            exit(-1);
        }
        printf("请输入第 %d 个节点的数据:", i + 1);
        scanf("%d", &val);    //    输入链表节点的数据

        pNew->Element = val;    //    把数据赋值给节点数据域
        PTail->Next = pNew;    //    末尾节点指针指向下一个新节点
        pNew->Next = NULL;        //    新节点指针指向为空
        PTail = pNew;    //    将新节点复制给末尾节点
    }
    printf("创建链表成功\n");
    return PHead;    //    返回头节点
}

最后返回的是链表的头节点PHead。其中需要重点解释的就是PNode PHead=(PNode)malloc(sizeof(Node));这一段代码说明了为什么一开始在定义结构体时分别定义了PNode 和 Node,据我观察大多数是为了在给新建的节点指针分配内存空间。


在理解了上述内容的情况下,相信各位可以更好的学习数据结构,更快的上手实现数据结构以及各种算法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值