作为一名和CS基本差不多的AI专业学生,掌握扎实的计算机基本知识是非常必要的。虽然是大一,但是为了赶超别人并且提前准备就业或者考研,数据结构的学习自然提上日程。之前只是零散的接收数据结构的知识,单独发系列文章记录自己的系统性学习过程,为了激励自己也为了方便复盘,尽可能用明白的语言记录自己的心得,不当之处希望各位大佬批评指正。
本人笔记以C语言为主,若翻看笔记的话要有C或者C++基础
清华大学 严蔚敏《数据结构(C语言版)》
王道考研 数据结构 20年版
B站蓝桥杯系列算法课程
入门级别的数据结构学习,线性结构。顾名思义,就是n个数据元素的有限的有序排列。
顺序表是线性表的一个最简单的体现,个人看来类似于数组(王道的课程里面也是数组)
比如,利用结构体创建一个顺序表,最大长度是静态数组的长度,length代表能使用的长度
typedef struct{
int data[10];
int length;
} SqList;
对顺序表进行初始化,就是遍历赋值,并把可使用长度设置到最大长度,注意函数参数
void InitList(SqList &l){
for(int i=0;i<10;++i){
l.data[i]=i;
}
l.length=10;
}
输出顺序表
void PrintList(SqList l){
for(int i=0;i<l.length;++i){
cout<<l.data[i]<<endl;
}
}
延长顺序表,无非就是涉及到动态的内存申请和释放,在C语言中是malloc和free函数,写惯了C++的new和delete一开始有点不适应
void Extend(SqList &l, int lengthExtend){
int *p=l.data;
l.data = (int *)malloc((l.Maxlength+lengthExtend)*sizeof(int));
for(int i=0;i<l.Maxlength ;++i){
l.data[i]=p[i];
}
l.Maxlength =l.Maxlength +lengthExtend;
free(p);
}
顺序表的查和改无非就是遍历查找或者排序后二分查找,不做赘述
链表在C++课程中已经接触过,先学习单向链表
作为任何数据结构,最基本的功能就是增删查改
作为单向链表,他在内存中的组成可以认为是两块区域,一个用来存放数据部分,一个用来存放指向下一个节点的指针
可以采用下列结构体
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
typedef不做赘述,定义了一个结构体,一个结构体指针,其中结构体指针是用来做表头,单独列出来意义更加明确
函数参数为链表头部,插入的位置,元素数值
bool Insert(LinkList &l,int i,int e){
if(i<1){
return false;
}
LNode *p; //扫描用指针
int j;//第几个节点
p=l;//让扫描节点指向头部
//下面是扫描节点,认为头节点是空的,为第0个,扫描到插入位置之前的那一个节点
while(p!=NULL&&j<i-1){
p=p->next ;
++j;
}
if(p==NULL){
return false;
}
LNode *s=(LNode *)malloc(sizeof(LNode));//申请新节点
s->data=e;
s->next=p->next ;//别和下一句写倒了,这是创建上一个节点和下一个节点链接的两句话
p->next =s;//同上
return true;
}//对于没有头结点的,直接找到i-1个节点或者直接创建一个(指在头部插入),接下来都会了吧
可以采取后插节点复制前一个节点的方法,从而不必知道表头的地址
bool InsertPriorSimple(LNode *n,int e){
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data =n->data ;
s->next =n->next ;
n->next =s;
n->data =e;
return true;
}
就是复制下一个节点的数据,删除下一个节点,要不然数据就断了
bool DeletNode(LNode *n){
LNode *q=n->next;
n->data =n->next->data;
n->next =q->next;
free(q);
return true;
}
如果是最后一个节点呢?很明显找不到前一个节点的next数据,删不干净,单向链表的弊端显现出来了
稍作改动就可以,把i-1改为i
while(p!=NULL&&j<i){
p=p->next ;
++j;
}
之后return p,即要找的节点
外界传入一个指定的e的值,从链表中查找
while(p!=NULL&&p->data!=e){
p=p->next ;
++j;
}
之后return p,即要找的节点
主要分为头插法和尾插法
书上给的方法是这样的
void Initial(LinkList &L,int n){
int t;
L=(LinkList)malloc(sizeof(LNode));
L->next =NULL;
for(int i=n;i>0;--i){
p=(LNode *)malloc(sizeof(LNode));
cin>>t;
p->data=t;
p->next=L->next ;
L->next =p;
}
}
很明显
p->next=L->next ;
L->next =p;
这两句就是倒着插入新节点
尾插法可以遍历链表,找到p->next为NULL的节点,调用插入下一个的函数
不过这样循环会越来越慢,时间复杂度为O(n^2)
可以借鉴头插的思路,设置一个尾部指针,调用插入函数