鏈表專題——面試中常見的鏈表問題


聲明: 鏈表定義如下:

//Java:
class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
//C++:
typedef struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
}ListNode;

 

從無頭單鏈表中刪除節點

詳情:給定一個沒有頭指針的單鏈表,一個指針指向此單鏈表中間的一個節點(不是第一個,也不是最后一個節點),請將該節點從單鏈表中刪除。
題解:
解法一:由於單鏈表並沒有給出頭指針,因此我們無法通過遍歷鏈表的方式找到該節點的前一個節點來改變其 next 指向去指向該節點的 next 節點。換一種思路,我們可以將該節點的元素值全部替換成其 next 節點,然后刪除 next 節點,這樣就相當於把該節點刪除了。

//Java
public void deleteRandomNode(ListNode currentNode) {
    ListNode nextNode = currentNode.next;
    if (nextNode != null) {
        currentNode.val = nextNode.val;
        currentNode.next = nextNode.next;
    }
    nextNode = null;
}
//C++
void deleteRandomNode(ListNode *current){
	ListNode *next = current->next;
	if (next != NULL){
		current->val = next->val;
		current->next = next->next;
	}
	delete next;
}

 

反轉鏈表

詳情:給定一個鏈表的頭指針,要求只遍歷一次,將單鏈表中的元素順序反轉過來。
題解:
解法一:題目較為簡單,每次反轉的時候記錄下一個節點的指針

//Java
public ListNode ReverseList(ListNode head) {
    ListNode pre = null, next = null;
    while (head != null) {
        next = head.next;
        head.next = pre;
        pre = head;
        head = next;
    }
    return pre;
}
//C++
ListNode *ReverseList(ListNode *pHead) {
    ListNode *current = NULL, *prev = NULL;
    while (pHead != NULL) {
        current = pHead;
        pHead = pHead->next;
        current->next = prev;
        prev = current;
    }
    return current;
}

 

兩個鏈表的第一個公共節點

詳情:輸入兩個鏈表,找出它們的第一個公共節點
題解:
解法一:為了找到兩個鏈表的公共節點,那么我們可以從尾往頭遍歷查找,但是只給了我們頭節點,因此類似於棧的先進后出,因此我們可以用兩個棧來保存節點,然后從棧中取出節點進行比較。
解法二:統計兩個鏈表的長度 len1 和 len2,讓較長的鏈表先走abs(len1 - len2)長度,之后二者同時繼續往下遍歷,查找第一個公共節點。

//C++
ListNode *FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
    int len1 = SizeLinkedList(pHead1);
    int len2 = SizeLinkedList(pHead2);

    if (len1 > len2) {
        pHead1 = walker(pHead1, len1 - len2);
    } else {
        pHead2 = walker(pHead2, len2 - len1);
    }

    while (pHead1->val != pHead2->val) {
        pHead1 = pHead1->next;
        pHead2 = pHead2->next;
    }

    return pHead1;
}

int SizeLinkedList(ListNode *head) {
    if (head == NULL)   return 0;
    int size = 0;
    ListNode *current = head;
    while (current != NULL) {
        size++;
        current = current->next;
    }
    return size;
}

ListNode *walker(ListNode *head, int cnt) {
    while (cnt--) {
        head = head->next;
    }
    return head;
}

 

判斷給定鏈表是否存在環

詳情:給定一個鏈表,判斷這個鏈表是否存在環
題解:
解法一:Floyd判圈算法

//C++
bool hasRing(ListNode *pHead){
	bool hasRing = false;
	ListNode *fast = pHead, *slow = pHead;
	while (fast != NULL && fast->next != NULL){
		fast = fast->next->next;
		slow = slow->next;
		if (fast == slow)	hasRing = true;
	}
	return hasRing;
}

 

鏈表中環的入口節點

詳情:給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。
題解:
解法一:Floyd判圈算法

//Java
public ListNode EntryNodeOfLoop(ListNode pHead) {
    ListNode fast = pHead, slow = pHead;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow)   break;
    }
    if (fast == null || fast.next == null)  return null;
    fast = pHead;
    while (fast != slow) {
        fast = fast.next;
        slow = slow.next;
    }

    return fast;
}
//C++
ListNode *EntryNodeOfLoop(ListNode *pHead) {
    ListNode *slow = pHead, *fast = pHead;
    while (fast != NULL && fast->next != NULL) {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)   break;
    }
    if (fast == NULL || fast->next == NULL)   return NULL;
    fast = pHead;
    while (fast != slow) {
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}

 

