【数据结构之链表】力扣刷题记录一


前言

今天开始在力扣上刷有关链表的题目,先从简单的开始~

具体题目

面试题 02.02. 返回倒数第 k 个节点

原问题描述: 戳这里.

解题思路:
  快慢指针的问题~
  快指针先走k步,然后快慢指针同时走,直到快指针指向最后一个结点时,慢指针所指内容即为倒数第k个节点。

解题代码:

class Solution {
public:
	int kthToLast(ListNode* head, int k) 
	{
		ListNode* l1 = head, *l2;
		//快指针先走
		for (int i = 0; i < k && l1 != NULL; i++)
		{
			l1 = l1->next;
		}
		//两个指针同时走
		l2 = head;
		while (l1!=NULL)
		{
			l1 = l1->next;
			l2 = l2->next;
		}
		return l2->val;
	}
};

AC结果:
在这里插入图片描述

剑指 Offer 22. 链表中倒数第k个节点

问题描述:戳这里.

解题思路:
和上一题目一样,只不过是最后需要返回l2而不是l2所指的值。

解题代码:


class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
         ListNode* l1 = head, *l2;
		//快指针先走
		for (int i = 0; i < k && l1 != NULL; i++)
		{
			l1 = l1->next;
		}
		//两个指针同时走
		l2 = head;
		while (l1!=NULL)
		{
			l1 = l1->next;
			l2 = l2->next;
		}
		return l2;
	}
};

AC结果:
在这里插入图片描述

面试题 02.03. 删除中间节点

问题描述:戳这里

解题思路:
题目说得不是很清楚,原来以为给定的参数是链表头,结果却是链表中要删除的节点。【看来程序员除了要会写代码还需要会看懂需求】理解完题意之后挺简单的,按照其他人的评论就是 我变成你之后再鲨了自己【bu shi】

解题代码:

class Solution {
public:
	void deleteNode(ListNode* node) {
		node->val = node->next->val;
		node->next = node->next->next;
	}
};

AC结果:
在这里插入图片描述

21.合并两个有序链表

问题描述:戳这里

解题思路:
这是一道比较简单的题目,有两种做法,一种是直接创建一条新的链表,然后不断的比较原来的旧链表,把值小的节点添加到新链表中,思路简单,但是需要用到多一点的内存。另一种方法是直接在原来的链表上进行修改【这一种不知道为什么我会超时】,参考了评论中使用递归进行合并(有点像归并排序)

解题代码:
代码参考题目评论,第一次意识到原来指针也可以使用swap。

class Solution {
public:
	ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
		ListNode* head = new ListNode(0);
		ListNode* n = head;
		while (l1 && l2)
		{
			if (l1->val < l2->val)
				swap(l1, l2);
			n->next = l1;
			l1 = l1->next;
			n = n->next;
		}
		n->next = l1 ? l1 : l2;
		return head->next;
	}
};

递归方法的思路也比较容易理解

class Solution {
public:
	ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
	 if(l1==NULL)
        return l2;
    if(l2==NULL)
        return l1;
    if(l1->val < l2->val){
        l1->next = mergeTwoLists(l1->next,l2);
        return l1;
    }else{
        l2->next = mergeTwoLists(l1,l2->next);
        return l2;
    }
	}
};

AC结果:
代码一:
在这里插入图片描述
代码二:
在这里插入图片描述

234.回文链表

问题描述:戳这里

解题思路:
这道题目的解法蛮多的,但有一些实现起来有点困难,也比较难理解。这里使用的是逆向建立一个新链表然后进行比较。时间复杂度还是O(n),空间复杂度也是O(n).

解题代码:

class Solution {
public:
	bool isPalindrome(ListNode* head) 
	{
		//反向建立链表
		ListNode* l1 = head, *newprev = NULL;
		while (l1)
		{
			ListNode *newone = new ListNode(l1->val, newprev);
			newprev = newone;
			l1 = l1->next;
		}
		//一起比较两个链表
		l1 = head;
		while (l1 && newprev)
		{
			if (l1->val != newprev->val)
			{
				return false;
			}
			l1 = l1->next;
			newprev = newprev->next;
		}
		return true;
	}
};

