单链表经典oj题(2)

前言

这次将要把剩下的oj题将以图解和自己的理解把它讲解完,希望对大家有所帮助,这次的讲解也是干货

第一题

21. 合并两个有序链表 - 力扣(LeetCode)

ok这次就简单点,大家自己去看题目了

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

暴力思路

想想看不考虑空间和时间的消耗,什么代码最容易想

两个链表,可不可以直接把两个链表的值放在一个数组中

然后再排序,当然这里的链表大小是有序的,放在数组中时

可以判断大小,从而不用去排序

再根据数组自行去malloc一个链表,是不是非常暴力的思路

这个暴力代码其实也很重要,至少是算法小白入门的绝招

#include<stdio.h>
#include<stdlib.h>
typedef struct SlistNode {
   struct SlistNode* next;
   int val;
}SLNode,*SL;
SLNode* Buyslistnode(int data)
{
	SL newnode = (SL)malloc(sizeof(SLNode));
	newnode->next = NULL;
	newnode->val = data;
	return newnode;
}
SL initlist1()
{
	SL list1 = (SL)malloc(sizeof(SLNode));
	list1->val = 1;
	list1->next = Buyslistnode(2);
	list1->next->next = Buyslistnode(4);
	return list1;
}
SL initlist2()
{
	SL list2 = (SL)malloc(sizeof(SLNode));
	list2->val = 1;
	list2->next = Buyslistnode(3);
	list2->next->next = Buyslistnode(4);
	return list2;
}
int cmp(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}
void text1()
{
	//我们这里直接考虑list1与list2都不是空链表的情况
	//其实其他的情况可以通过if判断排除
	//初始化题目
	//list1   1->2->4
	//list2   1->3->4
	SL list1 = initlist1();
	SL list2 = initlist2();
	//题目中链表的结点数是0~50
	//所以直接可以用静态数组
	int newarr[110];
	int size = 0;
	while (list1)
	{
		newarr[size++] = list1->val;
		list1 = list1->next;
	}
	while (list2)
	{
		newarr[size++] = list2->val;
		list2 = list2->next;
	}
	qsort(newarr, size, sizeof(int), cmp);
	SL phead = (SL)malloc(sizeof(SLNode));
	SL cur = phead;
	for (int i = 0; i < size; i++)
	{
		cur->next = Buyslistnode(newarr[i]);
		cur = cur->next;
	}
	//至此已经完成
	//打印看结果吧
	cur = phead->next;
	while (cur)
	{
		printf("%d ", cur->val);
		cur = cur->next;
	}
}
int main()
{
	text1();
	return 0;
}

那么看结果

OK接下来看看优化的思路 头结点+双头插

对于这个例子

我们一第二条链表为基础对他进行插入操作

我们使用两个指针,分别指向两条链表的第一个位置cur1 cur2

如果第一个链表的值小于第二个链表的值那么尾插到此时第二个链表指针的前面

cur1->val<=cur2->val

并且让第一个指针cur1=cur1->next;

当然这里只是大题思路不是代码

如果大于 cur1->val>cur2->val

那么让cur2=cur2->next

在进行操作的时候,我们为了避免在第一个位置插入 ,从而加入了头指针

OK,看代码呗

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    if(list1==NULL&&list2==NULL)
    return list1;
    else if(list1!=NULL&&list2==NULL)
    return list1;
    else if(list1==NULL&&list2!=NULL)
    return list2;
    struct ListNode*p1=list1;
    struct ListNode*p2=list2;
    struct ListNode*p2head=(struct ListNode*)malloc(sizeof(struct ListNode));
    p2head->next=p2;
    struct ListNode*front=p2head;
    while(p1&&p2)
    {
        if(p1->val<=p2->val)
        {
            //双指针中间插入
            //
            front->next=p1;
            struct ListNode*temp=p1;
            p1=p1->next;
            temp->next=p2;
            front=temp;
        }
        else
       {
        front=p2;
        p2=p2->next;
       }
    }
    //如果p2==null此时p1不为空,把p1接起来
    if(p2==NULL)
    front->next=p1;
    return p2head->next;


}

