1、鏈表與順序表的對比:
鏈表:內存上不連續,不支持下標訪問,使用時不用考慮內存問題
順序表:內存上連續,支持下標訪問,擴容麻煩
2、鏈表面試題
-
刪除鏈表中等於給定值 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;
}