链表

基本概念

数据结构的三要素

  • 逻辑结构
    • 线性结构
  • 存储结构
    • 链式存储
  • 数据的运算

费曼理解

链表的存储就好像打游戏下副本时的传送门,我们首先看到传送阵,然后进入传送门才能进入下一个房间,而这房间就是数据所在地址,传送门则是指针,指向下个数据所在房间。

内部实现:

单链表

1
2
3
4
5
typedef struct LNode
{
Elempment data;
struct LNode *next; //指向下一数据所在地址
}LNode,*linklist //LNode 节点指针,指向某一个节点;*Linklist 链表头指针,指向头节点
  • 头结点:代表链表上头指针指向的第一个结点,不带有任何数据。
1
2
3
4
5
6
7
8
9
10
### 内存布局
```text

链表头指针 LinkList L

+----------------+----------------+ +----------------+----------------+ +----------------+----------------+
| data | next | --> | data | next | --> | data | next |
| (头节点) | 0x2000 | | 10 | 0x3000 | | 20 | NULL |
+----------------+----------------+ +----------------+----------------+ +----------------+----------------+
地址: 0x1000 地址: 0x2000 地址:0x3000

复杂度分析:

初始化一个不带头节点的空链表以及声明单链表

1
2
3
4
5
6
7
8
9
10
bool  InitList(Linklist &L)
{
L=NULL
return true;
}
void BuidList()
{
Linklist L;
Initlist(L);
}

复杂度分析

  • 时间复杂度:O(1)O(1)
  • 空间复杂度:O(1)O(1)

初始化 带头结点的链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool InitList(Linklist &L)
{
L=(LNode*)malloc(sizeof(LNode));
if (L==NULL){
return false;
}
L -> next ==NULL;
return true;
}
void BuidList()
{
Linklist L;
Initlist(L);
}

复杂度分析

  • 时间复杂度:O(1)O(1)
  • 空间复杂度:O(1)O(1)

按位序插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool ListInsert(Linklist &L,int i,Elempment e)
{
//边界探索
if(i<1)
return false;
LNode *p;
int j=0;
p=L;
while(p!=NULL&& j<i-1){
p=p->next; //更新p指向
j++;
}
if(p==NULL){ //if i>L.length,p=NULL
return false;
}
LNode *s =(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}

复杂度分析

  • 时间复杂度:
    • 最好情况:在头节点后插入元素-O(1)O(1)
    • 最坏情况:在链表尾插入元素或i>L.length-O(N)O(N)
    • 平均情况:在第n/2个节点插入-O(N)O(N)
  • 空间复杂度:O(1)O(1)

指定节点的后插操作

1
2
3
4
5
6
7
8
9
bool InsertNext(LNode *p,Elempment e){
if(p==NUll)
return false;
LNode *s =(LNode *)malloc(sizeof(LNode));
s=e;
s->next=p-next;
p->next=s;
return true;
}

复杂度分析

  • 时间复杂度:O(1)O(1)
  • 空间复杂度:O(1)O(1)

指定节点的前插操作

1
2
3
4
5
6
7
8
9
10
bool Insertpre(LNode *p,Elempment e){
if(p==NULL)
return false;
LNode *s=(LNode *)malloc(sizeof(LNdoe));
s->next=p->next;
p->next=s;
s->data=p->data;
p=->data=e;
return true;
}

复杂度分析

  • 时间复杂度:O(1)O(1)
  • 空间复杂度:O(1)O(1)

按位序删除节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool ListDelete(LNode &l,int i,ElenType &e)
{
if(i<1) return false;
LNode *p;
int j=0;
while(p!=NULL&&j<i-1){
p=p->next;
j++;
}
if(p==NULL) return false;
if(p->next == NULL) //第i-1个结点之后已无其他结点 return false;
LNode *q=p->next;
e=q->data; //引用型参数e会将p-next节点也就是第i个节点从链表断开
p->next=q->next;
free(q);
return true;
}

复杂度分析

  • 时间复杂度:
    • 最好情况:在头节点后删除元素-O(1)O(1)
    • 最坏情况:在链表尾删除元素或i>L.length-O(N)O(N)
    • 平均情况:在第n/2个节点删除-O(N)O(N)
  • 空间复杂度:O(1)O(1)

指定结点的删除

1
2
3
4
5
6
7
8
bool DeleteNode(LNode *p){
if(p==NULL) return false;
LNode *q =p->next;
p->data=p->next->data;
p->next=q->next;
free(q);
return true;
}

复杂度分析

  • 时间复杂度:O(1)O(1)
  • 空间复杂度:O(1)O(1)

按位查找

1
2
3
4
5
6
7
8
9
10
11
12
LNode* GetElem(Linklist L,int i)
{
LNode *p=l;
int j=0;
p=L;
while(p!=null&&j<i)
{
p=p->next;
j++;
}
return p;
}

复杂度分析

  • 时间复杂度:
    • 最好情况:在头节点后插入元素-O(1)O(1)
    • 最坏情况:在链表尾插入元素或i>L.length-O(N)O(N)
    • 平均情况:在第n/2个节点插入-O(N)O(N)
  • 空间复杂度:O(1)O(1)

按值查找

1
2
3
4
5
6
7
8
9
10
LNode* LocalElem(Linklist L,ElemType e)
{
LNode *p;
p=L;
while(p!=null&&p->data!=e)
{
p=p->next;
}
return p;
}

复杂度分析

  • 时间复杂度:
    • 最好情况:在头节点后插入元素-O(1)O(1)
    • 最坏情况:在链表尾插入元素或i>L.length-O(N)O(N)
    • 平均情况:在第n/2个节点插入-O(N)O(N)
  • 空间复杂度:O(1)O(1)

单链表的长度

1
2
3
4
5
6
7
8
9
10
11
int Length(Linklist L)
{
int len=0;
LNode *p=L;
while(p->next!=NULL)
{
p=p->next;
len++;
}
return len;
}

复杂度分析

  • 时间复杂度:O(N)O(N)
  • 空间复杂度:O(1)O(1)

头插法建立单链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Linklist List_Headsert(Linklist &L)
{
LNode *s;
int x;
L=(Linklist)malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while(x!=9999)
{
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}

复杂度分析

  • 时间复杂度:O(N)O(N)
  • 空间复杂度:O(N)O(N)

尾插法建立单链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Linklist list_taiInsert(Linklist &L)
{
int x;
L=(Linklist)malloc(sizeof(LNode));
LNode *s,*r=L;
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;
return L;
}

复杂度分析

  • 时间复杂度:O(N)O(N)
  • 空间复杂度:O(N)O(N)

应用场景: