數據結構之鏈表常考面試題

1、鏈表與順序表的對比:
鏈表:內存上不連續,不支持下標訪問,使用時不用考慮內存問題
順序表:內存上連續,支持下標訪問,擴容麻煩

2、鏈表面試題

  1. 刪除鏈表中等於給定值 val 的所有節點。

     struct ListNode* removeElements(struct ListNode* head, int val) {
     // 如果鏈表爲空,返回空
     if (head == NULL){
     return NULL;
     }
    
     // 先不考慮第一個結點,相當於尾刪
    
     struct ListNode* cur = head;
     while (cur->next != NULL){
     if (cur->next->val == val){
     	struct ListNode* next = cur->next->next;
     	free(cur->next);
     	cur->next = next;
     }
     else{
     	cur = cur->next;
     }
     }
    
     // 如果第一個結點值爲val,相當於頭刪
    
     if (head->val == val){
     struct ListNode* newHead = head->next;
     free(head);
     return newHead;
    
     }
     else
     return head;
    
     }
    

// 2. 反轉一個單鏈表。
// 先後指針,或者三指針法

struct ListNode* reverseList(struct ListNode* head) {
struct  ListNode *prev = NULL;
struct ListNode *cur = head;

while (cur != NULL) {
	struct ListNode *next = cur->next;

	cur->next = prev;
	prev = cur;
	cur = next;
}

return prev;

}

// 3. 給定一個帶有頭結點 head 的非空單鏈表,返回鏈表的中間結點。如果有兩個中間結點,則返回第二個中間結點
// 快慢指針,快每次走兩步

struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while (fast != NULL&&fast->next != NULL){
	fast = fast->next->next;
	slow = slow->next;

}
return slow;

}

// 4. 輸入一個鏈表,輸出該鏈表中倒數第k個結點。

class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
	ListNode* front = pListHead;
	ListNode* back = pListHead;
	unsigned int i;
	for (i = 0; front != NULL&&i<k; i++){
		front = front->next;
	}
	if (i<k){
		return NULL;
	}
	while (front != NULL){
		front = front->next;
		back = back->next;
	}
	return back;

}
};

// 5. 將兩個有序鏈表合併爲一個新的有序鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。

class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
	// 如果鏈表l1或l2爲空
	if (l1 == NULL){
		return l2;
	}
	if (l2 == NULL){
		return l1;
	}
	ListNode* newHead = NULL; // 新鏈表的首結點
	ListNode* newTail = NULL; // 新鏈表的尾結點

	// 鏈表l1和l2都不爲空,分別取兩個鏈表的結點拼接在一起組成新的鏈表

	while (l1 != NULL&&l2 != NULL){

		// 取第一個鏈表的結點
		if (l1->val <= l2->val){
			if (newTail != NULL){
				newTail->next = l1;
				newTail = l1;
			}
			else{
				newHead = newTail = l1;
			}
			l1 = l1->next;
		}
		// 取第二個鏈表的結點
		else{
			if (newTail != NULL){
				newTail->next = l2;
				newTail = l2;
			}
			else{
				newHead = newTail = l2;
			}
			l2 = l2->next;
		}

		// 如果一個鏈表被取完了
		if (l1 != NULL){
			newTail->next = l1;
		}
		else{
			newTail->next = l2;
		}
	}
	return newHead;

}
};

// 6. 編寫代碼,以給定值x爲基準將鏈表分割成兩部分,所有小於x的結點排在大於或等於x的結點之前 .

class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
	// write code here
	ListNode* small = NULL;
	ListNode* small_last = NULL;
	ListNode* big = NULL;
	ListNode* big_last = NULL;

	ListNode* cur;
	for (cur = pHead; cur != NULL; cur = cur->next){

		// 切割鏈表
		if (cur->val<x){
			if (small_last == NULL){
				small = small_last = cur;
			}
			else{
				small_last->next = cur;
				small_last = cur;
			}
		}

		else{
			if (big_last == NULL){
				big = big_last = cur;
			}
			else{
				big_last->next = cur;
				big_last = cur;
			}
		}
	}

	// 拼接鏈表

	if (small_last != NULL){
		small_last->next = big;
	}
	if (big_last != NULL){
		big_last->next = NULL;
	}
	if (small != NULL){
		return small;
	}
	else{
		return big;
	}
}
};