第二题

现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

链表分割_牛客题霸_牛客网 (nowcoder.com)

ok

这个题目其实也可以暴力

但是

已经写了好多次暴力了,对吧

这一次可以说说思路

仍然可以使用数组,然后直接排序,不管x为多少,按从小到大的顺序,没有错

然后构建链表,这个题目就完了

挺暴力的吧

可以的

但是这个代码和之前大差不差

直接来看具体的思路

我们可以把一个链表分成两个链表,list1一个是大于等于x的,list2一个是小于x的

最后让list2连接list1

就完成任务了

思路看起来简单但是细节还是很多

我们在代码中有注释

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
    if(pHead==NULL&&pHead->next==NULL)
    return pHead;
    auto *head=(ListNode*)malloc(sizeof(ListNode));
    head->next=pHead;
    //头结点
    ListNode*front=head;
    ListNode*newlist=NULL;
    ListNode*newtail=NULL;
    ListNode*p=pHead;
    while(p)
    {
        if(p->val<x)
        {
            //在原有的链表上去除小于x的值
            ListNode*temp=p;
            p=p->next;
            temp->next=NULL;
            front->next=p;
            //对新的链表操作
            //小于x的值重新连成一个链表
            if(newlist==NULL)
            {
                newlist=newtail=temp;
            }
            else
            {
                newtail->next=temp;
                newtail=temp;
            }
        }
        else 
        {
            front=p;
            p=p->next;  
        }
    }
    //不要忘了呀,新的链表有可能为空呀,哎呦,搞了个半天
    if(newtail==NULL)
    return pHead;
    else
    newtail->next=head->next;
    return newlist;
    }
};

这个题目思路还是不难,继续努力

第三题

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

复制代码
1->2->2->1
复制代码
返回:true

这个题目有难度了

所以我们可以通过画图来理解一下

思路,

我们可以找到中间的结点,把中间的结点逆置过来

然后一个指针从中间

另一个指针从开头

一直判断他们是否相等,就可以得到答案

看图呗

OK,找中以及逆转链表之前已经有过讲解了

单链表的经典oj题(1)-CSDN博客

好吧,不懂可以去看

我们现在直接去看代码喽

 ListNode*middle(ListNode*A)
    {
        ListNode*slow=A;
        ListNode*fast=A;
        while(fast&&fast->next)
        {
            fast=fast->next->next;
            slow=slow->next;
        }
        return slow;
    }
    ListNode* revearse(ListNode*src)
    {
        ListNode* cur=src;
        ListNode* prev=NULL;
        while(cur)
        {
            ListNode* temp=cur->next;
            cur->next=prev;
            prev=cur;
            cur=temp;
        }
        return prev;
    }
    bool chkPalindrome(ListNode* A) {
        // write code here
        ListNode*cur=middle(A);
        ListNode* tail=revearse(cur);
        while(A&&tail)
        {
            if(tail->val!=A->val)
            return false;
            tail=tail->next;
            A=A->next;
        }
        return true;
    }

第四题

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

题目在这里

160. 相交链表 - 力扣(LeetCode)

OK

思路

首先要判断他们是否是有相交节点

OK可以让他们走到最后一个节点

如果他们的最后一个节点相等,那他们就有相交节点,那么问题来了

怎么找到节点呢

要是知道两个链表的长度,再利用相对位移让他们距离相交节点的位移一样

最后让他们同时走,再判断,直到相等即可

OK

