单链表定义
实现
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,* LinkList;
优缺点
- 优点:不需要大片的、连续的内存空间来存储。且改变容量很容易
- 缺点:无法随机存取,且需要额外的空间来存储节点的指针
初始化
不带头结点
//带头结点
bool InitList(LinkList &L){
L=null;
return true;
}
带头结点头结点不存储元素,头结点之后的结点才开始存储元素
bool InitList(LinkList &L){
L=(LNode *)malloc(sizeof(LNode));//创建一个结点
if(L==Null){
return false;
}
L->next = Null;
return true;
}
插入
1.按位序插入
带头结点
O(n)
bool ListInsert(LinkList &L,int i,ElemType e){
if(i<1)
return false;
LNode *p;
int j =0;
//==寻找到第i个结点==
while(p!=NULL && j<i-1){
p=p->next;
j++;
}
if(p==NULL)
return false;
LNode *s=(LNode *)malloc(sizeod(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
不带头结点
插入第一个结点的操作与其他不同
bool ListInsert(LinkList &L,int i ,ElemType e){
if(i<1)
return false;
if(i==1){
//插入第一个节点的操作与其他不同
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=L;
L=s;
return true;
}
LNode *p;
int j =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;
}
指定结点的后插操作
在结点p后插入元素e
bool InsertNextNode(LNode *p,ElemType e){
if(p==NULL)
return false;
LNode *s=(LNode *)malloc(sizeof(LNode *));
s->data=e;
s->next=p->next;
p->next = s;
}
在指定结点前面插入前插
O(1)
将新加入的结点插到p后面,然后将s->data赋值为p->data,p->data赋值为e
bool InsertPriorNode(LNode *p, ElemType e){
if(p==NULL)
return false;
LNode *s = (LNode *)malloc(sizeof(LNode*))
//**********
s->next=p->next;
p->next;//把新的结点连接到p之后
s->data=p->data;
p->data=e;
return true;
}
删除
按照位序删除
带有头结点
bool ListDelete(LinkList &L,int i,ElemType &e){
if (i<1)
return false;
Lnode *p;//指针p指向大当前扫描到的结点
int j=0;//当前p指向的是第几个结点
p = L;//L指向有结点,头结点是第0个结点
while(p!=NULL && j<i-1){
p=p->next;
j=j+1;
}
if(p==NULL || p->next ==NULL)
return false;
LNode *q = p->next;//q指向被删除的结点
e = q->data;//用e返回元素的值
p->next = q->next;//跳过q指向原来q的下一个元素
free(q)//释放资源
return true;
}
O(n)
单链表没办法逆向检索
查
按位查找
LNode * GetElem(LinkList L,int i){
if(i<0)
return NULL;
int j=0;
LNode p;
p=L;//指向头结点
while(p!=NULL & j<i){
p=p->next;
j++;
}
return p;
}
O(n)
按值查找
LNode * GetElemByValue(LinkList L,ElemType e){
LNode *p = L->next;//指向第一个节点
while(p->data != e && p!=NULL){
p=p->next;
}
return p;
}
建立单链表
尾插法==设置一个指向表尾结点的指针
头插法
双链表
typedef struct DNode{
ElemType data;
struct DNode *pior,*next;
}DNode,*DLinklist;
双链表插入
p后面加上s结点
bool InsertNextDNode(DNode *p,DNode *s){
s->next=p->next;
p->next->prior=s;//前向指针
s->prior=p;
p->next=s;//后向指针
return true;
}
双链表删除
bool DleteNextDNode(DNode *p){
if(p==NULL)
return false;
DNode *q=p->next;
if(q==NULL)
return false;
p->next=q->next;
if(q->next!=NULL)
q->next->prior=p;
free(q);
retunr true;
}
循环单链表
单链表的最后一个结点的指针指向NULL
循环链表的最后䘝结点的指针指向头结点
循环双链表
循环双链表中的prior指针指向尾结点,next指向头结点
代码问题
如何判空
循环单链表:head.next==head
bool empty(LinkList L){
if(L->next == L){
return true;
}else
return false;
}
循环双链表:head.nexthead && head.priorhead
bool Empty(DLinkList L){
if(L->next ==L)
return true;
else:
return false;
}
如何判断结点p是否是表尾/表头结点
//单链表判断尾结点
表头:if(current.next ==L)
//判断双链表的表尾
if(current.next ==L)
如何在表头、表中、表尾插入/删除结点(书)
初始化
bool InitList(LinkList &L){
L=(LNode *)malloc(sizeod(LNode));
if(L==NULL)
return false;
L->next=L;
return true;
}
bool InitDLinkList(DLinkList &L){
L=(DNode *)malloc(sizeof(DNode))
if(L==NULL)
return false;
L->prior=L;
L->next=L;
return true;
}
静态链表
分配一整片的连续的内存空间,各个节点集中安置
每隔结点有(数据)+(游标),游标标明结点的下一个结点在数组中的位置
游标=-1表示该点位***尾结点***,
数组中0号结点作为***头结点***==头结点数据格没有值,
e0起始地址:add
e1起始地址:malloca(sizeof(e0))*e0的游标数+add
定义静态链表==容量不可以改
#define MaxSize 10
struct Node{
ElemType data;
int next;
};
void testDLinkList(){
struct Node a{MaxSize};
}
查找n(n)
插入位序为i的结点
1.找到一个空的结点,存入数据元素
2.从头结点出发找到位序为i1的结点
3.修改新结点的next
4.修改i-1号结点的next
适用场景
1.不支持指针的低级语言
2.数据元素数量固定不变的场景eg:文件分配表FAT
总结顺序表链表
顺序表的优点:支持随机存取,存储密度高
缺点:大片连续空间分配不方便,改变容量不方便
链表的优点:离散的小空间分配方便,改变容量方便
缺点:不可随机存取,存储密度低