leetcode 常考链表面试题总结(持续更新中)

一. 移除链表元素

原题链接:

移除链表元素

第一种方法: 直接删除法

这种方法需要特殊考虑头结点的删除情况

首先考虑一般情况 ,删除的不是头结点,定义两个指针 prev , cur 一前一后,逐步删除移动

我们由图中可以看到,如果删除的是头结点,prev->next = cur->next 该步就会出错,因为 prev 此时是空指针

删除头结点的情况如下:

给出代码 :

struct ListNode* removeElements(struct ListNode* head, int val)
{
    
    
    if(head == NULL)
    {
    
    
        return NULL;
    }
    struct ListNode* prev = NULL,* cur = head;
    while(cur != NULL)
    {
    
    
        if(cur->val == val)
        {
    
    
            // 删除头结点的情况
            if(cur == head)
            {
    
    
                cur = cur->next;
                free(head);
                head = cur;
            }
            // 删除非头结点的情况
            else
            {
    
    
                prev->next = cur->next;
                free(cur);
                cur = prev->next;
            }
        }
        else
        {
    
    
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
}

第二种方法 : 创建虚拟头结点

该方法可以将删除头结点和其他结点的情况进行统一

我们创建一个新节点来作为整个链表的头结点,该节点中的值没有意义,只是用该节点来方便我们的操作。如果用DummyNode->next=head; 此时 我们操作DummyNode的话就把第一个结点当做了一个普通节点来操作,此时头结点就可以被删除了。最后返回DummyNode->next就满足条件了。正是由于有个无意义节点作为头结点会统一操作(把头结点当做普通节点)所以实际链表设计过程中都是有个无意义头结点的,遇到第一个结点不好解决的问题,大家可以设一个节点试试。
给出代码 :

typedef struct ListNode ListNode;
// 创建虚拟头结点
ListNode* CreateListNode()
{
    
    
    ListNode* DummyNode = (ListNode*)malloc(sizeof(ListNode));
    if(DummyNode == NULL)
    {
    
    
        exit(-1);
    }
    else
    {
    
    
        DummyNode->next = NULL;
    }
    return DummyNode;
}
struct ListNode* removeElements(struct ListNode* head, int val)
{
    
    
   ListNode* DummyNode = CreateListNode();
   ListNode* cur = head,* prev = DummyNode;
   DummyNode->next = head;
   head = DummyNode;
   // 执行删除操作
   while(cur != NULL)
   {
    
    
       if(cur->val == val)
       {
    
    
           prev->next = cur->next;
           free(cur);
           cur = prev->next;
       }
       else
       {
    
    
           prev = cur;
           cur = cur->next;
       }
   }
   return head->next;
}

方法三 : 递归

递归思路:

给出代码 :

struct ListNode* removeElements(struct ListNode* head, int val)
{
    
    
    if(head == NULL)
    {
    
    
        return NULL;
    }
    head->next = removeElements(head->next,val);
    return  (head->val == val) ? head->next : head;
}

二. 链表的中间结点

原题链接 :

链表的中间结点

第一种方法 :

快慢指针

(1) 链表结点个数为奇数个时 :

(2) 链表结点个数为偶数个时 :
给出代码 :

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

三. 链表中倒数第k个结点

原题链接 :

链表中倒数第k个结点

方法: 快慢指针

先让快指针走 k 步,接着快指针和慢指针一起走,当快指针走到 NULL 时,慢指针走到倒数第 k 个结点

注意判断一下 k 是否超出链表的长度

给出代码 :

struct ListNode* getKthFromEnd(struct ListNode* head, int k)
{
    
    
   if(head == NULL)
   {
    
    
       return NULL;
   }
   struct ListNode* fast = head,* slow = head;
   // fast指针先走 k 步
   while(k--)
   {
    
    
   		// 判断 k 是否满足条件
       if(fast != NULL)
       {
    
    
            fast = fast->next;
       }
       else
       {
    
    
           return NULL;
       }
   }
   // 快慢指针同时移动
   while(fast != NULL)
   {
    
    
       fast = fast->next;
       slow = slow->next;
   }
   return slow;
}

四. 合并两个有序链表

原题链接 :

合并两个有序链表

第一种方法 : 尾插迭代法


给出代码 :

typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
    
    
   if(l1 == NULL || l2 == NULL)
   {
    
    
       return (l1 == NULL) ? l2 : l1; 
   }
   ListNode* head = NULL,* tail = NULL;
   head = tail = (ListNode*)malloc(sizeof(ListNode));
   while(l1 != NULL && l2 != NULL)
   {
    
    
        if(l1->val <= l2->val)
        {
    
    
            tail->next = l1;
            tail = tail->next;
            l1 = l1->next;
        }
        else
        {
    
    
            tail->next = l2;
            tail = tail->next;
            l2 = l2->next;
        }
   }
   if(l1 == NULL) 
   {
    
    
       tail->next = l2;
   }
   else
   {
    
    
       tail->next = l1;
   }
   return head->next;
}

第二种方法 : 递归

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
    
    
    if(l1==NULL)
        return l2;
    if(l2==NULL)
        return l1;
    if(l1->val <= l2->val)
    {
    
    
        l1->next = mergeTwoLists(l1->next,l2);
        return l1;
    }else
    {
    
    
        l2->next = mergeTwoLists(l1,l2->next);
        return l2;
    }
}