判斷兩個鏈表是否相交

詳情:給定兩個單鏈表的頭指針,判斷這兩個鏈表是否相交。
題解:
解法一:若兩個鏈表相交,則鏈表的最后一個節點一定是公共的,因此可以利用這個性質求解。

//C++
bool isIntersect(ListNode *pHead1, ListNode *pHead2){
	if (pHead1 == NULL || pHead2 == NULL)	return false;
	while (pHead1->next != NULL)	pHead1 = pHead1->next;
	while (pHead2->next != NULL)	pHead2 = pHead2->next;
	if (pHead1 == pHead2)	return true;
	return false;
}

解法二:由於都是單項鏈表,也就是都沒有環,那么我們可以把第一個鏈表鏈接到第二個鏈表后面,如果新的鏈表有環,證明了有公共節點。

//C++
bool isIntersect(ListNode *pHead1, ListNode *pHead2){
	if (pHead1 == NULL || pHead2 == NULL)	return false;
	pHead1->next = pHead2;
	return hasRing(pHead1);
}

 

判斷兩個鏈表是否相交變形

詳情:給定兩個有環鏈表的頭指針,判斷這兩個鏈表是否相交。
題解:
解法一:對於有環鏈表,如果相交,存在以下幾種情況:

因此,找到鏈表的入口節點,判斷是否相等,對應情形一和二,對於三,我們可以固定一個節點,然后遍歷鏈表來判斷是否存在相交。

//C++
bool isIntersect(ListNode *pHead1, ListNode *pHead2){
	if (pHead1 == NULL || pHead2 == NULL)	return false;
	ListNode *entry1 = EntryNodeOfLoop(pHead1);
	ListNode *entry2 = EntryNodeOfLoop(pHead2);
	
	if (entry1 == entry2)	return true;
	else{
		ListNode *backup = entry2;
		do
		{
			entry2 = entry2->next;
		}while (entry2 != entry1 && entry2 != backup);
		return entry2 != backup;
	}
}

 

合並兩個排序的鏈表

詳情:輸入兩個單調遞增的鏈表,輸出兩個鏈表合成后的鏈表,當然我們需要合成后的鏈表滿足單調不減規則。
題解:
解法一:

//Java
public ListNode Merge(ListNode list1, ListNode list2) {
    if (list1 == null) {
        return list2;
    }
    if (list2 == null) {
        return list1;
    }

    ListNode prev = null;
    ListNode root = list1.val < list2.val ? list1 : list2;
    while (list1 != null && list2 != null) {
        if (list1.val < list2.val) {
            if (prev == null) {
                prev = list1;
            } else {
                prev.next = list1;
                prev = list1;
            }
            list1 = list1.next;
        } else {
            if (prev == null) {
                prev = list2;
            } else {
                prev.next = list2;
                prev = list2;
            }
            list2 = list2.next;
        }
    }
    while (list1 != null) {
        prev.next = list1;
        prev = list1;
        list1 = list1.next;
    }
    while (list2 != null) {
        prev.next = list2;
        prev = list2;
        list2 = list2.next;
    }

    return root;
}
//C++
ListNode *Merge(ListNode *pHead1, ListNode *pHead2) {
    if (pHead1 == NULL) return pHead2;
    if (pHead2 == NULL) return pHead1;
    ListNode *prev = NULL;
    ListNode *root = pHead1->val < pHead2->val ? pHead1 : pHead2;
    while (pHead1 != NULL && pHead2 != NULL) {
        if (pHead1->val < pHead2->val) {
            if (prev == NULL) {
                prev = pHead1;
            } else {
                prev->next = pHead1;
                prev = pHead1;
            }
            pHead1 = pHead1->next;
        } else {
            if (prev == NULL) {
                prev = pHead2;
            } else {
                prev->next = pHead2;
                prev = pHead2;
            }
            pHead2 = pHead2->next;
        }
    }

    while (pHead1 != NULL) {
        prev->next = pHead1;
        prev = pHead1;
        pHead1 = pHead1->next;
    }
    while (pHead2 != NULL) {
        prev->next = pHead2;
        prev = pHead2;
        pHead2 = pHead2->next;
    }
    return root;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM