单链表经典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;
}

总结

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

相关推荐
szuzhan.gy7 分钟前
DS查找—二叉树平衡因子
数据结构·c++·算法
一只码代码的章鱼38 分钟前
排序算法 (插入,选择,冒泡,希尔,快速,归并,堆排序)
数据结构·算法·排序算法
青い月の魔女1 小时前
数据结构初阶---二叉树
c语言·数据结构·笔记·学习·算法
我要出家当道士1 小时前
Nginx单向链表 ngx_list_t
数据结构·nginx·链表·c
林的快手2 小时前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode
千天夜2 小时前
多源多点路径规划:基于启发式动态生成树算法的实现
算法·机器学习·动态规划
从以前2 小时前
准备考试:解决大学入学考试问题
数据结构·python·算法
.Vcoistnt2 小时前
Codeforces Round 994 (Div. 2)(A-D)
数据结构·c++·算法·贪心算法·动态规划
ALISHENGYA3 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战训练三)
数据结构·c++·算法·图论
我码玄黄5 小时前
正则表达式优化之算法和效率优化
前端·javascript·算法·正则表达式