LeetCode/NowCoder-链表经典算法OJ练习3

  孜孜不倦:孜孜:勤勉,不懈怠。指工作或学习勤奋不知疲倦。💓💓💓

目录

说在前面

题目一:返回倒数第k个节点

题目二:链表的回文结构

题目三:相交链表

SUMUP结尾


说在前面

 dear朋友们大家好!💖💖💖数据结构的学习离不开刷题,在对链表的相关OJ进行练习后又更新复杂度的OJ,这并不意味这链表的题目就结束了,我们今天就继续联系链表相关的OJ练习当今天我们的题目除了LeetCode,还来自NowCoder。

 👇👇👇

友友们!🎉🎉🎉点击这里进入力扣leetcode学习🎉🎉🎉


​以下是leetcode题库界面:

 👇👇👇

🎉🎉🎉点击这里进入牛客网NowCoder刷题学习🎉🎉🎉
​以下是NowCoder题库界面:

​​

 ​​

题目一:返回倒数第k个节点

题目链接:面试题 02.02. 返回倒数第 k 个节点 - 力扣(LeetCode)

题目描述:

题目分析:

 思路1:将创建一个数组temp,将链表中节点的地址存入数组temp,再返回数组中下标为n-k的地址所指向的数据。

举例:1->2->3->4->5->6  和 k = 3

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
#define numsSize 100
typedef struct ListNode ListNode;
int kthToLast(struct ListNode* head, int k) {
    ListNode* pcur = head;
    ListNode* temp[numsSize];//创建临时数组
    int i = 0;
    while (pcur != NULL)//将链表节点地址存入数组temp
    {
        temp[i++] = pcur;
        pcur = pcur->next;
    }
    return temp[i - k]->val;//返回倒数第k个节点的数据
}

不过,假设链表很长,此时空间复杂度就会比较高,所以用numsSize固定长度显然不是好的办法,应该用动态内存分配的办法来初始化temp

代码如下:

 /**
  * Definition for singly-linked list.
  * struct ListNode {
  *     int val;
  *     struct ListNode *next;
  * };
  */
typedef struct ListNode ListNode;
int kthToLast(struct ListNode* head, int k) {
    ListNode* pcur = head;
    int length = 0;
    while (pcur != NULL)//遍历数组,得到节点的个数
    {
        length++;
        pcur = pcur->next;
    }
    //动态创建二级指针变量temp
    ListNode** temp = (ListNode**)malloc(length * sizeof(ListNode*));
    pcur = head;
    int i = 0;
    while (pcur != NULL)//将链表节点地址存入temp
    {
        temp[i++] = pcur;
        pcur = pcur->next;
    }
    return temp[i - k]->val;//返回倒数第k个节点的数据
}

不过显然空间复杂度为O(N),不是一个非常好的办法。如果给出前提条件:空间复杂度为O(1),这个方法就不行了。

 思路2:将创快慢指针法:定义快慢指针fast、slow,先让fast走k步,再让slow和fast同时走,这样当fast走完slow刚好指向倒数第k个节点。

