数据结构基础—线性表
数据结构基础—线性表
线性表是一种顺序存储结构其特点有:
- 存在唯一的一个被成为”第一个”的数据元素
- 存在唯一的一个被成为”最后一个”的数据元素
- 除第一个之外,集合中的每个元素均只有一个前驱,除最后一个元素哇,集合中每一个元素均只有一个后继
一、线性表类型定义
一个线性表是n个具有相同特征的数据元素的有限集合,可记作(a1,a2,a3...an),当n = 0时为空表
线性表是相当灵活的数据结构,不仅长度可变,还可以对其中的元素进行访问,插入和删除...具体的操作我会在后面解释
二、顺序表的表示与实现
顺序表指的是用一组地址连续的存储单元依次存储线性表的数据结构(类比数组),也称作顺序存储结构或是顺序映像是一种随机存取的存储结构
逻辑上相邻的两个元素在物理位置上也相邻
1.顺序表的表示
//顺序表的结构
//类型自己换吧
typedef struct List{
int *elem;
int length;
int MaxSize;
}SqList;
//可以把它比作开门的要是或是抽屉,通过这东西就就可以找到这个顺序表中的任意一元素
elem:表示顺序表的首地址,和数组一样
length:表示顺序表的当前长度
MaxSize:表示当前顺序表的最大容量(使用顺序表一般大开小用,当使用的范围超过最大容量后可以进行动态分配内存,之后会说)
2.一些相关的操作
//空表
length = 0; //长度为零
//初始化
void Set(SqList &L,int n){
L.elem = (int*)malloc(Size * sizeof(int)); //开辟空间 Size:宏定义,最大容量
if(! L.elem) {printf("创建失败") return;}
L.MaxSize = Size;
L.length = 0;
printf("创建成功\n");
}
//增表
void AddList(SqList &L){
if(L.length>= L.MaxSize){
int *bnew;
bnew = (int*)malloc(L.elem,(L.MaxSize+ADD) * sizeof(int));
L.elem = bnew;
L.MaxSize = L.MaxSize+ADD;
}
printf("顺序表增加成功\n");
}
//在n个位置上插入数值为k的元素
//这里可以再加入顺序表是否满的操作
void Add(SqList &L,int n,int k){
int j = L.length;
if(n<=0 ||n>L.length+1){
printf("插入的位置不合理\n");
} else{
int *p = L.elem+L.length-1;
for(;p>=L.elem+n-1;p--){
*(p+1) = *(p);
}
L.elem[n-1] = k;
L.length++;
}
printf("添加成功\n");
}
//删除第n个位置的元素
void Delete(SqList &L,int n){
int j = L.length;
if(n <=0 || n > L.length){
printf("要删除的位置不合理\n");
}else{
int *p = L.elem+n-1;
for(;p < L.elem+L.length-1;p++){
*p = *(p+1);
}
L.length--;
}
printf("删除成功\n");
}
//输出
void Get(SqList &L){
for(int i = 1;i <=L.length;i++){
printf("%d\t",L.elem[i-1]);
}
三、链表的表示与实现
上一节中的顺序表有个很大的缺点,就是在插入和删除元素时需要移动大量的元素,本节的链表就克服了这个不足,因为它不要求逻辑上相邻的元素在物理位置上也相邻。但是,失去了顺序表随机存取的优点
链表都是由一个一个节点相连形成的,像下图一样,一般一个数据域,一个指针域(放下一个节点的地址,这样就连起来啦)。当然在双向链表中有两个指针域...
1.线性链表
就是像上图那样一个一个节点连接起来的链表,不过一般情况下会有一个头指针,它不存放数据,起到一个总领全链的作用(总领全文?),他它的最后一个节点的指针是指向空的(NULL)
a.线性链表的表示
//结构体
typedef struct LNode
{
ElemType data; //存储链表的元素空间
struct LNode *next; //后继结点
}LinkList;//单链表结点类型定义
//类
class Lnode{//节点类
friend class List;
private:
int data;
Lnode *next;
};
class List{
};//其他的操作
b.一些基本操作
//创建链表,初始化
void Set(Lnode *L,int n,int k){//尾插
if(n ==0){
L->next =NULL;
cout << "链表为空";
}else{
Lnode *r =NULL,*p = NULL;
p = L;
for(int j = 1;j <n&&p != NULL;j++){
p = p->next;
}
if(p != NULL){
r = (Lnode *)malloc(sizeof(Lnode));
r->data = k;
r->next = NULL;
p->next = r;
p = r;
}
r->next = NULL;
}
}
//在第n个位置插入元素的数值为k
void Add(Lnode *L,int n,int k){
if(n <= 0||n>n+1){
cout << "要插入的位置不合理";
}else{
Lnode *p = L,*q;
for(int i = 1;i!= n;i++){
p = p->next; //p是插入位置的直接前驱
}
if(p != NULL){
q = (Lnode*)malloc(sizeof(Lnode));
q->data = k;
q->next = p->next; //看着里,关键操作
p->next = q;
}
}
cout << "添加成功\n";
}
//删除第n个元素
void Delete(Lnode *L,int n){
if(n <= 0||n>n+1){
cout << "要插入的位置不合理";
}else{
Lnode *p =NULL,*q =NULL;
p = L;
q =L->next;
for(int i = 1;i!= n;i++){
p = p->next; //p是插入位置的直接前驱
q = p->next; //Q是要删除的
}
if(p->next != NULL){
p->next = q->next; //看着里,关键操作
q = q->next;
//或是p->next = p->next->next
free(p);//记得free掉
}
}
cout << "删除成功\n";
}
//输出链表
void GetList(Lnode *L){
Lnode *r =NULL,*p = NULL;
p = L->next;
for(;p != NULL;p = p->next){
cout << p->data << "\t";
}
2.循环链表
与上述的纤细链表差不多只是最后一个元素的指针指回头指针
一些基本操作
//判空
p->next == p;//判断节点的next是否等于头指针
//其余操作和线性单链表基本相同
3.双向链表(一般都循环了)
之前的链表只能单向的查询和遍历,双向链表可以在链表的两个方向移动
a.双向链表的表示
typedef struct LNode
{
ElemType data; //存储链表的元素空间
struct LNode *prior; //前驱结点
struct LNode *next; //后继结点
}LinkList;//双向循环链表结点类型定义
b.一些基本的操作
//判空
first->prior == first;
first->next == first;
//构造一个空的双向循环链表L
void InitLinkList(LinkList *L)
{
L->prior=L->next=L;//空的双向循环链表L的prior域和next域均回指
}
//头插法建立双向循环链表L
void CreateLinkListF(LinkList *L,ElemType d[],int n)
{
LinkList *r;
int i;
L->next=NULL;
for(i=0;i<n;i++)
{
r=(LinkList *)malloc(sizeof(LinkList));//创建新结点r
r->data=d[i];//为新结点赋值
//将新结点r加入到头结点之后
r->next=L->next;
if(L->next!=NULL)
L->next->prior=r;
L->next=r;
r->prior=L;
}
r=L->next;
while(r->next!=NULL)
r=r->next;
r->next=L;
L->prior=r;
}
void CreateLinkListR(LinkList *L,ElemType d[],int n)//尾插法建立双向循环链表L
{
int i;
LinkList *p,*q;
L->next=NULL;
q=L;//q指向尾结点
for(i=0;i<n;i++)
{
p=(LinkList *)malloc(sizeof(LinkList));//创建新结点p
p->data=d[i];//为新结点赋值
//将新结点r加入到头结点之后
q->next=p;
p->prior=q;
q=p;//q重新指向尾结点
}
q->next=L;//尾结点的next域回指
L->prior=q;
}
//在双向循环链表L中将e插入到第i个位置
void LinkListInsert(LinkList *L,int i,ElemType e)
{
LinkList *p=L;//p指向头结点
LinkList *r;//r指向新结点。即值域为e的结点
int j;
if(i==1)//插入到第1个位置
{
r=(LinkList *)malloc(sizeof(LinkList));//创建新结点
r->data=e;//为其赋值
//将新结点根据双向循环链表的特性插入到双向循环链表中
r->next=p->next;
p->next=r;
r->next->prior=r;
r->prior=p;
}
else if(p->next==L)//原单链表为空表或者只有一个元素
{
r=(LinkList *)malloc(sizeof(LinkList));//创建新结点r
r->data=e;//为其赋值
p->next=r;
r->next=p; //看这里,关键操作
p->prior=r;
r->prior=p;
}
else
{
p=L->next;
for(j=1;j<i-1&&p!=L;j++)//查找第i-1个结点
{
p=p->next;//指向下一个结点
}
if(p==L)
printf("无\n");//若未找到,则给出适当的提示
else
{
r=(LinkList *)malloc(sizeof(LinkList));//创建新结点r,其值域为e,将其插入第i-1个位置之后
r->data=e;//为其赋值
//将新结点根据双向循环链表的特性插入到双向循环链表中
r->next=p->next;
if(p->next!=NULL)p->next->prior=r;
r->prior=p;
p->next=r;
}
}
}
//在双向循环链表L中删除第i个元素
//关键操作找到要删除的节点后,假设是p
p->prior->next = p->next;
p->next->prior = p->prior;
void LinkListDelete(LinkList *L,int i)
{
LinkList *p=L;
LinkList *q;
int j;
if(i!=1)
{
p=L->next;
for(j=1;j<i-1&&p!=NULL;j++)//查找第i-1个结点
p=p->next;//指向下一个结点
if(p==NULL)//若未找到第i-1个结点,则给出适当的提示
printf("无\n");
else
{
q=p->next;
if(q==NULL)
printf("无\n");//若未找到第i-1个结点,则给出适当的提示
p->next=q->next;//从链表中删除该结点
if(p->next!=NULL)
p->next->prior=p;
free(q);//成功删除该结点
}
}
else//i等于1时
{
q=L->next;
L->next=q->next;
q->next->prior=L;
free(q);
}
}
//输出显示双向循环链表L的各个元素值
void DispLinkList(LinkList *L)
{
LinkList *p;
p=L->next;//p指向头结点后的第一个结点
while(p!=L)//判断是否结束,注意双向循环链表的判断结束的条件
{
printf("%c ",p->data);//依次显示输出元素值
p=p->next;//指向下一个结点
}
}