力扣OJ题感悟——链表篇

本文分享了作者在解决力扣(LeetCode)中的链表题目时的一些理解和思路,包括移除链表元素、反转链表、找到链表中间结点、判断回文结构、合并链表和检测环等。通过具体的代码实现,阐述了如何使用不同的技巧处理链表问题,如快慢指针、反转链表等。
摘要由CSDN通过智能技术生成

链表OJ题对于我现阶段的学习确实存在这一些难度,对题目里所蕴含的知识也是很浅,但我愿意分享我的一些理解,记录我的学习过程,也时常以博客来反省自己,提醒自己。
后续我会根据自己的学习情况补充OJ题思路和解法。
1.移除链表元素
代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 
struct ListNode* removeElements(struct ListNode* head, int val)
{
    if(head==NULL)
    {
        return head;
    }
    struct ListNode* pre=NULL;
    struct ListNode* cur=head;
    while(cur)
    {
        if(cur->val==val)
        {
            struct ListNode* next=cur->next;
            if(pre==NULL)
            {
                head=next; 
            }
            else
            {
                pre->next=next;
            }
            free(cur);
            cur=next;
        }
        else
        {
            pre=cur;
            cur=cur->next;
        }
    }
        return head;
}

思路分析:要移除带头单链表所有符合要求的结点,首先要判断头指针是否为空。之后定义三个结点指针变量pre(指向前趋结点指针),cur(指向当前结点指针)和next(指向后继结点指针),用这三个变量进行操作。在函数实现中无非两种大情况:一种是cur指针指向的结点的符合条件,另一种则是不符合。判断这两个条件的前提则是cur指针的指向是否是空。为了保证下一个结点可以被找到,先把下一个结点保存,如果头指针指向的结点就是要移除的结点,那么必须让下一个结点变为头结点。
如果头结点不是要移除的元素,则将下一个节点覆盖到当前当前结点,即移除了当前结点,最后cur指针指向下一个结点。如果当前结点不是要移除的结点,那么pre、cur和next指针都向后移动一个结点。最后返回头指针head。

2.反转链表
代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* newhead=NULL;
    struct ListNode* cur=head;
    if(head==NULL)
    {
        return head; 
    }
    while(cur)
    {
        struct ListNode* next=cur->next;
        cur->next=newhead;
        newhead=cur;
        cur=next;
    }
    return newhead;
}


/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head)
{
    if(head==NULL)
    {
        return head;
    }
    struct ListNode* pre=NULL;
    struct ListNode* cur=head;
    while(cur)
    {
        struct ListNode* next=cur->next;
        cur->next=pre;
        pre=cur;
        cur=next;
    }
    return pre;
}

思路分析:反转链表最简单的思路就是改变结点自身指针的方向,再进行迭代就能完成其过程。三个指针变量pre(指向前趋结点),cur(指向当前结点)和next(指向后继结点)。保存下个节点。将cur指向的结点自身的后继指针反向,指向pre指向的结点,pre、cur和next指针都向后移动一个结点,循环即可。

3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间节点
代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     struct ListNode *next;
* };
*/


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

思路分析:最简单的方法就是利用快慢指针来操作。定义两个指针变量slow(慢指针)和fast(快指针),判断头指针是否为空。两个指针向后移动的前提是fast指向的结点和fast指针指向的下一个结点不为空,slow走一步,fast就走两步,当fast指向的结点为空或者下个节点为空,此时slow指向的结点则是中间结点。

4. 链表的回文结构

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    struct ListNode* reverseList(struct ListNode* head) {
        struct ListNode* newhead = NULL;
        struct ListNode* cur = head;
        while(cur)
        {
            struct ListNode* next = cur->next;
            cur->next = newhead;
            newhead = cur;
            cur = next;
        }
     
        return newhead;
    }
     
    bool chkPalindrome(ListNode* A) {
        int n = 0;
        struct ListNode* cur = A;
        while(cur)
        {
            cur = cur->next;
            ++n;
        }
         
        cur = A;
        int mid = n/2;
        while(mid--)
        {
            cur = cur->next;
        }
         
        struct ListNode* head1 = A;
        struct ListNode* head2 = reverseList(cur);
         
        mid = n/2;
        while(mid--)
        {
            if(head1->val == head2->val)
            {
                head1 = head1->next;
                head2 = head2->next;
            }
            else
            {
                   return false;
            }
        }
         
        return true;
    }
};/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        int a[900];
        int n = 0;
        struct ListNode* cur = A;
        while(cur)
        {
            a[n++] = cur->val;
            cur = cur->next;
        }
        
        int left = 0, right = n-1;	
        while(left < right)
        {
              if(a[left] != a[right])
                  return false;
            ++left;
            --right;
        }
        
        return true;
    }
};

思路分析:在链表的中间位置的后半部分进行反转,进行判断,如果和前半段的结点相同,则是回文结构,返回真。

5. 合并链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
    if(l1 == NULL)
        return l2;
    else if(l2 == NULL)
        return l1;
    
    // 先取一个小的节点做头
    struct ListNode* head, *tail;
    if(l1->val < l2->val)
    {
        head = l1;
        l1 = l1->next;
    }
    else
    {
        head = l2;
        l2 = l2->next;
    }
    
    // 取小的节点进行尾插
    tail = head;
    while(l1 != NULL && l2 != NULL)
    {
        if(l1->val < l2->val)
        {
            tail->next = l1;
            l1 = l1->next;
        }
        else
        {
            tail->next = l2;
            l2 = l2->next;
        }
        tail = tail->next;
    }
    
    if(l1 != NULL)
        tail->next = l1;
    
    if(l2 != NULL)
        tail->next = l2;
    
    return head;
}

思路分析:每次拿两个链表中较小的结点进行尾插,取第一个较小的结点既做头又做尾,之后每次取下的结点进行尾插。当l1指向的链表的结点被拿到之后,l1每次指向下一个结点,同样l2每次指向下个结点。如果其中一个链表为空,那么把另一个链表直接拿下来尾插。

6. 判断链表是否有环

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
   struct ListNode* slow = head;
   struct ListNode* fast = head;
   while(fast && fast->next)
   {
       slow = slow->next;
       fast = fast->next->next;
       
       if(slow == fast)
           return true;
   }
    
    return false;
}

思路分析:快慢指针的再次利用,如果slow和fast的指向相同,则是有环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值