連結串列專題——面試中常見的連結串列問題
宣告:連結串列定義如下:
//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;
從無頭單鏈表中刪除節點
詳情:給定一個沒有頭指標的單鏈表,一個指標指向此單鏈表中間的一個節點(不是第一個,也不是最後一個節點),請將該節點從單鏈表中刪除。 題解:
//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;
}