前言:
線性表可分爲:順序表與鏈表。其中,順序表又可分爲動態的和靜態的兩種,鏈表可分爲單向鏈表、單向循環鏈表、雙向鏈表、雙向循環鏈表等。本篇文章主要講解動態順序表與單向鏈表的區別與應用場景以及關於鏈表的一些經典例題。
正文:
順序表與單鏈表的區別:
- 順序表可以實現下標的快速訪問,單鏈表則不可以,單鏈表必須從頭依次遍歷查找。
- 順序表在中間或者頭部插入節點時必須依次挪動後面節點到下一個節點位置,然而單鏈表卻不用,單鏈表插入、刪除節點比較方便。
- 順序表每次增容固定大小的空間有可能造成空間浪費,但不用每次插入時都動態開闢空間,單鏈表每次插入節點時都必須動態開闢空間、刪除節點時必須釋放掉動態開闢的空間。
- 由於計算機設計的多級緩存機制遵循局部性原理,所以連續訪問順序表時緩存命中率較高,而單鏈表本身存儲比較分散,連續訪問時緩存命中率較低還會造成緩存污染。
順序表與單鏈表的應用場景:
- 順序表:
- 尾插尾刪較多使用順序表。
- 單鏈表:
- 頭部或者中間插入較多使用單鏈表。
單鏈表經典例題:
//1.從尾到頭打印單鏈表
//2.刪除一個無頭單鏈表的非尾節點
//3.在無頭單鏈表的一個節點前插入一個節點
//4.單鏈表實現約瑟夫環
//5.逆置 / 反轉單鏈表
//6.單鏈表排序(冒泡排序&快速排序)
//7.合併兩個有序鏈表, 合併後依然有序
//8.查找單鏈表的中間節點,要求只能遍歷一次鏈表
//9.查找單鏈表的倒數第k個節點,要求只能遍歷一次鏈表
基本函數:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DataType;//節點中數據類型
typedef struct ListNode//節點數據結構
{
DataType data;
struct ListNode *next;
} ListNode;
ListNode *Find(ListNode *plist,DataType x)//查找函數,返回查找到節點的地址
{
ListNode *cur = plist;
while (cur)
{
if ((cur->data) == x)
{
return cur;
}
cur = cur->next;
}
return cur;
}
ListNode *BuyNode(DataType x)//產生一個節點並把節點中的數據置數,返回節點地址
{
ListNode *plist = (ListNode *)malloc(sizeof(ListNode));
if (plist != NULL)
{
plist->data = x;
plist->next = NULL;
return plist;
}
return NULL;
}
void PrintList(ListNode *plist)//打印單鏈表
{
assert(plist != NULL);
while (plist)
{
printf("%d->",plist->data);
plist = plist->next;
}
printf("NULL\n");
}
void PushBuck(ListNode **pplist,DataType x)//在函數尾部插入節點
{
ListNode *cur = NULL;
assert(pplist);
cur = *pplist;
if (*pplist == NULL)
{
*pplist = BuyNode(x);
return;
}
while ((cur->next) != NULL)
{
cur = cur->next;
}
cur->next = BuyNode(x);
}
從尾到頭打印單鏈表:
void PrintTailToHead(ListNode *plist)//從尾到頭打印單鏈表(採用遞歸實現)
{
if (plist == NULL)
return;
PrintTailToHead(plist->next);
printf("%d->", plist->data);
}
測試貼圖:
刪除一個無頭單鏈表的非尾節點:
void EraseNonTail(ListNode *pos)//刪除一個無頭單鏈表的非尾節點
{
ListNode *tmp = NULL;
assert(pos);
assert(pos->next);
pos->data = pos->next->data;
tmp = pos->next;
pos->next = pos->next->next;
free(tmp);
tmp = NULL;
}
測試貼圖:
在無頭單鏈表的一個節點前插入一個節點:
void InsertBeforeNode(ListNode *pos,DataType x)//在無頭單鏈表的一個節點前插入一個節點
{
ListNode *tmp = NULL;
DataType ret = 0;
assert(pos);
tmp = BuyNode(x);
tmp->next = pos->next;
pos->next = tmp;
ret = pos->data;
pos->data = tmp->data;
tmp->data = ret;
}
測試貼圖:
單鏈表實現約瑟夫環:
ListNode *JosephRing(ListNode *plist,int k)//單鏈表實現約瑟夫環
{
int count = 0;
ListNode *tmp = 0;
assert(plist);
while (plist != plist->next)
{
count = k;
while (--count)
{
plist = plist->next;
}
plist->data = plist->next->data;
tmp = plist->next;
plist->next = plist->next->next;
free(tmp);
tmp = NULL;
}
return plist;
}
測試貼圖:
逆置 / 反轉單鏈表:
ListNode *Reverse(ListNode *plist)//逆置 / 反轉單鏈表
{
ListNode *phead = NULL;
ListNode *tmp = NULL;
if ((plist == NULL) || (plist->next == NULL))
return plist;
else
{
while (plist)
{
tmp = plist;
plist = plist->next;
if (phead == NULL)
{
phead = tmp;
tmp->next = NULL;
}
else
{
tmp->next = phead;
phead = tmp;
}
}
return phead;
}
}
結果貼圖:
單鏈表排序:
ListNode *Bubblesort(ListNode *plist)//單鏈表排序(冒泡排序&快速排序)
{
if ((plist == NULL) || (plist->next == NULL))
{
}
else
{
ListNode *cur = NULL;
ListNode *next = NULL;
ListNode *tail = NULL;
while (tail != plist->next)
{
cur = plist;
next = plist->next;
while (next != tail)
{
if (cur->data > next->data)
{
DataType tmp = next->data;
next->data = cur->data;
cur->data = tmp;
}
cur = next;
next = next->next;
}
tail = cur;
}
cur = next = tail = NULL;
}
return plist;
}
結果貼圖:
合併兩個有序鏈表:
ListNode *Merge(ListNode *plist1,ListNode *plist2)//合併兩個有序鏈表, 合併後依然有序
{
ListNode *tail = NULL;
ListNode *tmp = NULL;
ListNode *plist = NULL;
if (plist1 == NULL)
{
return plist2;
}
if (plist2 == NULL)
{
return plist1;
}
while (plist1 && plist2)
{
if (plist1->data < plist2->data)
{
tmp = plist1;
plist1 = plist1->next;
}
else
{
tmp = plist2;
plist2 = plist2->next;
}
if (plist == NULL)
{
plist = tmp;
tail =ist;
}
else
{
tail->next = tmp;
tail = tmp;
}
}
if (plist1 == NULL)
{
tail->next = plist2;
}
else
{
tail->next = plist1;
}
return plist;
}
結果貼圖:
查找單鏈表的中間節點:
ListNode *FindMidNode(ListNode *plist)//查找單鏈表的中間節點,要求只能遍歷一次鏈表
{
assert(plist);
ListNode *fast = plist;
ListNode *slow = plist;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
結果貼圖:
查找單鏈表的倒數第k個節點:
ListNode *FindTailKNode(ListNode *plist,int k)//查找單鏈表的倒數第k個節點,要求只能遍歷一次鏈表
{
ListNode *fast = plist;
ListNode *slow = plist;
assert(plist);
while (--k)
{
fast = fast->next;
assert(fast);
}
while (fast->next != NULL)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
結果貼圖: