一.线性表-顺序表学习笔记(1)

前言

线性表(Liner List)

线性表:是具有相同数据类型的n(n≥0)个数据元素的有限序列,其中n为表长,当n = 0时,线性表是一个空表。

特点:一对一,除第一个唯一前驱,除最后一个有唯一后继.

a1–>a2–>a3–>a4

数据结构三要素:逻辑结构,存储结构,运算.

线性表是一种逻辑结构,有两种存储结构:顺序表和链表.

基本操作

  • 创建,销毁
  • 增加元素
  • 删除元素
  • 按位查找
  • 按值查找

常用操作

  • 判断表空
  • 求表长
  • 打印表

整个项目的构建

image-20210328141636320

编程软件采用vscode,采用cmake管理多文件,不需要手动编译.

关于具体vscode的讲解.建议查看b站视频.

https://www.bilibili.com/video/BV13K411M78v?p=2&spm_id_from=pageDriver

顺序表(静态分配)

静态分配,元素为结构体的顺序表

定义

我们取元素为结构体,写一个复杂一点点的,注意比较结构体相同的时候,不能用==.

未命名文件

#define MAXSIZE 10
//这里的写法也可以typedef struct Student{} Student;一个意思
typedef struct{
    int number;  //序号
    string name; //姓名   
}Student;

//定义一个静态,元素为字符的顺序表,最大元素10
typedef struct 
{
    int length;             //当前表长
    Student data[MAXSIZE] ; //数据

}Seqlist;

初始化

典型错误

//初始化
void initList(Seqlist & l){
    l.length=0;
}
  • 由于内存中的脏数据,假如不初始化,强行访问可能产生意想不到的效果.

我们强制打印一下,明显不是我们想要的结果

void test(){
    Seqlist l;
    initList(l);
    //insertListByOrder(l,1,'a');
    //printList(l);
    for(int i=0;i<10;i++){
        cout << "第" <<i <<"个" <<endl;
        cout << "名字是:"<<l.data[i].name << endl;
        cout << "序号是:" <<l.data[i].number << endl;
    }
}

image-20210328110744796

完善后的如下:

void initList(Seqlist & l){
    for(int i=0;i<Maxsize;i++){
        l.data[i].number=0;
        l.data[i].name="";
    }
    l.length=0;
}

image-20210328111320482

静态分配没必要销毁.

逻辑上长度为0即可,通过声明分配的空间,函数结束会自动回收.

image-20210328143132687

  • 注意健壮性,判断位置与表满,

  • 尤其注意插入位置是1到表长+1

  • 由于需要修改顺序表,注意采用&

bool insertList(Seqlist & l,int n,int number,string name){
    //插入位置检查
    if(n<1||n>l.length+1){
        cout <<"插入位置不合法" <<endl;
        return false;
    }
    //表满检查
    if(l.length>=MAXSIZE){
        cout <<"表已满" <<endl;
        return false;
    }
    /* //从最后开始往后移一个位置
    for(int i=l.length-1;i>=n-1;i--){
        l.data[i+1]=l.data[i];
    }
    l.data[n-1]=e;
    l.length++;
    return true; */
    for(int i=l.length;i>=n;i--){
        l.data[i].name=l.data[i-1].name;
        l.data[i].number=l.data[i-1].number;

    }
    l.data[n-1].name=name;
    l.data[n-1].number=number;
    l.length++;
    return true;
}

调用代码,注意返回是bool,所以可以判断是否插入成功,printList()在后面有解释

   if(insertList(l,1,0,"Alice")){
        cout <<"insert success" <<endl;
    }
    //insertList(l,1,0,"Alice");
    insertList(l,2,1,"Baker");
    insertList(l,3,2,"Chris");

 	printList(l);

image-20210328140459095

  • 注意有多个返回值,所以通过指针修改

  • 删除位置的合法性判断,1到表长

bool deleteListByOrder(Seqlist & l,int n,int &number,string & name){
    if(n<1||n>l.length){
        cout <<"删除位置不合法" <<endl;
        return false;

    }

    number=l.data[n-1].number;
    name=l.data[n-1].name;
    for(int i=n;i<l.length;i++){
        l.data[i-1].name=l.data[i].name;
        l.data[i-1].number=l.data[i].number;
    }
    l.length--;
    return true;

}

调用

string name;
    int number;
    if(deleteListByOrder(l,2,number,name)){
        cout <<"删除的学生序号是" <<number <<endl;
        cout <<"删除的学生名字是" <<name <<endl;
    }
    printList(l);

image-20210328140621044

// 按位序查找元素,由于返回序号,和名字,通过引用修改.
void findElementByOrder(Seqlist  l,int n,int & number,string & name);

// 按内容---姓名查找位置,第一次出现,没有返回-1
int findElementByValue(Seqlist  l,string name);

//按内容---结构体查找位置
int findStructByValue(Seqlist  l,Student student);
  • 注意判断结构体相同的方法
  • 由于返回值有两个,用引用,或者指针返回。
  • 判断结构体相同的方法在后面,注意不能用==
void findElementByOrder(Seqlist  l,int n,int &number,string & name){
    if(n<1||n>l.length){
        cout <<"查找位置不合法" <<endl;
        return;

    }
    number=l.data[n-1].number;
    name=l.data[n-1].name;

}

int findElementByValue(Seqlist  l,string name){
    for(int i=0;i<l.length;i++){
        if(l.data[i].name==name){
            return i+1;
        }
    }
    return -1;


}

int findStructByValue(Seqlist  l,Student student){
    for(int i=0;i<l.length;i++){
        if(isSameStudent(l.data[i],student)){
            return i+1;
        }
    }
    return -1;



}