AC结果:
在这里插入图片描述

206. 反转链表

问题描述:戳这里

解题思路:
意外的发现可以直接套用上面的结果。

解题代码:

class Solution {
public:
	ListNode* reverseList(ListNode* head) {
		//反向建立链表
		ListNode* l1 = head, *newprev = NULL;
		while (l1)
		{
			ListNode *newone = new ListNode(l1->val, newprev);
			newprev = newone;
			l1 = l1->next;
		}
		return newprev;
	}
};

AC结果:
在这里插入图片描述

剑指 Offer 18. 删除链表的节点

问题描述:戳这里

解题思路:
这道题目和之前不太一样的地方在于题目只给出了要删除节点的数值,因此需要遍历链表找到该节点后进行删除。遇到的问题有两个:①一开始我使用了上面题目的我鲨我自己的代码,同时加上了如果要删除的节点是最后一个的话,就将该节点置为NULL,结果发现测试样例是删除最后一个节点时会出错,即链表并没有真正的删除该节点,这是因为出现了虽然从代码上来看该节点已经置为NULL,但是内存并没有删除这一块,链表中的连接还保留着,因此输出的时候还会输出最后一个节点的值。②这里遍历的时候只遍历一次,默认只有一个节点需要删除,如果有多个相同值的节点要删除的话,需要从头遍历到尾。

解题代码:

class Solution {
public:
	ListNode* deleteNode(ListNode* head, int val) {
		ListNode* l1 = head,*l2=l1;
		while (l1->val!=val)
		{
			l2 = l1;
			l1 = l1->next;
		}
		if (l1->next != NULL)
		{
			l1->val = l1->next->val;
			l1->next = l1->next->next;
		}
		else
		{
			l2->next = NULL;
		}
		return head;
	}
};

AC结果:
在这里插入图片描述

876.链表的中间节点

问题描述:戳这里

解题思路:
使用快慢指针!
先从头出发,每一次慢指针走一步,快指针走两步,如果快指针已经指向NULL,则慢指针恰好指向链表的中间节点。

解题代码:

class Solution {
public:
	ListNode* middleNode(ListNode* head) {
		ListNode* slow,*quick;
		slow = quick = head;
		while (quick && quick->next!=NULL)
		{
			slow = slow->next;
			quick = quick->next;
			if (quick != NULL)
				quick = quick->next;
		}
		return slow;

	}
};

AC结果:
在这里插入图片描述

203.移除链表元素

问题描述:戳这里

解题思路:
依然还是我鲨我自己 ,需要注意一些小细节。

解题代码:

class Solution {
public:
	ListNode* removeElements(ListNode* head, int val) 
	{
		ListNode* l1 = head, * l2 = l1;
		while (l1!=NULL)
		{
			while (l1 && l1->val != val)
			{
				l2 = l1;
				l1 = l1->next;
			}
			if (l1 && l1->val == val)
			{
				if (l1->next != NULL)
				{
					l1->val = l1->next->val;
					l1->next = l1->next->next;
				}
				else if (l1 != head)
				{
					l2->next = NULL;
                    l1=NULL;
				}
				else
				{
					return NULL;
				}
			}
		}
		return head;
	}
};

AC结果:
https://leetcode-cn.com/problems/remove-linked-list-elements/

剑指 Offer 52. 两个链表的第一个公共节点

问题描述:戳这里

解题思路:
一开始我以为是找到两条链表中第一个相同的值,结果发现并不是,应该是两条链表最后一部分是一样的,借用题目的图:
在这里插入图片描述
这道题目想了蛮久的,找不到一个好的方法,看了评论直呼woc。官方的解答很好,大佬的评论很感人(bu shi)。
在这里插入图片描述
在这里插入图片描述

最后的解题思路是:使用两个指针,分别指向两条链表的头部,如果不相等,则依次向后走,当一个指针走完时,指向另一条链表的头部。再借用评论区一位大佬的话:
在这里插入图片描述

