【一週程式設計學習】--2.單鏈表與環形連結串列的實現
阿新 • • 發佈:2019-01-05
1.單鏈表知識小結
- 構成:頭指標(Header),若干個節點(節點包括了資料域和指標域),最後一個節點要指向空。
- 單鏈表的基本操作初始化:
typedef int ElemType; typedef struct Node{ ElemType data; struct Node* next; }Node; typedef struct Node* LinkList; void initList(LinkList *L){ (*L)=(LinkList)malloc(sizeof(Node)); (*L)->next=NULL; (*L)->data=0;}
- 單鏈表的插入:
s=(LinkList)malloc(sizeof(Node));
s->data=e;
s->next=p->next;
p->next=s;
- 單鏈表的刪除:
q=p-next;
p->next=q->next;
*e=q->data;
free(q);
- LinkList L、LinkList *L、Node *p、Node p的用法:
LinkList L: L是指向定義的node結構體的指標,
LinkList *L:L是指向定義的Node結構體指標的指標,用頭指標表示連結串列類,即實質是該連結串列的頭指標型別
Node *p :定義了一個node型別的結構體指標p,p是工作指標(初始時p指向)
Node p:在網上到時沒有查到Node p的使用,不過倒是查到了P=new node()的使用,也表示node型別的指標
如果函式會改變指標L的值,而你希望函式結束呼叫後儲存L的值,那你就要用LinkList *L,這樣,向函式傳遞的就是指標的地址,結束呼叫後,自然就可以去改變指標的值;而如果函式只會修改指標所指向的內容,而不會更改指標的值,那麼用LinkList L就行了;
2.LeetCode第206題 單鏈表的翻轉
單鏈表的反轉是對結點一個一個操作的,每次把後面的一個結點拋到前面,不需要開闢另外的記憶體空間。
使用C++和Python實現:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==NULL)
return NULL;
//結點初始化
ListNode *pCur,*pPre,*pNext;
pPre=head;
pCur=pPre->next;
//翻轉連結串列
while(pCur){
pNext=pCur->next;
pCur->next=pPre;
pPre=pCur;
pCur=pNext;
}
//返回頭指標
head->next=NULL;
head=pPre;
return head;
}
};
class Solution:
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if head is None:
return None
p=head
pCur=None
pPre=None
while p is not None:
pCur=p.next
p.next=pPre
pPre=p
p=pCur
return pPre
3. LeetCode 第142題 環形連結串列
- 方法一:快慢指標法
判斷一個單鏈表是否有環的問題。如果一個單向連結串列不帶環,那尾部結點的next指標是NULL,否則尾結點的指標指向連結串列中的某一結點的資料域。通常可採用快慢指標,定義兩個指標,一個指標一次移動一個結點slowP,另一個指標一次移動兩個結點fastP,如果兩個指標相遇,那麼是有環;否則快指標指向NULL,則是無環;第一次相遇時,slowP指向頭結點,fastP指向相遇的結點處,每次移動一個結點,直到再次相遇,得到位置。
使用C++和Python實現:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL||head->next==NULL)
return NULL;
ListNode* fastP=head;
ListNode* slowP=head;
while(slowP!=NULL&&fastP!=NULL&&fastP->next!=NULL)
{
slowP=slowP->next;
fastP=fastP->next->next;
if(slowP==fastP)
{
ListNode *slowP1=head;
while(slowP1!=slowP)
{
slowP=slowP->next;
slowP1=slowP1->next;
}
return slowP;
}
}
return NULL;
}
};
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
slow,fast=head,head
while fast and fast.next:
slow=slow.next
fast=fast.next.next
if slow==fast:
slow=head
while fast!=slow:
fast=fast.next
slow=slow.next
return slow
- 方法二:雜湊解法
使用unordered_map記錄當前節點是否被訪問過,如訪問過返回該節點,如到達尾部說明無環。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_map<ListNode*,bool> visited;
while(head!=NULL)
{
if(visited[head]==true)
return head;
visited[head]=true;
head=head->next;
}
return NULL;
}
};
快慢指標的應用:
- 判斷一個連結串列是否有環
- 求一個連結串列是否存在環,如果存在,則求出環的入口結點
- 另一個應用是求連結串列是否存在環的變式,如給定兩個連結串列A和B,判斷兩個連結串列是否相交,解決方法就是將A連結串列尾節點指向頭結點形成一個環,檢測B連結串列是否存在環,如果存在,則兩個連結串列相交,而檢測出來的依賴環入口即為相交的第一個點。
- 求有序連結串列中求出其中位數,這種問題也是設定快慢指標,當快指標到底連結串列尾部的時候,慢指標剛好指向連結串列中間的結點。