1. 程式人生 > >【一週程式設計學習】--2.單鏈表與環形連結串列的實現

【一週程式設計學習】--2.單鏈表與環形連結串列的實現

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連結串列是否存在環,如果存在,則兩個連結串列相交,而檢測出來的依賴環入口即為相交的第一個點。
  • 求有序連結串列中求出其中位數,這種問題也是設定快慢指標,當快指標到底連結串列尾部的時候,慢指標剛好指向連結串列中間的結點。