解题代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
	ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) 
	{
		if (headA == NULL || headB == NULL)
			return NULL;
		ListNode* l1 = headA;
		ListNode* l2 = headB;
		while (l1 != l2)
		{
			if (l1 == NULL)
				l1 = headB;
			else
			{
				l1 = l1->next;
			}
			if (l2 == NULL)
				l2 = headA;
			else
			{
				l2 = l2->next;
			}
		}
		return l1;
	}
};

AC结果:
在这里插入图片描述

1290.二进制链表的转换

问题描述:戳这里

解题思路:
这里可以简单的使用移位运算!
每次将当前的数值右移一位后加上链表的数值,直到遍历完整个链表。
需要注意符号的优先级顺序~

解题代码:

class Solution {
public:
	int getDecimalValue(ListNode* head) 
	{
		int n = 0;
		while (head)
		{
			n =(n<<1)+head->val;
			head = head->next;
		}
		return n;
	}
};

AC结果:
在这里插入图片描述

83.删除排序链表中的重复元素

问题描述:戳这里

解题思路:
继续我鲨我自己的删除节点法
在这里插入图片描述

解题代码:

class Solution {
public:
	ListNode* deleteDuplicates(ListNode* head) 
	{
		if (head == NULL)
			return NULL;
		int help = head->val;
		ListNode* l1 = head->next;
		ListNode* l2 = head;
		while (l1)
		{
			if (l1->val == help)
			{
				if (l1->next != NULL)
				{
					l1->val = l1->next->val;
					l1->next = l1->next->next;
				}
				else
				{
					l1 = NULL;
					l2->next = l1;
				}
			}
			else
			{
				help = l1->val;
				l2 = l1;
				l1=l1->next;
			}
		}
		return head;
	}
};

AC结果:
在这里插入图片描述

141.环形链表

问题描述:戳这里

解题思路:
这里使用了快慢指针,如果有环,快指针总能更快的进入到环中,慢指针慢一步进入环,只要在环内,两个指针终能相遇。

解题代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
	bool hasCycle(ListNode* head) 
	{
		if (head == NULL)
			return NULL;
		ListNode* quick = head;
		ListNode* slow = head;
		while (quick && slow)
		{
			quick = quick->next;
			if (quick != NULL)
			{
				quick = quick->next;
				slow = slow->next;
			}
			if (slow == quick)
				return true;
		}
		return false;
	}
};

AC结果:
在这里插入图片描述

剑指 Offer 06. 从尾到头打印链表

问题描述:戳这里

解题思路:
可以借助algorithm库中的reverse来对vector容器进行逆转。

解题代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
	vector<int> reversePrint(ListNode* head) {
		vector<int>res;
		while (head)
		{
			res.push_back(head->val);
			head = head->next;
		}
		reverse(res.begin(),res.end());
        return res;
	}
};

AC结果:
在这里插入图片描述

面试题 02.01. 移除重复节点

问题描述:戳这里

解题思路:
借助一个缓冲区visit来记录当前节点的值是否有出现过,如果有出现 继续采用我鲨我自己的删除结点法,如果没有,继续遍历链表。

解题代码:

class Solution {
public:
	ListNode* removeDuplicateNodes(ListNode* head) {
		int visit[20005]={ 0 };
		ListNode* l1 = head,*l2 = l1;
		while (l1!=NULL)
		{
			//有出现  我鲨我自己
			if (visit[l1->val]==1)
			{
				if (l1->next == NULL)
				{
					l2->next = NULL;
					break;
				}
				else
				{
					l1->val = l1->next->val;
					l1->next = l1->next->next;
				}
			}
			else if(visit[l1->val]==0)
			{
				visit[l1->val] = 1;
				l2 = l1;
				l1 = l1->next;
			}
		}
		return head;
	}
};

AC结果:
在这里插入图片描述

总结

  这篇博客中的题目都是力扣中有关链表tag的简单题目。其实写了这么多,感觉很多题目的解法都可以归为两种:快慢指针和删除结点。通过刷题我学会了删除结点中一种比较简便的操作【但是这种操作会有野指针出现,但是在代码里不影响】
  明天开始要刷中等难度的题目,看看中等难度的题目是不是也可以用这两种方法来解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选和操作应用两大类,单选部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作目,提高学生解决实际问的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值