chore: 尝试切换到 ali oss
This commit is contained in:
@@ -6,13 +6,13 @@
|
||||
|
||||
使用链表存储数据,不强制要求数据在内存中集中存储,各个元素可以分散存储在内存中。例如,使用链表存储 {1,2,3},各个元素在内存中的存储状态可能是:
|
||||
|
||||

|
||||

|
||||
|
||||
可以看到,数据不仅没有集中存放,在内存中的存储次序也是混乱的。那么,链表是如何存储数据间逻辑关系的呢?
|
||||
|
||||
链表存储数据间逻辑关系的实现方案是:为每一个元素配置一个指针,每个元素的指针都指向自己的直接后继元素,如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
显然,我们只需要记住元素 1 的存储位置,通过它的指针就可以找到元素 2,通过元素 2 的指针就可以找到元素 3,以此类推,各个元素的先后次序一目了然。像图 2 这样,数据元素随机存储在内存中,通过指针维系数据之间“一对一”的逻辑关系,这样的存储结构就是链表。
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
|
||||
在链表中,每个数据元素都配有一个指针,这意味着,链表上的每个“元素”都长下图这个样子:
|
||||
|
||||

|
||||

|
||||
|
||||
数据域用来存储元素的值,指针域用来存放指针。数据结构中,通常将这样的整体称为结点。
|
||||
|
||||
也就是说,链表中实际存放的是一个一个的结点,数据元素存放在各个结点的数据域中。举个简单的例子,图 3 中 {1,2,3} 的存储状态用链表表示,如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
在 C 语言中,可以用结构体表示链表中的结点,例如:
|
||||
|
||||
@@ -66,7 +66,7 @@ typedef struct Node* Link;
|
||||
|
||||
例如,创建一个包含头结点的链表存储 {1,2,3},如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
## 链表的创建
|
||||
|
||||
@@ -104,7 +104,7 @@ while (Judgement)
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
### 创建结点——尾插法
|
||||
|
||||
@@ -121,7 +121,7 @@ while (Judgement) //for同理
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
## 链表的基本操作
|
||||
|
||||
@@ -176,7 +176,7 @@ int GetElem(Link *L, int i; int *e)
|
||||
|
||||
例如,在链表 `{1,2,3,4}` 的基础上分别实现在头部、中间、尾部插入新元素 5,其实现过程如图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
从图中可以看出,虽然新元素的插入位置不同,但实现插入操作的方法是一致的,都是先执行步骤 1 ,再执行步骤 2。实现代码如下:
|
||||
|
||||
@@ -207,7 +207,7 @@ int ListInsert(Link *L, int i, int e)
|
||||
|
||||
对于没有头结点的链表,在头部插入结点比较特殊,需要单独实现。
|
||||
|
||||

|
||||

|
||||
|
||||
和 2)、3) 种情况相比,由于链表没有头结点,在头部插入新结点,此结点之前没有任何结点,实现的步骤如下:
|
||||
|
||||
@@ -253,7 +253,7 @@ temp->next=temp->next->next;
|
||||
|
||||
例如,从存有 `{1,2,3,4}` 的链表中删除存储元素 3 的结点,则此代码的执行效果如图 3 所示:
|
||||
|
||||

|
||||

|
||||
|
||||
实现代码如下:
|
||||
|
||||
@@ -282,7 +282,7 @@ int ListDelete(Link *L, int i, int* e)
|
||||
|
||||
对于不带头结点的链表,需要单独考虑删除首元结点的情况,删除其它结点的方式和图 3 完全相同,如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
实现代码如下:
|
||||
|
||||
@@ -319,7 +319,7 @@ int ListDelete(Link *L, int i, int* e)
|
||||
|
||||
如图所示,假设此时圆周周围有 5 个人,要求从编号为 3 的人开始顺时针数数,数到 2 的那个人出列:
|
||||
|
||||

|
||||

|
||||
|
||||
出列顺序依次为:
|
||||
|
||||
@@ -339,10 +339,10 @@ int ListDelete(Link *L, int i, int* e)
|
||||
|
||||
为了使空链表和非空链表处理一致,我们通常设一个头结点,当然,并不是说,循环链表一定要头结点,这需要注意。循环链表带有头结点的空链表如图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
对于非空的循环链表如图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断 p->next 是否为空,现在则是 p->next 不等于头结点,则循环未结束。
|
||||
|
||||
Reference in New Issue
Block a user