五. 分割链表

原题链接 :

分割链表

在这里插入图片描述

给出代码 :

class Partition {
    
    
public:
    ListNode* partition(ListNode* pHead, int x)
    {
    
    
        ListNode* LessHead = (ListNode*)malloc(sizeof(ListNode));
        ListNode* GreaterHead = (ListNode*)malloc(sizeof(ListNode));
        LessHead->next = GreaterHead->next = NULL;
        ListNode* LessHeadTail = LessHead;
        ListNode* GreaterHeadTail = GreaterHead;
        ListNode* cur = pHead;
        while (cur != NULL)
        {
    
    
            if (cur->val < x)
            {
    
    
                LessHeadTail->next = cur;
                LessHeadTail = LessHeadTail->next;
            }
            else
            {
    
    
                GreaterHeadTail->next = cur;
                GreaterHeadTail = GreaterHeadTail->next;
            }
            cur = cur->next;
        }
        GreaterHeadTail->next = NULL;
        LessHeadTail->next = GreaterHead->next;
        ListNode* List = LessHead->next;
        free(LessHead);
        free(GreaterHead);
        return List;
    }
};

六. 反转链表

原题链接 :

反转链表

第一种方法 : 双指针

给出代码 :

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
    
    
    ListNode* n1 = NULL;
    ListNode* n2 = head;
    while(n2 != NULL)
    {
    
    
        ListNode* n3 = n2->next;
        n2->next = n1;
        n1 = n2;
        n2 = n3;
    }
    return n1;
}

第二种方法 : 头插法

设置一个新节点 newNode , 采取单链表中讲解到的头插的方法 ,即可达到逆序链表的目的

给出代码 :

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
    
    
	// 设置新节点
    ListNode* newNode = NULL,* cur = head;
    // 进行头插操作,因为每次改变了 cur->next , 所以用 next 保存 cur 的下一个结点
    while(cur != NULL)
    {
    
    
        ListNode* next = cur->next;
        cur->next = newNode;
        newNode = cur;
        cur = next;
    }
    return newNode;
}

七. 链表的回文结构

原题链接 :

链表的回文结构

方法 : 先找到链表的中间结点 , 具体方法可参考第二题 , 接着逆序从中间结点起往后的部分(逆序操作可看第六题),判断逆序后和前半部分结点是否相等

给出代码 :

typedef struct ListNode ListNode;
// 逆序链表操作
ListNode* reverseList(ListNode* head)
{
    
    
    ListNode* n1 = NULL;
    ListNode* n2 = head;
    while(n2 != NULL)
    {
    
    
        ListNode* n3 = n2->next;
        n2->next = n1;
        n1 = n2;
        n2 = n3;
    }
    return n1;
}
class PalindromeList {
    
    
public:
    bool chkPalindrome(ListNode* A)
    {
    
    
        ListNode* fast = A;
        ListNode* slow = A;
        ListNode* prev = NULL;
        // 找到链表中间结点
        while(fast != NULL && fast->next != NULL)
        {
    
    
            prev = slow;
            slow = slow->next;
            fast = fast->next->next;
        }
        slow = reverseList(slow);
        // 判断前半部分结点和后半部分结点是否相等
        while(A != NULL && slow != NULL)
        {
    
    
            if(A->val != slow->val)
            {
    
    
                return false;
            }
            else
            {
    
    
                A = A->next;
                slow = slow->next;
            }
        }
        return true;
    }
};

猜你喜欢

转载自blog.csdn.net/DR5200/article/details/113930232