牛客网刷题之链表(一)


以下题全部出自牛客网。
题目题目考察的知识点链表:
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表的分类有单向、双向等多种类型。其中单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。单链表中的数据是以结点来表示的,每个结点的构成:元素 (数据元素 的映象) + 指针 (指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
与链表有关的题基本都是插入,删除,交换顺序等,解决这些问题通常将链表的指针进行修改,有的简单有的复杂;比如删除链表的头结点和别的结点代码不一样,或者容易对空结点容易解引用等,这时可以加一个头结点,一些问题就可以得到很好的解决。

NB1 删除链表峰值

描述:
农场主人有一群牛,他给每只牛都打了一个编号,编号由整数表示。这些牛按照编号的大小形成了一个链表。现在农场主人想删除链表中比前后结点值都大的牛的编号,你能帮他设计一个算法来实现这个功能吗?注意,只考虑删除前,首尾的牛的编号不删除。

在这里插入图片描述

问题分析: 要删除链表中比前后结点值都大的编号,并且只考虑删除前,首尾的编号不删除,所以用三个指针:prev,cur,next,prev指向删除结点的前一个结点,cur指向将要删除的结点,next指向删除结点的下一个,这样就能很容易将结点连接起来。
代码如下:

ListNode* deleteNodes(ListNode* head)
{
    
    
    // write code here
	ListNode* prev = head, * cur = prev->next, * next = cur->next;
	while (next)
	{
    
    
		if ((cur->val > prev->val) && (cur->val > next->val)) //删除cur,prev不移动
		{
    
    
			prev->next = next;
			free(cur);
		}
		else //不删除cur,prev向后移动
		{
    
    
			prev = prev->next;
		}
		cur = next;
		next = cur->next;
	}

	return head;
}

NB2 牛群排列去重

描述:
农场里有一群牛,每头牛都有一个独特的编号,编号由一个整数表示,整数范围是[0, 200]。牛群中的牛用单链表表示,链表已经按照非降序排列。
因为一些事故,导致一头牛可能多次出现在链表中。给你一个链表的头 head,删除链表中所有重复的编号,只留下所有牛的不重复编号。返回已排序的链表。
在这里插入图片描述
问题分析: 这道题就是单链表的删除,可以保留相同值结点的第一个,用三个指针:prev,cur,next,prev指向删除结点的前一个结点,cur指向将要删除的结点,next指向删除结点的下一个。

代码如下:

ListNode* deleteDuplicates(ListNode* head)
{
    
    
	// write code here
	if (head == nullptr)
		return head;
	ListNode* prev = head, * cur = prev->next, * next = cur->next;
	while (cur) //因为可能需要删除结尾,所以结束标志为cur
	{
    
    
		if (cur->val == prev->val)
		{
    
    
			prev->next = next;
			free(cur);
		}
		else 
		{
    
    
			prev = prev->next;
		}
		cur = next;
		next = cur->next;
	}
}

NB3 调整牛群顺序

描述:
农场里有一群牛,每头牛都有一个编号,编号由一个整数表示,整数范围是[0, 100]。牛群中的牛用单链表表示。
现在,农场主想要调整牛群的顺序。给你一个链表,将链表中的倒数第 n 个结点移到链表的末尾,并且返回调整后的链表的头结点。
在这里插入图片描述
问题分析: 首先找出倒数第 n 个结点,使用快慢指针方法,然后这个倒数第 n 个结点可能是头结点,头结点可能是需要改变的,所以有两种思路:第一种,将改变的结点判断一下,若为头结点,则修改头结点的指针,若不是,则正常处理;第二种,构造一个新的头结点,然后再按正常处理就行(构造新的头结点可以很好的处理问题)。

如下为第二种思路,代码如下:

//NB3 调整牛群顺序
ListNode* moveNthToEnd(ListNode* head, int n)
{
    
    
    // write code here
    if (n == 1)
        return head;

    ListNode* newhead = new ListNode(-1);
    newhead->next = head;
    ListNode* cur = newhead;
    ListNode* prev = newhead, * end = newhead; //prev指向的是交换结点的前一个指针

    //寻找倒数第k个结点使用快慢指针方法
    for (int i = 0; i < n; ++i)
    {
    
    
        end = end->next;
    }
    while (end->next)
    {
    
    
        prev = prev->next;
        end = end->next;
    }

    cur = prev->next;
    prev->next = cur->next;
    end->next = cur;
    cur->next = nullptr;
    head = newhead->next; //头结点可能被改变,所以更新头结点
    free(newhead);

    return head;
}

NB4 牛群的重新分组

描述: 农场里有一群牛,每头牛都有一个编号,编号由一个整数表示,整数范围是[0, 1000]。牛群中的牛用单链表表示。
现在,农场主想要重新分组牛群。给定一个单链表的头指针 head 和一个整数 k,每 k 个节点一组进行翻转,请你返回修改后的牛群链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
在这里插入图片描述
问题分析: 这道题需要改变头结点,所以新建一个头结点,然后需要翻转单链表,翻转单链表可以用链表头插, 将需要翻转的结点一个一个头插到某个结点的后面,总之,这道题用头插不是很容易。

代码如下:

int isreverse(ListNode*& tmp, int& k) //判断是否翻转
{
    
    
    int n = 0;
    while (tmp)
    {
    
    
        ++n;
        if (n == k)
        {
    
    
            if (tmp->next == nullptr) //因为如果最后一个结点头插,最后一个结点的指针将头插到前一个,此时tmp后面就不为空,所以这里附空
            {
    
    
                tmp = nullptr;
            }
            return 1;
        }
        tmp = tmp->next;
    }
    return 0;
}
ListNode* reverseKGroup(ListNode* head, int k)
{
    
    
    // write code here
    ListNode* newhead = new ListNode(-1);
    newhead->next = head;
    ListNode* prev = newhead; //prev是开始进行翻转的前一个结点
    ListNode* end = prev->next; //end是每次头插第一个结点
    //头插k个结点
    ListNode* cur = prev->next; //当前节点
    ListNode* next = cur->next; //下一个结点
    while (cur)
    {
    
    
        ListNode* tmp = cur; //tmp进行遍历
        if (isreverse(tmp, k)) //够翻转,进行翻转
        {
    
    
            for (int i = 0; i < k; ++i)
            {
    
    
                if (i == 0)
                {
    
    
                    end = cur;
                }
                //头插
                cur->next = prev->next; 
                prev->next = cur;
                //更新结点
                cur = next;
                if (cur->next)
                    next = next->next;

            }
            //连接结点
            prev = end;
            end->next = cur;
        }
        else
            break;
        if (tmp == nullptr) //如果全部翻转,将最后一个结点指空,end是每次头插第一个结点,也就是最后一个结点
        {
    
    
            end->next = nullptr;
            break;
        }
    }

    head = newhead->next;
    delete(newhead);

    return head;
}

NB5 牛群的重新排列

描述:
农场里有一群牛,每头牛都有一个编号,编号由一个整数表示,整数范围是[-500, 500]。牛群中的牛用单链表表示。
现在,农场主想要改变牛群的排列顺序。给定一个单链表的头指针 head 和两个整数 left 和 right,其中 left <= right。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的牛群链表。

在这里插入图片描述
问题分析:这道题可以用头插来做,也可以用栈来做,相比上一道要容易些,大致思路和上一个差不多;prev指向第一个翻转结点的前一个,end指向第一个翻转的结点,cur指向翻转的结点,next指向翻转结点的下一个结点,然后对第left到第right个的结点头插,最后在将头插的和未投插的结点连接起来。

ListNode* reverseBetween(ListNode* head, int left, int right)
{
    
    
    // write code here
    ListNode* newhead = new ListNode(-501);
    newhead->next = head;

    ListNode* prev = newhead; //prev是开始进行翻转的前一个结点
    int n = 1;
    while (n < left)
    {
    
    
        prev = prev->next;
        ++n;
    }
    ListNode* cur = prev->next; //cur是需要翻转的结点
    ListNode* next = cur->next; //next是翻转的结点的下一个结点
    ListNode* end = cur; //end是第left个结点
    while (left <= right)
    {
    
    
        //头插
        cur->next = prev->next;
        prev->next = cur;
        //更新结点
        cur = next;
        if (next)
            next = next->next;
        ++left;
    }
    //连接
    end->next = cur;

    head = newhead->next;
    delete newhead;

    return head;
}

NB6 合并两群能量值(合并有序单链表)

描述:
农场里有两群牛,每群牛都有一定的能量值。能量值由一个整数表示,整数范围是[-100, 100]。每群牛的能量值已经按照非递增顺序排列,并存储在链表中。
现在,你需要将这两群牛的能量值合并为一个新的非递增链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
在这里插入图片描述
题目分析:这道题就是合并两个有序单链表,首先判断l1,l2是否有空,1. 若都为空,则返回空;2. 若一个为空,另一个不为空,则返回不为空的一个;3. 两个都不为空,开始遍历两个链表,比较两个结点的大小,插入大的那一个结点,若遍历某个链表为空,则将不为空的链表进行插入,否则继续遍历。
代码如下:

ListNode* mergeEnergyValues(ListNode* l1, ListNode* l2)
{
    
    
    // write code here
    ListNode* head, * cur;
    //先判断是否有空
    if (l1 == nullptr && l2 != nullptr)
        return l2;
    else if (l1 != nullptr && l2 == nullptr)
        return l1;
    else if (l1 == nullptr && l2 == nullptr)
        return nullptr;

    //给head附头
    if (l1->val >= l2->val)
    {
    
    
        head = cur = l1;
        l1 = l1->next;
    }
    else
    {
    
    
        head = cur = l2;
        l2 = l2->next;
    }
    while (l1 || l2)//两个都空结束
    {
    
    
        //判断遍历时是否有空
        if (l1 == nullptr && l2 != nullptr)
        {
    
    
            cur->next = l2;
            return head;
        }
        else if (l1 != nullptr && l2 == nullptr)
        {
    
    
            cur->next = l1;
            return head;
        }
        if (l1->val >= l2->val)
        {
    
    
            cur->next = l1;
            cur = cur->next;
            l1 = l1->next;
        }
        else
        {
    
    
            cur->next = l2;
            cur = cur->next;
            l2 = l2->next;
        }
    }
    cur->next = nullptr;
    return head;
}
ListNode* mergeEnergyValues(ListNode* l1, ListNode* l2)
{
    
    
    if (!l1 || !l2)
        return l1 ? l1 : l2;
    ListNode* head = new ListNode(-1);
    ListNode* cur = head, * p1 = l1, * p2 = l1;
    while (l1 && l2)
    {
    
    
        if (p1->val > p2->val)
        {
    
    
            cur->next = p1;
            p1 = p1->next;
        }
        else
        {
    
    
            cur->next = p2;
            p2 = p2->next;
        }
        cur = cur->next;
    }
    //至少有一个链表为空,cur->next连接不为空的链表
    cur->next = (p1 ? p1 : p2);

    cur = head->next;
    delete head;
    return cur;
}

NB7 牛群的能量值(单链表相加)

描述:
农场里有两群牛,每群牛都有一定的能量值。能量值由一个非负整数表示,且每头牛的能量值只有一位数字。能量值按照逆序的方式存储在链表中,即链表的第一个节点表示个位,第二个节点表示十位,以此类推。
现在,你需要将这两群牛的能量值相加,然后以相同的逆序形式返回表示和的链表。
注意:除了数字0之外,这两个数都不会以0开头。

在这里插入图片描述

问题分析:单链表相加,遍历两个链表,将两个链表相加,注意链表的长短不一样,还有就是最后一位需不需要进位。

ListNode* addEnergyValues(ListNode* l1, ListNode* l2)
{
    
    
    ListNode* head = new ListNode(-1);
    ListNode* cur = head;
    int m = 0; //是否进位
    //有一个链表为空则退出
    while (l1 && l2)
    {
    
    
        int n = l1->val + l2->val + m;
        m = 0;
        if (n > 9)
            m = 1;
        cur->next = new ListNode(n%10);
        cur = cur->next;
        l1 = l1->next;
        l2 = l2->next;
    }
    if (l1)
        l2 = l1;
    //if处理后,l2一定不为空
    while (l2)
    {
    
    
        int n = l2->val + m;
        m = 0;
        if (n > 9)
            m = 1;
        cur->next = new ListNode(n % 10);
        cur = cur->next;
        l2 = l2->next;
    }
    //处理最后进位的数
    if (m == 1)
        cur->next = new ListNode(m);
    return head->next;
}

猜你喜欢

转载自blog.csdn.net/weixin_68278653/article/details/131819739
今日推荐