算法刷题第二篇——链表
Posted 赵四司机
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法刷题第二篇——链表相关的知识,希望对你有一定的参考价值。
个人简介:
> 📦个人主页:赵四司机
> 🏆学习方向:JAVA后端开发
> 📣种一棵树最好的时间是十年前,其次是现在!
> 🔔博主推荐网站:牛客网 刷题|面试|找工作神器
> 💖喜欢的话麻烦点点关注喔,你们的支持是我的最大动力。
前言:
最近有不少小伙伴私信博主问我马上到秋招了,而自己平时没怎么练过算法,在算法这一块存在很大的弱势,应该怎么快速提升自己的算法水平。在这里我首先要说的是算法能力并不是可以快速掌握的,这需要慢慢积累,因为算法不仅考验我们的知识记忆深度,还考验我们的思维广度,因此很多很多大厂面试都会注重算法的考核。
其实博主一开始也没怎么练过算法题,但是对于中等简单的算法题还是可以通过一段时间的刷题来习得的。我最近就在一个叫牛客网的网站上面刷面试算法题,上面还很贴心给我们总结出了常考的题目,像剑指Offer、面试必刷Top101、面试高频榜单都有,先上图为证:
怎样,是不是很心动,下面我给你们介绍一下这个网站,传送门:牛客刷题神器
目录
一:反转链表
1.题目要求
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
要求:空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n) 。
如当输入链表1,2,3时,经反转后,原链表变为3,2,1,所以对应的输出为3,2,1。
以上转换过程如下图所示:
2.解题思路
反转链表可以考虑用栈来解决,将链表节点依次进栈,然后再出栈形成链表即可,但是这样空间复杂度就为O(n)级别。
我们可以做到在空间复杂度为O(1)级别情况下解决问题,可以做到原地反转链表。具体做法为摘掉当前节点然后将其链接到前面形成的链表后面,当然还需要另外一个节点记录尚未反转的链表表头。
3.代码实现
/*
public class ListNode
int val;
ListNode next = null;
ListNode(int val)
this.val = val;
*/
public class Solution
public ListNode ReverseList(ListNode head)
ListNode newHead = null;
while(head != null)
ListNode temp = head.next;
head.next = newHead;
newHead = head;
head = temp;
return newHead;
二:链表中的节点每k个一组翻转
1.题目要求
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表。如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样。你不能更改节点中的值,只能更改节点本身。
要求空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如:
给定的链表是 1→2→3→4→5
对于 k = 2k=2 , 你应该返回 2→1→4→3→5
对于 k = 3k=3 , 你应该返回 3→2→1→4→5
2.解题思路
- step 1:每次从进入函数的头节点优先遍历链表k次,分出一组,若是后续不足k个节点,不用反转直接返回头。
- step 2:从进入函数的头节点开始,依次反转接下来的一组链表,反转链表同上。
- step 3:这一组经过反转后,原来的头变成了尾,后面接下一组的反转结果,下一组采用上述递归继续。
3.代码实现
import java.util.*;
/*
* public class ListNode
* int val;
* ListNode next = null;
*
*/
public class Solution
/**
*
* @param head ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode reverseKGroup (ListNode head, int k)
// write code here
ListNode tail = head;
for(int i = 0; i < k; i++)
if(tail == null)
return head;
tail = tail.next;
//翻转链表
ListNode pre = null;
ListNode cur = head;
while(cur != tail)
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
head.next = reverseKGroup(tail,k);
return pre;
三:合并两个排序的链表
1.题目要求
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0≤n≤1000,−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
如输入1,3,5,2,4,6时,合并后的链表为1,2,3,4,5,6,所以对应的输出为1,2,3,4,5,6,转换过程如下图所示:
或输入-1,2,4,1,3,4时,合并后的链表为-1,1,2,3,4,4,所以对应的输出为-1,1,2,3,4,4,转换过程如下图所示:
2.实现思路
初始化:定义cur指向新链表的头结点
操作:
- 如果l1指向的结点值小于等于l2指向的结点值,则将l1指向的结点值链接到cur的next指针,然后l1指向下一个结点值
- 否则,让l2指向下一个结点值
- 循环步骤1,2,直到l1或者l2为nullptr
- 将l1或者l2剩下的部分链接到cur的后面
3.代码实现
/*
public class ListNode
int val;
ListNode next = null;
ListNode(int val)
this.val = val;
*/
public class Solution
public ListNode Merge(ListNode list1,ListNode list2)
ListNode res = new ListNode(-1);
ListNode cur = res;
while(list1 != null && list2 != null)
if(list1.val < list2.val)
cur.next = list1;
cur = list1;
list1 = list1.next;
else
cur.next = list2;
cur = list2;
list2 = list2.next;
cur.next = (list1 == null) ? list2 : list1;
return res.next;
四:合并k个已排序的链表
1.题目要求
合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
数据范围:节点总数 0≤n≤5000,每个节点的val满足 |val| <= 1000∣val∣<=1000
要求:时间复杂度 O(nlogn)O(nlogn)
示例1
输入:[1,2,3,4,5,6,7]
复制返回值:1,2,3,4,5,6,7
示例2
输入:[1,2,1,4,5,6]
复制返回值:1,1,2,4,5,6
2.实现思路
可以考虑使用优先队列解决问题,优先队列即PriorityQueue,是一种内置的机遇堆排序的容器,分为大顶堆与小顶堆,大顶堆的堆顶为最大元素,其余更小的元素在堆下方,小顶堆与其刚好相反。且因为容器内部的次序基于堆排序,因此每次插入元素时间复杂度都是O(log2n)O(log_2n)O(log2n),而每次取出堆顶元素都是直接取出。
具体实现:
- step 1:不管是Java还是C++都需要重载比较方法,构造一个比较链表节点大小的小顶堆。
- step 2:先遍历k个链表头,将不是空节点的节点加入优先队列。
- step 3:每次依次弹出优先队列中的最小元素,将其连接在合并后的链表后面,然后将这个节点在原本链表中的后一个节点(如果不为空的话)加入队列,类似上述归并排序双指针的过程。
3.代码实现
/**
* Definition for singly-linked list.
* public class ListNode
* int val;
* ListNode next;
* ListNode(int x)
* val = x;
* next = null;
*
*
*/
import java.util.*;
public class Solution
public ListNode mergeKLists(ArrayList<ListNode> lists)
PriorityQueue<ListNode> queue = new PriorityQueue<>((val1,val2) -> val1.val - val2.val);
for(int i =0; i < lists.size(); i++)
if(lists.get(i) != null)
queue.add(lists.get(i));
ListNode res = new ListNode(-1);
ListNode cur = res;
while(!queue.isEmpty())
cur.next = queue.remove();
cur = cur.next;
if(cur.next != null)
queue.add(cur.next);
return res.next;
今天的题目介绍就到这,实践才是检验真理的唯一标准,算法光看还不行,必须得自己动手练习,传送门链接,一键直达练习场 :牛客网 刷题|面试|找工作神器
蓝桥杯备赛刷题
第十三届蓝桥杯第二场模拟赛题解
马上到蓝桥杯了,虽然省赛简单化,但是刷题是不可或缺的,下面我会更新蓝桥杯刷题题解,一起冲进国赛
文章目录
前言
马上到蓝桥杯了,虽然省赛简单化,但是刷题是不可或缺的,下面我会更新蓝桥杯刷题题解,一起冲进国赛
提示:以下是本篇文章正文内容,下面案例可供参考
填空题
第一题
0-255 所以最大为255
第二题
直接利用gcd求解最大公约数即可
import gmpy2
count=0
for i in range(2,2022):
if(gmpy2.gcd(2021,i)!=1):
count+=1
print(count)
答案:89
第三题
暴力解法求解, i=jj-kk
进而得出 i=(j-k)*(j+k)所以j-k一定是大于0的 所以j>k
i=( 一个正数)*j+k
所以j一定小于i的,所以遍历即可
count=0
flag=0
for i in range(2022):
flag=0
for j in range(2022):
if(flag==1):
break
for k in range(j):
if(flag==0):
if(i==j*j-k*k):
count+=1
flag=1
break
else:
continue
print(count)
答案是
1516
第四题
问题描述
小蓝要用01串来表达一段文字,这段文字包含 a, b, c, d, e, f 共 6 个字母,每个字母出现的次数依次为:a 出现 10次,b 出现 20 次,c 出现 3 次,d 出现 4 次,e 出现 18 次,f 出现 50 次。
小蓝准备分别对每个字母使用确定的01串来表示,不同字母的01串长度可以不相同。
在表示文字时,将每个字母对应的01串直接连接起来组成最终的01串。为了能够正常还原出文字,小蓝的编码必须是前缀码,即任何一个字符对应的01串都不能是另一个字符对应的01串的前缀。
例如,以下是一个有效的编码:a: 000
b: 111
c: 01
d: 001
e: 110
f: 100
其中 c 的长度为 2,其它字母的编码长度为
3,这种方式表示这段文字需要的总长度为:103+203+32+43+183+503=312。上面的编码显然不是最优的,将上面的 f
的编码改为 10,仍然满足条件,但是总长度为 262,要短
50。要想编码后的总长度尽量小,应当让出现次数多的字符对应的编码短,出现次数少的字符对应的编码长。请问,在最优情况下,编码后的总长度最少是多少?
代码如下(示例):
一看就是哈夫曼编码,这里由于是填空题,直接手算就行了,多的距离近,少的距离远
计算如下
50*1+2*20*3*18+10*4*4+4*3
数据结构哈夫曼知识了
画个图手算一下就有了
第五题
下面的矩阵中包含 ABCDEF 六种字符,请问出现最多的字符出现了几次?
FFEEFEAAECFFBDBFBCDA
DACDEEDCCFFAFADEFBBA
FDCDDCDBFEFCEDDBFDBE
EFCAAEECEECDCDECADDC
DFAEACECFEADCBFECADF
DFBAAADCFAFFCEADFDDA
EAFAFFDEFECEDEEEDFBD
BFDDFFBCFACECEDCAFAF
EFAFCDBDCCBCCEADADAE
BAFBACACBFCBABFDAFBE
FCFDCFBCEDCEAFBCDBDD
BDEFCAAAACCFFCBBAAEE
CFEFCFDEEDCACDACECFF
BAAAFACDBFFAEFFCCCDB
FADDDBEBCBEEDDECFAFF
CDEAFBCBBCBAEDFDBEBB
BBABBFDECBCEFAABCBCF
FBDBACCFFABEAEBEACBB
DCBCCFADDCACFDEDECCC
BFAFCBFECAACAFBCFBAF
data = pd.read_csv(
‘https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv’)
print(data.head())
利用collect来进行统计
```python
from collections import Counter
s='FFEEFEAAECFFBDBFBCDADACDEEDCCFFAFADEFBBAFDCDDCDBFEFCEDDBFDBEEFCAAEECEECDCDECADDCDFAEACECFEADCBFECADFDFBAAADCFAFFCEADFDDAEAFAFFDEFECEDEEEDFBDBFDDFFBCFACECEDCAFAFEFAFCDBDCCBCCEADADAEBAFBACACBFCBABFDAFBEFCFDCFBCEDCEAFBCDBDDBDEFCAAAACCFFCBBAAEECFEFCFDEEDCACDACECFFBAAAFACDBFFAEFFCCCDBFADDDBEBCBEEDDECFAFFCDEAFBCBBCBAEDFDBEBBBBABBFDECBCEFAABCBCFFBDBACCFFABEAEBEACBBDCBCCFADDCACFDEDECCCBFAFCBFECAACAFBCFBAF'
s=Counter(s)
s1=dict(s)
print(s1.values())
答案是78
第六题
问题描述
小蓝要到店里买铅笔。铅笔必须一整盒一整盒买,一整盒 12 支,价格 p 元。小蓝至少要买 t 支铅笔,请问他最少花多少钱?
输入格式
输入一行包含两个整数 p、t,用一个空格分隔。
输出格式
输出一行包含一个整数,表示答案。
样例输入
5 30
样例输出
15
样例说明
小蓝至少要买3盒才能保证买到30支铅笔,总共花费 15 元。
评测用例规模与约定
对于所有评测用例,1 <= p <= 100,1 <= t <= 10000
除数问题
list1= input('输入一行包含两个整数 p、t,用一个空格分隔').split(' ')
a=int(list1[0])
b=int(list1[1])
if(b%12==0):
print(b//12*a)
else:
print((b//12+1)*a)
第七题
问题描述
给定一个三角形的三条边的长度 a, b, c,请问这个三角形是不是一个直角三角形。
输入格式
输入一行包含三个整数 a, b, c,表示三角形三边的长度,相邻整数之间用一个空格分隔。
输出格式
如果是直角三角形,输出“YES”(全大写),否则输出“NO”(全大写)。
样例输入
3 4 5
样例输出
YES
样例输入
4 5 4
样例输出
NO
评测用例规模与约定
对于所有评测用例,1 <= a, b, c <= 1000。
一共三种情况:
a*a+b*b==c*c
a*a+c*c==b*b
a*a==c*c+b*b
```python
list1=input('输入一行包含三个整数 a, b, c,表示三角形三边的长').split(' ')
a=int(list1[0])
b=int(list1[1])
c=int(list1[2])
if(a*a+b*b==c*c or a*a+c*c==b*b or a*a==c*c+b*b):
print('YES')
else:
print("NO")
加个条件过去就行了
第八题
问题描述
n 个小朋友正在做一个游戏,每个人要分享一个自己的小秘密。 每个小朋友都有一个 1 到 n 的编号,编号不重复。
为了让这个游戏更有趣,老师给每个小朋友发了一张卡片,上面有一个 1 到 n 的数字,每个数字正好出现一次。
每个小朋友都将自己的秘密写在纸上,然后根据老师发的卡片上的数字将秘密传递给对应编号的小朋友。如果老师发给自己的数字正好是自己的编号,这个秘密就留在自己手里。
小朋友们拿到其他人的秘密后会记下这个秘密,老师会再指挥所有小朋友将手中的秘密继续传递,仍然根据老师发的卡片上的数字将秘密传递给对应编号的小朋友。
这样不断重复 n 次。 现在,每个小朋友都记下了很多个秘密。
老师现在想找一些小朋友,能说出所有秘密,请问老师最少要找几个小朋友?输入格式
输入的第一行包含一个整数 n。 第二行包含 n 个整数 a[1], a[2], …,
a[n],相邻的整数间用空格分隔,分别表示编号 1 到 n 的小朋友收到的数字。输出格式
输出一行包含一个整数,表示答案。
样例输入
6 2 1 3 5 6 4
样例输出
3
样例说明
最终小朋友 1, 2 互相知道了对方的秘密,小朋友 3 只知道自己的秘密,小朋友 4, 5, 6 互相知道了对方的秘密。 至少要找
3 个小朋友才能说出所有秘密。评测用例规模与约定
对于 30% 的评测用例,2 <= n <= 30。 对于 60% 的评测用例,2 <= n <= 1000。
对于所有评测用例,2 <= n <= 100000。
————————————————
我们这里利用填坑思想,如果 遍历过程就是遍历signal数组的过程,并将其填1 如果已经是1了那末就+1,因为要想知道对方秘密,那末一定是能按照标志能便利所有了,如果中断了那末就将结果+1
n=5
temp=[]
count=0
s='2 3 1 5 4'.split(' ')
print(s)
sigal=n*[0]
p=int(s[0])-1
ee=0
while 1:
if(sigal[p]!=1):
sigal[p]=1
p=int(s[p])-1
ee+=1
if(ee==n):
break
else:
p = int(s[ee])-1
print(sigal)
count+=1
print(count+1)
最后一次没有循环,所以结果再加一就行了
以上是关于算法刷题第二篇——链表的主要内容,如果未能解决你的问题,请参考以下文章
(基础杂记) —— 2021-07-13 —— 牛客刷题错题记录
(基础杂记) —— 2021-07-13 —— 牛客刷题错题记录