看代码

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode*A =headA;
    struct ListNode*B =headB;
    //这里最后一个节点没有加上,所以初始为1
    int sizeA=1;int sizeB=1;
    while(A->next)
    {
        A=A->next;
        sizeA++;
    }
    while(B->next)
    {
        B=B->next;
        sizeB++;
    }
    if(A!=B)return NULL;
    //通过假设,来简化代码
    //总之就是让A最长
    if(sizeA<sizeB)
    {
        A=headB;
        B=headA;
    }
    else
    {
        A=headA;
        B=headB;
    }
    int abs=sizeA-sizeB>0?sizeA-sizeB:sizeB-sizeA;
    //使他们里相交点的距离相等
    while(abs--)
    {
        A=A->next;
    }
    while(A!=B)
    {
        A=A->next;
        B=B->next;
    }
    return B;
}

第五题

141. 环形链表 - 力扣(LeetCode)

题目

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

复制代码
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点

OK

这个题目的思路很重要,我这里有两种做法

暴力求解

如果只要求求出题目,那么我们可不可以用一个数据来标明这个节点走过没有

  • -10^5 <= Node.val <= 10^5

当我们走过一个结点

直接给他加上一个10^6+10

+10是防止特殊情况,那么只要遍历一遍,走到某个节点,如果它大于

10^5证明我们走过这里,但是此时又回到了这里,此时可以判断为有环

缺点是破坏了链表

bool hasCycle(struct ListNode *head) {
    while(head)
    {
        if(head->val>1e5)
        return true;
        else
        {
            head->val+=1e6+10;
        }
        head=head->next;
    }
    return false;
}

这种办法还是挺有意思的不是吗?

OK

来看看不改变链表的结构的写法吧

这个还是要画图理解,并且这里还是涉及到了相对位置的概念

bool hasCycle(struct ListNode *head) 
{
    struct ListNode * slow = head ;
    struct ListNode * fast = head ;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;

        if(slow == fast)
        return true;
    }
    return false;

}

话不多说下一题

第六题

142. 环形链表 II - 力扣(LeetCode)

这个题目,是上个题目的升级版

OK看题

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始 )。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改链表。

示例 1:

复制代码
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

思路

大家看这里就是不允许修改链表了

这该如何是好

还是画图吧

这个题目更是一个物理的模型

OK,看代码吧

struct ListNode *detectCycle(struct ListNode *head) {
  struct ListNode*slow=head;
  struct ListNode*fast=head;
  struct ListNode*meet=NULL;
  struct ListNode*find=head;
  while(fast&&fast->next)
  {
    fast=fast->next->next;
    slow=slow->next;
    if(fast==slow)
    {
        meet=slow;
        break;
    }
  }
  if(meet==NULL)
  return NULL;
  while(find!=meet)
  {
    find=find->next;
    meet=meet->next;
  }
  return find;
}

总结

今天的题还是挺多的,好好练吧

相关推荐
小码农<^_^>30 分钟前
优选算法精品课--滑动窗口算法(一)
算法
羊小猪~~32 分钟前
神经网络基础--什么是正向传播??什么是方向传播??
人工智能·pytorch·python·深度学习·神经网络·算法·机器学习
软工菜鸡1 小时前
预训练语言模型BERT——PaddleNLP中的预训练模型
大数据·人工智能·深度学习·算法·语言模型·自然语言处理·bert
南宫生1 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
AI视觉网奇1 小时前
sklearn 安装使用笔记
人工智能·算法·sklearn
JingHongB2 小时前
代码随想录算法训练营Day55 | 图论理论基础、深度优先搜索理论基础、卡玛网 98.所有可达路径、797. 所有可能的路径、广度优先搜索理论基础
算法·深度优先·图论
weixin_432702262 小时前
代码随想录算法训练营第五十五天|图论理论基础
数据结构·python·算法·深度优先·图论
小冉在学习2 小时前
day52 图论章节刷题Part04(110.字符串接龙、105.有向图的完全可达性、106.岛屿的周长 )
算法·深度优先·图论
Repeat7152 小时前
图论基础--孤岛系列
算法·深度优先·广度优先·图论基础
小冉在学习2 小时前
day53 图论章节刷题Part05(并查集理论基础、寻找存在的路径)
java·算法·图论