連結串列-雙向非通用連結串列
阿新 • • 發佈:2020-10-10
[toc]
---
## 前言
* 20201010
* 在閱讀 RTOS LiteOS 核心原始碼時發現該核心使用的連結串列時**通用連結串列**,而 FreeRTOS 核心使用的時**非通用連結串列**,所以,有必要釋出一下關於連結串列實現的筆記。
* 以下內容為個人筆記,涉及一些非專業詞彙,敬請諒解,謝謝。
### 連結
* [我的Gitee](https://gitee.com/lidreaming)
* [非通用連結串列完整C語言原始碼](https://www.cnblogs.com/lizhuming/p/13792662.html)
### 參考
* *上面連結*
* FreeRTOS 核心原始碼
* 野火
## 概念
* 正常表達
* 連結串列:
* 連結串列為 C 中一種基礎的資料結構。
* 看成環形晾衣架即可。
* 節點:
* 節點組成連結串列
* 自理解概念
* 連結串列:圓形的晾衣架
* 節點:掛鉤
* 包含上一個
* 下一個
* 鉤子等其它需要的資訊
* 襪子:掛在到 **鉤子** 的東西
* 包含**被鉤子**
* 襪子攜帶的資訊
![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201010151010312-299469988.png)
* **通用連結串列與非通用連結串列的區別**
* 通用連結串列節點內容很少一般只有 **上一個** 和 **下一個**。
* 通用連結串列節點被放到資訊結構體中,通過偏移找到所在的結構體(即是通過偏移找到襪子頭)
* 而非通用連結串列是在節點中攜帶資訊結構體的指標的(即是節點就攜帶資訊)。
* ***別人通俗理解,讀者不必理會本小點***
* 通用連結串列是把襪子放到晾衣架的圓形圈上,襪子與圓形圈接觸部分為襪子接待的節點。(***資訊攜帶節點***)
* 非通用連結串列是。(***節點攜帶資訊***)
## 筆錄草稿
## 雙向連結串列
* 雙向連結串列理解圖
* 原理:連結串列包括 **根節點** 和 **普通節點**
* **根節點** 主要管理連結串列的,一般包括
* 上一個
* 下一個
* 存在多少個等資訊
![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201010151036592-974377954.png)
* **普通節點** 主要用於鉤住襪子(即是攜帶資訊)
### 節點及節點結構體程式碼
* 普通節點
* 存放節點資訊
* **掛載東西(掛鉤),如掛載襪子等等**
![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201010151112205-1621246703.png)
```c
/*
* The linked list node
*/
struct LIST_ITEM_LSS_T
{
struct LIST_ITEM_LSS_T * pxNext; // 下一個
struct LIST_ITEM_LSS_T * pxPrevious; // 上一個
/* 節點屬性,(根據個人需求增減以下內容) */
uint32_t xItemValue; // 記號值,一般用於排序
void * pvOwner; // 掛鉤,即攜帶的資訊
void * pvContainer; // 歸屬,即屬於哪一個連結串列
};
typedef struct LIST_ITEM_LSS_T listItem_t;
```
* root節點(連結串列點)
* 存放連結串列的資訊
* 有一個**mini節點,用於駁接和定位(相當於位置校準點),不掛載如何東西,且簡潔為妙**
* **mini節點**的記號值在雙向連結串列中為最大值,因為最大是尾也是頭。
![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201010151136748-2080404947.png)
```c
/* mini 節點結構體 */
struct LIST_MINI_ITEM_LSS_T
{
/* 指向,(根據單、雙鏈表刪減以下內容) */
struct node *pxPrevious; // 上一個節點
struct node *pxNext; // 下一個節點
/* 節點屬性,(根據個人需求增減以下內容) */
uint32_t xItemValue; // 記號值,在雙向連結串列中為最大值
};
typedef struct LIST_MINI_ITEM_LSS_T ListMiniItem_t;
/* 連結串列結構體,即根節點 */
struct LIST_LSS_T
{
/* 節點屬性,(根據個人需求增減以下內容) \*/
uint32_t uxNumberOfItems; // 節點數,統計粘附在本連結串列上的節點數
struct LIST_ITEM_LSS_T * pxIndex; // 索引,連結串列索引,指向連結串列中的某個節點
struct LIST_MINI_ITEM_LSS_T xListEnd; // 連結串列根節點
};
typedef struct LIST_LSS_T List_t;
```
### 連結串列操作的函式程式碼
#### 連結串列初始化函式
1. 連結串列索引指向該連結串列的**尾節點**(尾節點,即也是頭節點)
2. 連結串列**尾節點**記號值賦值為最大值(***根節點包含尾節點***)
3. 初始化尾節點的**上一個**及**下一個**,分別都指向**尾節點
4. 初始化節點總數為 0。
```c
/**
* @brief 連結串列初始化
* @param
* @retval
* @author lzm
*/
void listInit(list_t * const list)
{
/* 索引指向最後:尾就是頭 */
list->pxIndex = (listItem_t *) &(list->xListEnd);
/* 連結串列最大值 */
list->xListEnd.xItemValue = lssLIST_MAX_VALUE;
list->xListEnd.pxNext = ( listItem_t * ) &( list->xListEnd );
list->xListEnd.pxPrevious = ( listItem_t * ) &( list->xListEnd );
list->uxNumberOfItems = (lssLIST_BASE_TYPE)0U;
}
```
#### 節點初始化函式
1. 初始化節點攜帶的資訊為空
```c
/**
* @brief 節點初始化
* @param
* @retval
* @author lzm
*/
void listItemInit(listItem_t * const item)
{
item->pvContainer = NULL;
}
```
#### 節點插入連結串列尾部函式
***注意***:需要插入的節點以下稱為**節點A**
1. 獲取索引(索引即遊標,也就是該連結串列當前指向的節點)
2. **節點A**的**下一個**指向**索引節點**,
3. **節點A**的**前一個**指向**索引節點**的**前一個**
4. **索引節點前一個**的**下一個**指向**節點A**
5. **索引節點**的**前一個**指向**節點A**
6. 設定**節點A**歸屬於哪個連結串列
7. 連結串列節點計數值 +1
```c
/**
* @brief 插入連結串列尾部(*雙向連結串列沒有絕對的頭尾,此處是以遊標為參考物*)
* @param
* @retval
* @author lzm
*/
void listInsertEnd( list_t * const pxList, listItem_t * const pxNewListItem )
{
listItem_t * const pxIndex = pxList->pxIndex;
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
```
#### 節點有序插入連結串列函式
***注意***:需要插入的節點以下稱為**節點A**
1. 獲取新節點記號值
2.找出需要插入的位置
1. 如果記號值為連結串列中最大值(即和**尾節點**的記號值相等),則取出尾節點的前一個節點作為參考節點
2. 如果記號值**不**為連結串列中最大值,則從尾節點開始找,直至找到**當前節點**的**下一個節點**的記號值為**大於 節點A**的記號值(即是在連結串列中找出紅框節點B)
![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201010151241361-1257329753.png)
3. 插入節點
1. **節點A**(***節點A*** 為需要插入的節點)的**下一個**指向**索引節點**,
2. **節點A**的**前一個**指向**節點B**的**前一個**
3. **節點B的前一個**的**下一個**指向**節點A**
4. **節點B**的**前一個**指向**節點A**
5. 設定**節點A**歸屬於哪個連結串列
6. 連結串列節點計數值 +1
```c
/**
* @brief 按記號值值插入
* @param
* @retval
* @author lzm
*/
void listInsert( list_t * const pxList, listItem_t * const pxNewListItem )
{
listItem_t *pxIterator;
const lssLIST_BASE_TYPE xValueOfInsertion = pxNewListItem->xItemValue; // 獲取新節點記號值
/* 按順序尋找 */
if( xValueOfInsertion == lssLIST_MAX_VALUE )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else
{
for( pxIterator = ( listItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
{
/* There is nothing to do here, just iterating to the wanted insertion position. */
}
}
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* Remember which list the item is in. This allows fast removal of the
item later. */
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
```
#### 從連結串列中刪除函式
***注意***:被刪除的節點以下稱為**節點A**
1. 獲取連結串列
2. **節點A下一個節點**的**前一個**指向**節點A的前一個節點**
3. **節點A前一個節點**的**下一個**指向**節點A的下一個節點**
4. 檢查連結串列索引是否指向了**節點A**
1. 是:索引更新為指向**節點A的前一個節點**
2. 否:跳過
5. 清空**節點A**的連結串列歸屬
6. 連結串列節點計數值 -1
7. 返回連結串列節點數
```c
/**
* @brief 從連結串列中刪除節點
* @param
* @retval
* @author lzm
*/
uint32_t listRemove( listItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
item. */
list_t * const pxList = ( list_t * ) pxItemToRemove->pvContainer;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
pxItemToRemove->pvContainer = NULL;
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
```
## 原始碼集合
* 內含
* 節點及連結串列結構體
* 節點初始化函式
* 連結串列初始化函式
* 節點插入函式
* 刪除節點函式
* 配對掛鉤與襪子函式
* 獲取節點資訊函式
* 獲取記號值函式
* 獲取第一個節點的節點值函式
* 獲取連結串列的入口節點函式
* 獲取下一個節點函式
* 獲取連結串列最後一個節點(尾節點)函式
* 判斷連結串列是否為空函式
* 獲取連結串列當前節點總數函式
* 獲取連結串列索引指向的下一個節點函式
* 跳轉到[非通用連結串列完整C語言原始碼](https://www.cnblogs.com/lizhuming/p/13792662.htm