调用

    int n=2;
    string name2="";
    int number2;
    findElementByOrder(l,n,number2,name2);
    cout << "第" <<n <<"个位置的序号是" << number2 <<endl;
    cout << "第" <<n <<"个位置的名字是" << name2 <<endl;

    cout <<"Alice的位序是" <<findElementByValue(  l,"Alice") <<endl;

    Student a;
    a.number=0;
    a.name="Alice";
    if(findStructByValue(l,a)){
        cout <<"位序是" <<findStructByValue(l,a) <<endl;
    }

image-20210328141449802

工具

//打印一下顺序表
void printList(Seqlist  l);

//表长
int lengthOfList(Seqlist l);

//判断表空 
bool isEmpty(Seqlist l);

//判断结构体相同
bool isSameStudent(Student a,Student b);
void printList(Seqlist  l){
    for (int i=0;i<l.length;i++){
        cout <<"第" << i+1 <<"个学生序号是 " << l.data[i].number <<endl;
        cout <<"第" << i+1 <<"个学生名字是 " << l.data[i].name <<endl;
    }
}

int lengthOfList(Seqlist l){
    return l.length ;
}

bool isEmpty(Seqlist l){
    if(l.length!=0){
        return false;
    }
    return true;
}

bool isSameStudent(Student a,Student b){
    if(a.name==b.name&&a.number==b.number){
        return true;
    }
    return false;
}

源程序如有必要,留言.

typedef与struct

struct Stu {
    int data;
    string name;
}Stu;
声明一个结构体
使用的时候,struct Stu  a;
struct  {
    int data;
    string name;
}Stu;
声明一个结构体变量
使用的时候,Stu.data=3;Stu.name="abc";
这两个的结果相同,都是将结构体变量重命名
使用的时候,Stu a;

typedef struct Stu {
    int data;
    string name;
}Stu;

typedef struct {
    int data;
    string name;
}Stu;
我们将那个结构体重命名,至于它的名字,我们不在乎了

但是在c++中,也可以不需要typedef

使用结构体的时候,不需要struct

struct Stu {
    int data;
    string name;
};
 	Stu a;
	a.data=5;
    a.name="all";

指针与引用

我们可以把引用理解成变量的别名。定义一个引用的时候,程序把该引用和它的初始值绑定在一起,而不是拷贝它。计算机必须在声明r的同时就要对它初始化,并且,r一经声明,就不可以再和其它对象绑定在一起了。

引用自知乎匿名用户

1.引用和指针,在内存中都是占用4个字节(32bits系统中)的存储空间。指针和引用存放的都是被引用对象的地址,引用必须在定义的同时进行初始化。

2.指针常量本身(以p为例)允许寻址,即&p返回指针常量(常变量)本身的地址,被引用对象用*p表示;引用变量本身(以r为例)不允许寻址,&r返回的是被引用对象的地址,而不是变量r的地址(r的地址由编译器掌握,程序员无法直接对它进行存取),被引用对象直接用r表示。

3.凡是使用了引用变量的代码,都可以转换成使用指针常量的对应形式的代码,只不过书写形式上要繁琐一些。反过来,由于对引用变量使用方式上的限制,使用指针常量能够实现的功能,却不一定能够用引用来实现。

4.一些其他不同:

引用使用时无需解引用(*),指针需要解引用;

“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;

引用不能为空,指针可以为空;

指针和引用的自增(++)运算意义不一样;引用自增被引用对象的值,指针自增内存地址。

引用自知乎RainMan

  1. 引用只是c++语法糖,可以看作编译器自动完成取地址、解引用的常量指针
  2. 引用区别于指针的特性都是编译器约束完成的,一旦编译成汇编就喝指针一样
  3. 引用由编译器保证初始化,使用起来较为方便(如不用检查空指针等)
  4. 引用没有顶层const即int & const,因为引用本身就不可变,所以在加顶层const也没有意义; 但是可以有底层const即 const int&,这表示引用所引用的对象本身是常量
    int b=999;
    int& r=b;
    //通过r访问变量
    cout<<"引用的值"<<r<<endl;
    int* p=&b;
    cout<<"指针的值"<<*p<<endl;
    int c=1010;
    //r仍然指向b,但是现在b的值是1010
    r=c;
    cout<<"引用的值"<<r<<endl;
    cout<<"修改后的b"<<b<<endl;
    int d=2020;
    p=&d;
    cout<<"指针的值"<<*p<<endl;
int a;
int* p1 = NULL;  //p1叫空指针,空指针指的是不指向任何实体的指针,其值为NULL就表示空指针
p1 = &a;         //此时p1当然就不是空指针了
void* p2;        //p2并非叫空指针,它只是未初始化,指向void的指针与是否空指针无关
p2 = NULL;       //p2此时才叫空指针
p2 = &a;         //p2不是空指针

类似地,空引用指的是没有对其它实体进行引用的引用,由于C++规定引用必须初始化,所以不存在空引用,而指针在这方面是自由的,当它被赋值NULL时,才成为空指针。

int * p1 = NULL;  //p1是一个空指针
int & r = *p1;     //虽然p1是一个空指针,但r并非空引用,r有引用,只不过r引用的东西是无效的,所以r是一个无效引用,不仅对r的使用属于未定义行为,而且*p1本身就属于未定义行为。
“不存在的实体”与“尚未引用其它实体的引用”不是一回事。

无论引用还是指针都存在两个概念,“引用本身/被引用的实体”和“指针本身/被指向的实体”。

空引用/空指针指的是指针或引用本身为空,而非被指向或被引用的实体为空。

引用或指针本身为空称为尚未引用/指向其它实体,被指向或被引用的实体为空称为不存在的实体。
引用自  https://bbs.csdn.net/topics/390538538?page=2

image-20210415105041721

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值