// 7. 在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。

class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
	// 鏈表爲空
	if (pHead == NULL)
	{
		return NULL;
	}
	ListNode* fask = (ListNode*)malloc(sizeof(ListNode));
	fask->next = pHead;
	ListNode* prev = fask;
	ListNode* p1 = pHead;
	ListNode* p2 = pHead->next;
	while (p2 != NULL)
	{
		if (p1->val != p2->val)
		{
			prev = p1;
			p1 = p2;
			p2 = p2->next;

		}
		else
		{
			while (p2 != NULL&&p2->val == p1->val)
			{
				p2 = p2->next;
			}
			// 開始刪除重複結點
			prev->next = p2;
			p1 = p2;
			if (p2 != NULL)
			{
				p2 = p2->next;
			}

		}
	}
	pHead = fask->next;
	free(fask);   // 刪除假結點

	return pHead;

}
};

// 8. 鏈表的迴文結構。

class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
	// write code here
	// 0、如果鏈表爲空返回false,如果鏈表爲單鏈表則返回true

	if (A == NULL){
		return false;
	}
	else if (A->next == NULL){
		return true;
	}

	// 1、找到鏈表的中間結點
	ListNode* fast = A;
	ListNode* slow = A;

	while (fast != NULL&&fast->next != NULL){
		fast = fast->next->next;
		slow = slow->next;
	}

	// 2、逆置後半部分
	ListNode* p1 = slow;
	ListNode* p2 = p1->next;

	while (p1 != NULL){
		p1->next = slow;
		slow = p1;
		p1 = p2;
		p2 = p2->next;
	}

	// 3、從頭、尾指針向中間遍歷,判斷A是否是迴文 

	while (A != slow){
		if ((A->val) != (slow->val)){
			return false;
		}
		else{
			if (A->next == slow){
				return true;
			}
			A = A->next;
			slow = slow->next;
		}
	}

	return true;
}
};

// 9. 輸入兩個鏈表,找出它們的第一個公共結點。

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
if (headA == NULL || headB == NULL){
	return NULL;
}
struct ListNode* p = headA;
struct ListNode* q = headB;

// 定義兩個指針同時出發,如果p爲NULL另p=headB,否則讓p=p->next;
// 如果q==NULL,q=headA,否則q=q->next;
// 最後p=q時返回p,即爲交點
while (p != q) {
	p = !p ? headB : p->next;   
	q = !q ? headA : q->next;
}
return p;
}

// 10. 給定一個鏈表,判斷鏈表中是否有環。
// 快慢指針,快的先走一步,如果兩個指針相遇則有環,反之無環

bool hasCycle(struct ListNode *head) {
if (head == NULL){
	return false;
}
struct ListNode* fast = head->next;
struct ListNode* slow = head;

while (fast != NULL){
	if (fast == slow){
		return true;
	}
	slow = slow->next;
	fast = fast->next;
	if (fast)
		fast = fast->next;
}
return false;

}

// 11. 給定一個鏈表,返回鏈表開始入環的第一個節點。 如果鏈表無環,則返回 NULL
// 快慢指針,快指針比慢指針快一步,如果快指針小於慢指針,則必有環,此時返回快指針即爲入環的第一個結點

struct ListNode *detectCycle(struct ListNode *head) {
if (head == NULL){
	return NULL;
}
if (head->next == head){
	return head;
}
struct ListNode* fast = head->next;
struct ListNode* slow = head;

while (fast != NULL&& fast>slow){
	fast = fast->next;
	slow = slow->next;
}
return fast;


}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章