线性表
在我们刚开始学习数据结构的过程中,往往是从数据结构的历史开始学起,然后是什么是算法,算法的特性等,接下来便是复杂度的分析。
因为写这个博客我的初衷是对每章的源码进行剖析,以便于自己能够更好的学习这门课,掌握其思想。在这里笔者只希望大家不要去死学数据结构。数据结构是一切算法的思想,它本身就非常有趣,当然也可以学的很开心。
顺序表
我来先讲一下顺序表的思想。
咱们举个例子来阐述,中学的时候大家应该都会做广播体操的吧,在做广播体操之前,我们往往会先排队,你的前面是谁,后面是谁,一般都是会固定好。而且数量短时间内是不会变得。等老师喊多少号多少号同学出列,很明显很好查找。好了,你把顺序表想成数组就行。
下面我们来看代码
/*顺序表的初始化*/
#define MAXSIZE 60/*数组元素的最大个数为60*/
typedef struct {
int data;/*数组,看你数组中存放什么类型的数据,此处也可以不是整型*/
int length;/*顺序表的长度*/
}L;
插入的图形解释
/*顺序表的插入*/
L insert(L *H,int i,int z)/*在位置为i的地方出入值为z的元素*/
/*此时注意,顺序表位置i是从1开始的,而数组的位置则是从0开始的,所以e要减1*/
{
if(H->length==MAXSIZE){
reurn error;
}
if(i-1<0||i>H->length+1)
return error;/*判断插入的位置是不是顺序表里的位置*/
for(int a=length-1;a>=i-1;a--){/*从顺序表的末尾开始移动,直到第i-1个元素*/
H->data[a+1]=H->data[a];
}
H->data[i-1]=z;/*第i个元素的值为z*/
H->length++;/*顺序表的长度加1*/
return H;/*返回顺序表*/
}
删除的图形解释
/*顺序表的删除*/
L Delete(L *H,int i,int &z){/*删除位置为i的元素*/
if(i-1<0||i>H->length+1||H->length==0)/*判断顺序表中是否有i的位置和这个顺序表是不是空表*/
return error;
z=H->data[i-1];/*把要删除的元素赋值给z*/
for(int a=i;a<=length-1;a++){
H->data[i-1]=H->data[i];/*数组下标从i开始,直到末尾,每个元素向前移一格*/
}
H->length--;/*顺序表的长度减1*/
return H;
}
小总结
我们从代码中可以看到,顺序表的查找效率是非常高的,时间复杂度为O(1),但是顺序表的插入和删除操作却是非常的繁琐。在插入时,最好的情况肯定是插入在表尾,时间复杂度为O(1)但是平均的时间复杂度为O(n),因为我们从源码中可以看出,每次插入一个元素,所属位置以及他后面的元素都要向后移动一个元素。删除操作和插入操作相似。
所以如果我们要如何解决插入和删除过于复杂的问题呢。前人早已经帮我们想好了,就是用链式存储。
线性表的链式存储
我们该如何理解链式存储的思想呢。老规矩,先上例子(虽然我的例子并不是那么通俗易懂,哈哈哈,理科生,稍微理解一下),大家应该都有过去银行办理业务的经历吧。去银行时,银行为了保证效率,通常会采用取号的方法。这样取到号码的人根本不用去排在一起,这就和链表的思想相一致,存储空间没必要一定连续,上一个记录下一个的位置就好。
/*单链表的初始化*/
typedef struct LNode{
int data;/*数据域,和顺序表的数组类型一样,此处不一定是int,我只是为了方便*/
struct LNode *next;/*指针域,用于指向下个节点的地址*/
}LNode,*Linklist;
使用头插法建立单链表图解
/*头插法建立单链表*/
Linklist HeadBuild(Linklist L){
L=(Linklist)malloc(sizeof(LNode));/*赋空间给头指针=头节点*/
LNode *s;/*使用指针变量s来指向一个新的节点*/
L->next=NULL;/*头节点的指针域为NULL*/
int x;
scanf("%d",&x);
while(x!9999){/*此时不知道链表的长度,所以不能用for语句*/
s=(LNode*)malloc(sizeof(sizeof(LNode));/*赋空间给指针s=新节点*/
s->data=x;
s->next=L->next;/*新节点指向原来的第一个节点,如图第三步所示*/
L->next=s;/*把新节点作为第一个节点*/
scanf("%d",&x);
}
return L;
}
根据代码我们可以看出,头插法每次都把一个新生成的节点当作链表的第一个节点,所以当你输出每个节点的数据域的值是,顺序是逆序的。
尾插法图示
/*尾插法建立单链表*/
Linklist TailBuild(Linklist L){
L=(Linklist)malloc(sizeof(LNode));/*给头节点赋空间*/
LNode *r=L;/*声明指向表尾的指针*/
int x;
LNode *s;/*声明指向新节点的指针*/
scanf("%d",&x);
while(x!=9999){
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
r->next=s;/*新节点等于尾节点的下一个节点*/
r=s;/*尾节点指向新的尾节点*/
scanf("%d",&x);
}
r->next=NULL;/*尾节点的next域设为null*/
return L;
}
这种方法创建的链表输出正常
链表插入的图示
/*单链表的插入*/
Linklist Insert(Linklist L,int i,int x)/*在第i个节点后面插入数据域为x的节点*/
{
LNode *p=L;/*声明一个专门用于查找的指针,初始指向头节点*/
LNode *s;/*声明一个指向插入节点的指针*/
s=(LNode *)malloc(sizeof(LNode));/*给插入节点赋空间*/
s->data=x;/*给准备插入的节点的数据域为x*/
int z=0;
while(z<=i){
p=p->next;/*使p指向第i个节点*/
z++;
}
s->next=p->next;/*s指向p原来的下一个节点*/
p->next=s;/*p的下一个节点指向s*/
return L;
}
**
注意,最后的两个顺序是不可以换的,因为你如果换了一个序,原来的p->next就没了
**
线性表的删除
/*线性表的删除*/
/*这里我们只写关键的一步*/
/*这里我们把要删除的节点叫s,s的前驱节点为p*/
Listlink q;
q=s;
p->next=s->next;
free(q);
小总结:以上是关于单链表的插入,删除以及查找。从代码中我们可以看出,单链表的查找没有顺序表来的方便,顺序表查找的时间复杂度为O(1),而单链表的查找的时间复杂度为O(n).但是插入和删除操作单链表的时间复杂度是高于顺序表的。当我们需要插入和删除的操作多时选用链表,查找的操作多时选择顺序表。
下面我们来讲另一种链表,双链表
如果说单链表的某个节点只知道他的后继的话,那么双链表里的节点既知道他的前驱又知道他的后继。
话不多说先上图,再上代码
/*双链表的初始化*/
typedef struct {
int data;/*双链表节点的数据域*/
struct LNode *prior;/*前驱域,用来指向这个节点的前驱节点*/
struct LNode *next; /*后继域,用来指向这个节点的下一个节点*/
}LNode,*Linklist;
双链表的建立
图示
/*双链表的建立*/
Linklist Doublebuild(Linklist L){
int x;
L=(Linklist)malloc(sizeof(LNode));/*给头节点赋空间,也就是创建头节点*/
L->next=NULL;/*头节点的next域为NULL*/
L->prior=NULL;/*头节点前驱域为NULL*/
Linklist r=L;/*声明一个指向表尾的节点*/
LNode *s;/*声明一个新生成的节点*/
scanf("%d",&x);
while(x!=9999){/*利用这个循环来不断创建新的节点*/
s=(LNode*)malloc(sizeof(LNode));/*创建新节点*/
s->data=x;/*给新节点的数据域赋值*/
r->next=s;/*表尾节点的next域指向新的节点*/
s->prior=r;/*新的节点的前驱节点域指向表尾的节点*/
r=s;/*让r指向新的表尾节点*/
scanf("%d",&x);
}
r->next=NULL;/*把最后表尾节点的next域指向为NULL*/
}
双链表的插入
图示
/*双链表的插入操作*/
p->next->prior=s;/*把p节点原来的后继节点的前驱节点域指向新插入的节点*/
s->next=p->next;/*把新插入节点的后继域指向原来p节点的下一个节点*/
s->prior=p;/*把新插入节点的前驱域指向p*/
p->next=s;/*p的后继节点域指向s*/
双链表的删除操作
/*双链表的删除操作*/
p->prior->next=p->next;/*把p的前驱节点的next域指向p原来的下一个节点*/
p->next->prior=p->prior;/*把p下一个节点的前驱域指向p原来的前一个节点*/
free(p);
循环单链表和双链表就简单的说下,就是把最后一个节点和头节点连起来的,就把原来的NULL改成头节点就行
如果各位对源码还有不了解的欢迎评论区留言。