1. 程式人生 > >連結串列-雙向非通用連結串列

連結串列-雙向非通用連結串列

[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