2.從尾到頭打印單鏈表
3.刪除一個無頭單鏈表的非尾節點
4.在無頭單鏈表的一個節點前插入一個節點
5.單鏈表實現約瑟夫環
6.逆置/反轉單鏈表
7.單鏈表排序(冒泡排序&快速排序)
8.合併兩個有序鏈表,合併後依然有序
9.查找單鏈表的中間節點,要求只能遍歷一次鏈表
10.查找單鏈表的倒數第k個節點,要求只能遍歷一次鏈表
解答:
1.比較順序表和鏈表的優缺點,說說它們分別在什麼場景下使用?
順序表:順序表的地址空間是連續的,空間利用不充分。 查找速度快,便於大量元素的查找訪問。但是不利於增、刪。
鏈表: 鏈表的地址空間是不連續的,對於內存空間不會造成浪費。便於大量元素的指定位置增、刪。但查找速度相對較慢。
(二者特性剛好互補。)
2.從尾到頭打印單鏈表
//遞歸解決
void printfromback(ListNode* mylist)
{
//1沒有節點或者只有一個節點
//2有多個節點
if(mylist != NULL)
{
return ;
}
else
{
printfromback(mylist->next);
printf("%d ",mylist->data);
}
}
3.刪除一個無頭單鏈表的非尾節點
//無頭鏈表刪除
//後一個節點覆蓋前一個節點 在釋放後一個節點
void deletnoheadlist(ListNode** pos )
{
ListNode* next;
assert(*pos);
assert((*pos)->next);
next = (*pos)->next;
(*pos)->data = next->data;
(*pos)->next = next->next ;
free(next);
}
4.在無頭單鏈表的一個節點前插入一個節點
//無頭鏈表插入
//在pos後面插入一個數,再交換兩個節點的內容
void insertonheadlist(ListNode** pos, DataType input)
{
int tmp;
ListNode* cur = *pos;
ListNode* NewNode = BuyNode(input);
//插入新節點
NewNode->next = (*pos)->next;
(*pos)->next = NewNode;
//交換data
tmp = NewNode->data ;
NewNode->data = cur->data;
(*pos)->data = tmp;
}
5.單鏈表實現約瑟夫環
//約瑟夫環
//1把鏈表連起來
ListNode* JosephRing(ListNode** ppList, int k)
{
//1空鏈表
//2非空
ListNode* cur = *ppList;
if(*ppList == NULL)
{
return ;
}
while(cur->next != NULL)
{
cur = cur->next;
}
cur->next = (*ppList);
while((*ppList)->next != *ppList)
{
int loop = k;
while(--loop)
{
(*ppList) = (*ppList)->next;
}
//刪除
(*ppList)->data = (*ppList)->next->data;
(*ppList)->next = (*ppList)->next->next;
}
return *ppList;
}
6.逆置/反轉單鏈表
//鏈表逆置 頭插法
//建一個新鏈表 在新鏈表上頭插原來的節點元素
ListNode* ReverseList(ListNode** ppList)
{
//1鏈表爲空 或者只有一個元素
//2鏈表不爲空
ListNode* cur = *ppList;
ListNode* newlist=NULL ;
if((*ppList)->next == NULL || *ppList == NULL)
{
return ;
}
else
{
ListNode* tmp;
while(cur != NULL)
{
//摘節點
tmp = cur;
cur = cur->next;
//放節點
tmp->next =newlist;
newlist = tmp;
}
return newlist;
}
}
7.單鏈表排序(冒泡排序&快速排序)
//冒泡排序鏈表
void bubble_sotr_list(ListNode* pList)
{
//1鏈表爲空或者只有一個節點
//2有多個節點
ListNode* cur = pList;
ListNode* nextnode = cur->next;
ListNode* stop = NULL;
DataType tmp;
if((pList) == NULL || nextnode == NULL)
{
return ;
}
else
{
while(stop != (pList)->next)
{
cur = pList;
nextnode = cur->next;
while(nextnode != stop)
{
if((cur->data) > (nextnode->data))
{
tmp = cur->data;
cur->data = nextnode->data;
nextnode->data = tmp;
}
cur =cur->next;
nextnode = nextnode->next;
}
stop = cur;
}
}
}
8.合併兩個有序鏈表,合併後依然有序
//合併兩個有序鏈表,合併後依然有序
ListNode* MergeList(ListNode* List1,ListNode* List2)
{
//1兩個鏈表都爲空
//2其中一個鏈表爲空
//3兩個鏈表都不爲空
ListNode* Newlist = NULL;
ListNode* Newlisthead = NULL;
if(List1 == NULL && List2 == NULL)
{
return NULL;
}
else if (List1 != NULL && List2 == NULL)
{
return List1;
}
else if (List1 == NULL && List2 != NULL)
{
return List2;
}
else
{
//摘鏈表頭
if(List1->data < List2->data)
{
Newlist = List1;
List1 = List1->next;
}
else
{
Newlist = List2;
List2 = List2->next;
}
Newlisthead = Newlist;
//往節點後面加節點
while(List1 !=NULL && List2 != NULL)
{
if(List1->data < List2->data)
{
Newlist->next = List1;
Newlist = Newlist->next;
List1 = List1->next;
}
else
{
Newlist->next = List2;
Newlist = Newlist->next;
List2 = List2->next;
}
}
//把剩餘的鏈表鏈接上
if(List1 == NULL)
{
Newlist->next = List2;
}
if(List2 == NULL)
{
Newlist->next = List1;
}
return Newlisthead;
}
}
9.查找單鏈表的中間節點,要求只能遍歷一次鏈表
//查找鏈表中心元素 (只能遍歷一次)
//快慢指針
ListNode* find_list_mid(ListNode* pList)
{
//1爲空
//2只有一個或者兩個節點
//3有多個節點
ListNode* slow = pList;
ListNode* fast = pList;
if(pList == NULL)
{
return NULL ;
}
else if(pList->next == NULL || pList->next->next == NULL)
{
return pList;
}
else
{
while(fast && fast->next != NULL) //奇數偶數節點問題
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
}
10.查找單鏈表的倒數第k個節點,要求只能遍歷一次鏈表
//找倒數第k個元素
ListNode* find_tail_k(ListNode* pList,int k)
{
//1鏈表爲空
//2鏈表元素少於k
//3鏈表元素多餘k
ListNode* slow = pList;
ListNode* fast = pList;
if(pList == NULL)
{
return NULL;
}
while(--k) //先走k步
{
fast = fast->next;
if(fast == NULL)
{
return NULL ;
}
}
while(fast->next != NULL) //一起走
{
slow = slow->next;
fast = fast->next;
}
return slow;
}