OJ——单链表练习题(一)

一、 移除链表元素

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路:

        我们可以直接暴力求解:遍历一遍链表,将不是val的节点存放到新的链表当中,返回新的链表。

struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* cur = head;
    struct ListNode* newhead=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* tail=newhead;
	while (cur)
	{
		if (cur->val == val)
		{
            struct ListNode* next=cur->next;
            free(cur);
            cur=next;
		}
        else
        {
            tail->next=cur;
            tail=tail->next;
            cur=cur->next;
        }
	}
    tail->next=NULL;
    head=newhead->next;
    free(newhead);
    return head;
}

在LeetCode做这些OJ题malloc一般不会创建失败,可以不进行判断。

二、反转链表

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路:

       反转是将节点原本指向下一个节点的指针指向了上一个节点,第一个指向了NULL。 我们可以先画图来理解:

       先创建一个空指针(newhead),遍历一遍链表,每次遍历存储下一个节点,然后将当前节点的next指向newhead,再将当前节点赋值给newhead。遍历结束后返回newhead。

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;
}

三、链表的中间节点

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路:

        遍历查找:遍历链表拿到节点的个数n,n/=2,再从头开始遍历。以数组下标为例,头节点下标为0找到下标为n的节点,这个节点就是中间节点,并且它肯定是中间节点的第二个节点。

                时间复杂度:O(N)=N+2/N

        快慢指针:创建两个指针来遍历(slow和fast),slow一次走一步(slow=slow->next),fast一次走两步(fast=fast->next->next),结束条件是fast为空或fast的下个节点为空。遍历结束slow刚好停在中间节点的第二个节点。

                时间复杂度:O(N)=N

快慢指针:

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

四、链表中倒数第k个节点

链表中倒数第k个结点_牛客题霸_牛客网

思路:

        暴力求解:遍历得到链表的节点个数n,再重新遍历,循环走n-k次就得到了倒数第k个节点(也可以参考上一题的遍历查找,以下标的角度来遍历)。

                时间复杂度:O(N)=2N

        快慢指针:fast先提前走k步,然后两个指针在同时遍历,fast为空时停下,slow就是倒数第k个节点。

                时间复杂度:O(N)=N

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode* slow=pListHead;
    struct ListNode* fast=pListHead;
    while(k--)
    {
        if(fast==NULL)
        {
            return NULL;
        }
        fast=fast->next;
    }
    while(fast)
    {
        slow=slow->next;
        fast=fast->next;
    }
    return slow;
}

五、合并两个有序链表

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路:

        首先判断两个链表若一个为空那就直接返回另一个。创建一个新的指针(newlist)来链接list1和list2,循环遍历list1和list2,每次将它们两个中val较小的那个链接到newlist,list1或list2为空时结束循环,然后将其中不空的链接到newlist中。链接时使用cur指针指向newlist的尾节点来控制newlist的链接,循环时要单独判断newlist为空时的情况。

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    if (list1 == NULL)
        return list2;
    if (list2 == NULL)
        return list1;
    struct ListNode* newlist = NULL;
    struct ListNode* cur = NULL;
    while (list1 && list2)
    {
        struct ListNode* tmp = NULL;
        if (list1->val < list2->val)
        {
            tmp = list1;
            list1 = list1->next;
        }
        else
        {
            tmp = list2;
            list2 = list2->next;
        }
        
        if (cur == NULL)
        {
            newlist = tmp;
            cur = newlist;
        }
        else
        {
            cur->next = tmp;
            cur = cur->next;
        }
    }
    cur->next = list1 == NULL ? list2 : list1;
    return newlist;
}

六、链表分割

链表分割_牛客题霸_牛客网

思路:

        创建两个空指针(head1和head2),遍历pHead将小于x的节点放入head1,大于等于x的节点放入head2,遍历时使用tail1和tail2指向head1和head2的尾节点,head1和head2为空时单独判断。遍历结束后head1和head2一个为空,那就直接返回另一个。如果没有就将tail2的next置为空,再将head2链接给head1(遍历完pHead后tail2可能不是pHead的尾节点,那tail2的next就不为空,链接完后head1就会是循环链表),返回head1。

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        ListNode* head1 = NULL;
        ListNode* head2 = NULL;
        ListNode* cur = pHead, * tail1 = head1, * tail2 = head2;
        while (cur)
        {
            if (cur->val < x)
            {
                if (tail1 == NULL)
                {
                    head1 = cur;
                    tail1 = head1;
                }
                else
                {
                    tail1->next = cur;
                    tail1 = tail1->next;
                }
            }
            else
            {
                if (tail2 == NULL)
                {
                    head2 = cur;
                    tail2 = head2;
                }
                else
                {
                    tail2->next = cur;
                    tail2 = tail2->next;
                }
            }
            cur = cur->next;
        }
        if (tail1 == NULL)
        {
            return head2;
        }
        if (tail2 == NULL)
        {
            return head1;
        }
        tail2->next = NULL;
        tail1->next = head2;
        return head1;
    }
};

七、链表的回文结构

链表的回文结构_牛客题霸_牛客网

思路:

        这道题主要是将前面的快慢指针和链表反转结合了起来。

例链表为:1->2->3->3->2->1

        先找到中间节点:

        再从中间节点开始将后面的节点反转:

        最后从原链表的尾节点(n1)和头节点(cur)开始遍历判断,n1==cur->next结束循环,返回true,如果循环时两个节点的val不相等则返回false。

class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        ListNode* slow = A, * fast = A;
        ListNode* cur = A;
        //找到中间节点
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        //将中间节点后面的节点反转
        ListNode* n1 = slow, * n2 = slow->next;
        while (n2)
        {
            ListNode* tmp = n2->next;
            n2->next = n1;
            n1 = n2;
            n2 = tmp;
        }
        //第一个节点和最后一个节点开始相互比较
        while (n1 != cur->next)
        {
            if (cur->val != n1->val)
                return false;
            cur = cur->next;
            n1 = n1->next;
        }
        return true;
    }
};

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值