举例:1->2->3->4->5->6  和 k = 3

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
int kthToLast(struct ListNode* head, int k) {
    //定义快慢指针
    ListNode* slow = head;
    ListNode* fast = head;
    while (k--)//让fast先走k步
    {
        fast = fast->next;
    }
    //当fast走完,此时slow指向的就是倒数第k个节点
    while (fast != NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow->val;
}

 ​​

题目二:链表的回文结构

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

题目描述:

题目分析:

 思路:先找到链表的中间节点,再将链表的后半部分反转,比较前半部分和后半部分的元素是否相等。

我们需要用到之前OJ练习1中的两个函数:


1.链表的中间节点:876. 链表的中间结点 - 力扣(LeetCode)

2.反转链表:206. 反转链表 - 力扣(LeetCode)

如果忘记了,大家点击这里复习:LeetCode/NowCoder-链表经典算法OJ练习1

代码如下:

/*struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
	//寻找链表的中间节点
	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;
	}
	//反转链表
	struct ListNode* reverseList(struct ListNode* head) {
		if (head == NULL)
			return NULL;
		struct ListNode* n1 = NULL;
		struct ListNode* n2 = head;
		struct ListNode* n3 = head->next;
		while (n2)
		{
			n2->next = n1;
			n1 = n2;
			n2 = n3;
			if (n3)
				n3 = n3->next;
		}
		return n1;
	}
	bool chkPalindrome(ListNode* A) {
        //寻找链表的中间节点
		struct ListNode* mid = middleNode(A);
        //反转链表后半段
		struct ListNode* rmid = reverseList(mid);
		while (rmid && A)//比较前后段是否相同
		{
			if (rmid->val != A->val)
				return false;
			rmid = rmid->next;
			A = A->next;
		}
		return true;
	}
};

像这种题属于组合体,比较综合,同时也告诉我们具有一定功能的代码在下次使用时可以稍加修改再Ctrl+C/V,可以省去很多麻烦,也比较方便。  

 ​​

题目三:相交链表

题目链接:160. 相交链表 - 力扣(LeetCode)

题目描述:

题目分析:

思路:这题比较复杂,我们需要模块化思考。同时注意单链表相交为"Y"型而不可能为"X"型,因为单链表没有两个next指针。

1、如何判断相交?

判断尾指针,如果尾指针地址相同则相交(注意,不能用尾节点所存储的值是否相同判断,因为之前有可能也有节点存储了和尾节点相同的值)。

2、若相交,如何找出第一个交点?

思路1:A链表的节点依次和B链表的所有节点比较,A的某个节点和B链表的某个节点相等,则这个节点就是交点。

 代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    ListNode* cur1 = headA;
    ListNode* cur2 = headB;
    //让A、B链表都走到尾节点
    while(cur1->next)
    {
        cur1 = cur1->next;
    }
    while(cur2->next)
    {
        cur2 = cur2->next;
    }
    if(cur1 != cur2)//判断尾节点是否相等
        return NULL;
    cur1 = headA;
    while(cur1->next)//让A中每个节点都和B中的所有节点比较
    {
        cur2 = headB;
        while(cur2->next)
        {
            if(cur1 == cur2)//第一个相等的就是交点
            return cur2;
            cur2 = cur2->next;
        }
        cur1 = cur1->next;
    }   
    return cur2;
}

这个方法的时间复杂度为O(N^2)

思路2:分别计算出链表A、B的长度,让长的先走差距的步数,然后再让两链表同时开始走,第一个相等的就是交点。

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
	ListNode* cur1 = headA, * cur2 = headB;
	int lenA = 0;
	int lenB = 0;
	while (cur1->next)//统计链表A的长度
	{
		lenA++;
		cur1 = cur1->next;
	}
	while (cur2->next)//统计链表B的长度
	{
		lenB++;
		cur2 = cur2->next;
	}
	if (cur1 != cur2)//判断是否有交点
		return NULL;
    //假设法,设置长短链表
	ListNode* LongList = headA, * ShortList = headB;
	if (lenA < lenB)
	{
		LongList = headB;
		ShortList = headA;
	}
	int gap = abs(lenA - lenB);//两链表节点差值
	while (gap--)//让长的先走差值的步数
	{
		LongList = LongList->next;
	}
	while (LongList != ShortList)//让两链表一起走,第一个相等的就是交点
	{
		LongList = LongList->next;
		ShortList = ShortList->next;
	}
	return LongList;
}

这个方法的时间复杂度为O(N)

对比两种解法,显然第二种方法是更好的。 

 

SUMUP结尾

数据结构就像数学题,需要刷题才能对它有感觉。之后还会更新数据结构相关的练习题、面试题,希望大家一起学习,共同进步~

如果大家觉得有帮助,麻烦大家点点赞,如果有错误的地方也欢迎大家指出~

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值