leetcode刷题总结(一)

链表:

1.Sort a linked list in O(n log n) time using constant space complexity.
算法:用归并排序:
1.快慢指针找中点
2.建立头节点合并两端
3.递归这个过程,递归退出条件为归并段只剩一个节点或不剩节点。
2. Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?
算法:
快慢指针,如果相遇就是有否则没有环。

3.Given a singly linked list L: L 0→L 1→…→L n-1→L n,
reorder it to: L 0→L n →L 1→L n-1→L 2→L n-2→…
You must do this in-place without altering the nodes’ values.
For example,
Given{1,2,3,4}, reorder it to{1,4,2,3}.
算法:
1.用快慢指针找到链表中点
2.反转后半段链表
3.前后两端链表拼起来

4.Given a linked list, return the node where the cycle begins. If there is no cycle, returnnull.
Follow up:
Can you solve it without using extra space?
算法:
1.用快慢指针找到环中的点
2.把一个指针指向链表头,一个指向环中相遇的点,以同样的速度推进,相等的点就是环的起始点。

5.A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
算法:
1.遍历链表,在每个节点后插入一个copy节点,copy节点的值和源节点相同。
2.拷贝random指针
3.把源链表和copy链表分开

6.Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.
算法:
1.用快慢指针找到中点,作为根节点
2.左子树为左半个链表递归的结果,右子树为右半个链表递归的结果。
3.递归结束的标志是头节点等于尾后节点

7.Reverse a linked list from position m to n. Do it in-place and in one-pass.
For example:
Given1->2->3->4->5->NULL, m = 2 and n = 4,
return1->4->3->2->5->NULL.
Note:
Given m, n satisfy the following condition:
1 ≤ m ≤ n ≤ length of list.
算法:
1.设立一个头节点,以防第一个节点开始就要反转
2.一个pre指针指向需要反转节点的前一个节点, start指针指向需要反转的第一个节点
3.start指针始终指向这个节点,每循环一次,就把start后面的节点放到最前面,也就是pre->next,然后start指向后面的第二个节点。循环下去,只要遍历一次就能反转指定段的链表。

8.Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.
For example,
Given1->4->3->2->5->2and x = 3,
return1->2->2->4->3->5.
算法:
1.建立两个链表,遍历源链表
2.值小于x的连接到第一个链表后,否则连接到第二个链表后
9.Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
For example,
Given1->2->3->3->4->4->5, return1->2->5.
Given1->1->1->2->3, return2->3.
算法:
1.建立头节点,防止第一个节点就被删除的情况
2.两个指针,一个pre,一个cur
3.用cur遍历链表,如果cur->val!=cur->next->val,两个指针都往前挪一位,否则找到最后一个满足相等条件的cur,cur=cur->next,pre->next=cur.

10.Given a sorted linked list, delete all duplicates such that each element appear only once.
For example,
Given1->1->2, return1->2.
Given1->1->2->3->3, return1->2->3.
算法:
1.建立头节点
2.两个指针,一个pre,一个cur
3.用cur遍历链表,与上题不同的是,遇到相同的val时,cur指向相同的最后一个节点。

11.Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
算法:
建立头节点,遍历两个链表,谁的头小就加入头节点后面

12.Given a list, rotate the list to the right by k places, where k is non-negative.
For example:
Given1->2->3->4->5->NULLand k =2,
return4->5->1->2->3->NULL.
算法:
1.遍历一遍计算链表长度,并找到尾节点,让他的next指向头节点,形成一个环。
2.从尾节点开始向前移动length-k%length,断开链表即可。

13.Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.
You may not alter the values in the nodes, only nodes itself may be changed.
Only constant memory is allowed.
For example,
Given this linked list:1->2->3->4->5
For k = 2, you should return:2->1->4->3->5
For k = 3, you should return:3->2->1->4->5
算法:
1.遍历一遍计算出链表长度length
2.做length/k次翻转链表即可

14.Given a linked list, swap every two adjacent nodes and return its head.
For example,
Given1->2->3->4, you should return the list as2->1->4->3.
Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed.
算法:
1.建立头节点
2.每两个互换一下就行

15.合并k个已排序的链表并将其作为一个已排序的链表返回。分析并描述其复杂度。
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
算法:
1.把k个链表的第一个节点指针加入一个vector中
2.在这个vector中排序(用堆排序比较快),找到最小值,加入链表,并在vector中删除
3.然后重复1,2步,直到vector为空。
时间复杂度为nlogk。

16.Given a linked list, remove the n th node from the end of list and return its head.
For example,
Given linked list:1->2->3->4->5, andn= 2.↵↵ After removing the second node from the end, the linked list becomes1->2->3->5.↵
Note:
Given n will always be valid.
Try to do this in one pass.
算法:
1.用尺子原理,建立头节点,让一个指针p1从head走n步
2.用一个指针p2指向头节点,p1和p2同时往前走,直到p2指向NULL,这是p1指的位置就是要删除元素的前一个元素。
17.You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
算法:
1.用一个数表示进位值
2.当(l1!=NULL||l1!=NULL||进位值!=NULL)时循环继续,每次循环用第一个链表的值加第二个的值加进位值得到加法的结果。
3.加法的结果%10链接到链表,加法的结果/10作为下一位的进位值。

18.从尾到头打印链表
算法:用一个栈将遍历的链表保存,再挨个弹出保存到vector中即可

19.移除重复节点
与第9,10题不同的是,这道题的链表不是有序的及,即重复的节点不是相邻的,因此没法采取双指针的方法来解决。
算法:只需一个指针pre,遍历链表,首次见到的节点值就放到hash表里,第二次见到的就删除。

贪心

1.环形路上有n个加油站,第i个加油站的汽油量是gas[i].
你有一辆车,车的油箱可以无限装汽油。从加油站i走到下一个加油站(i+1)花费的油量是cost[i],你从一个加油站出发,刚开始的时候油箱里面没有汽油。
求从哪个加油站出发可以在环形路上走一圈。返回加油站的下标,如果没有答案的话返回-1。
注意:
答案保证唯一。
算法:
1.设定一站为起始点,下一站为终点,算出从起始点到终点油的净耗量
2.如果这个值大于0,说明可以走到下一站,则下一站往前诺一站,再算总的净耗量
3.如果这个值小于0,走不到下一站,说明这一站净耗量很大,需要将start往后移一站,看能不能补上这个负值
4.一直循环下去,直到起点等于终点,然后判断总的净耗量是否是正的即可。

2.整数转罗马数字
算法:贪心算法,每次把最大的数字兑换出来
1.把所有的数字和字符分别存入vector中(从大到小),不用hash用vector是因为需要遍历
2.遍历存有数字的vector,拿整数和当前数字比较。如果大于当前整数,则把字符串加入结果中,同时整数减去当前数字,循环往复。

栈:

Easy:
1.删除最外层括号
算法:
1.建立一个栈,每有一个左括号就进栈,记录第一个进栈的左括号位置。
2.每有一个右括号就出栈,记录栈为空时右括号的位置。
3.将起始左括号的位置加一到右括号位置减一之间的括号通过append函数链接到结果字符串中
4.继续遍历字符串,循环步骤123直到结束。

2.棒球比赛
算法:
1.遍历输入字符串,遇到数字就直接进栈
2.遇到"D"就把栈顶的元素乘2再压进栈
3.遇到“C”就把栈顶元素弹出
4遇到“+”就把栈顶两个元素相加再压进栈

3.用栈实现队列
算法:
1.用两个栈来实现,元素推入队列就把元素推入s1栈中,推入之前把s1栈中的所有元素都压入s2中。
2.把新元素推入s1之后,再把s2中的元素都压回s1中,这样就能保证新加入的元素在底部。
3.top(),pop()和empty()都直接在s1上操作即可。

4.用队列实现栈
算法:
1.用两个队列来实现,新元素加入新元素加入空的q2中。
2.再把q1中的元素都push到q2中,这样就能保证新加入的元素在最前面,可以最先弹出。
3.把q1和q2交换,这样就不用来回复制了。

5.下一个最大元素
算法:用单调栈的方法来实现
1.建立一个栈,倒着遍历输入的vector,把倒着的第一个元素压到栈里,它对应的下一个最大元素为-1
2.随后遍历的元素,每一个都与栈顶的元素进行比较,如果栈顶点元素小(说明有了这个新的元素之后,前面的元素再也看不到那些小的元素了)应该把所有小于当前元素的栈里的元素都弹出去,剩下的栈顶就是下一个最大元素,并把当前元素压进去

6.二叉树的中序遍历
算法:中序遍历即先输出左子树再输出根,最后输出右子树,递归即可,递归结束条件为根为NULL;

7.二叉树的前序遍历
算法:递归的算法和中序遍历类似
非递归版本用栈来实现,先把根节点压入栈,每个节点出栈时都先把右孩子压入,再把左孩子压入。出栈时把自己的值推入vector中即可。

8.每日温度
算法:下一个最大元素的变形,这次压入栈的是序号。

9.字符串解码
算法:包含有括号匹配的都需要用栈来实现
1.建立两个栈,一个存倍数,一个存当前的字符串,遍历给的字符串
2.遇到‘[’时,把当前的倍数和字符串压栈
3.遇到’]'时,新的当前字符串=字符串栈顶的串+倍数栈顶的数*当前的串,两个栈都出栈一个元素。
4.循环直到字符串遍历完

10.简化路径
算法:我们只把“/”之间的值压入结果中,而不管“/”本身,在输出时再把“/”加上
1.遍历字符串,遇到“/”就跳过,直到不是“/”
2.累加“/”之前的值,直到再遇到“/”,得到一个完整的小块路径
3.路径如果是”.“就什么都不做,如果是“…”就弹出栈顶的值,其他情况把路径压入栈

排序:

1.Sort a linked list using insertion sort.

算法:插入排序:
1.建立头节点指针;
2.一个pre指针,指示插入位置的前方节点,每次初始时指向头节点。
3.一个遍历链表的指针。

2 . 两个数组的交集
算法:
1.用map来存储其中一个数组的值和访问标记
2.遍历另一个数组,在map中找该元素,如果找到,且标记为0,则压入输出数组中,且把标记变为1。
3.如果标记为1则不再输出

3.摆动排序
算法:
1.遍历整个数组,如果当前数字和下一个数字不满足排序规则就交换。
2.可以用奇偶来区分排序规则

4.排序链表(要求用时间复杂度为nlogn,空间复杂度为O(1)的算法)
算法:使用归并排序,因为要求空间复杂度为O(1),所以用归并排序的迭代模式
1.第一遍先两个两个合并,然后再四个四个合并,依此类推,直到合并完。
2.定义两个函数,第一个切割函数,把需要合并的大小切出来,第二个合并函数
3.两层循环即可

5.合并区间
算法:
1.先用sort把vector<vector < int> >按每个vector的首字母排序
2.创建一个vector<vector < int> >res来存放结果,先把原始vector中的首个vector推入。
2.然后遍历,每次拿res尾部vector的尾元素和便利的首元素比较,如果大于等于,则尾元素更新,否则把遍历得到的vector推入res中

6.重构字符串
算法:贪心的思想
1.用一个vector统计每个字符出现的次数,
2.建立一个大根堆,按照每个字符出现次数的降序排列(需要一个pair表示字符和次数,大根堆的排序得自己另外写)
3.每次弹出大根堆的最大的两个元素,依次接到结果字符串中。弹出后次数减一,再推入大根堆中
4.循环2.3步直到输出完所有元素。

7.最大数
算法:
自定义比较函数即可,使s1+s2>s2+s1;

8.最大间距
因为题目要求线性时间复杂度,所以要用箱子排序
算法:
1.有多少个数就有多少个箱子,(最大数-最小数)/(箱子数-1)作为箱子的容量
2.构建箱子的数据结构,包括bool变量表示箱子是否为空,另外还有一个最大值和最小值
2.遍历数组,把数放到箱子里
3.遍历箱子比较箱子间距,间距为当前箱子的最小值减去前一个箱子的最大值,最大的间距即为所求。

数组

1.最大子序和
算法:动态规划
1.两个变量,一个保存已经比较过的最大值ans(初始化为第一个元素),一个保存之前的子序列的和sum,sum初始化为0
2.sum>0时,说明对当前项有正向影响,sum+=当前项
3.sum<0时,说明加上后对当前项有负向影响,sum更新为当前项。
4.每一步都比较sum和ans的值,其中大的赋值给ans

2. 三数之和
算法:
1.对数组排序,然后遍历
2.遍历的每个元素,用两个指针的方法,左指针指向下一个元素,右指针指向尾元素,然后向中间逼近。
3.当前元素加两个指针指向的元素和如果符合要求,则加入最后的结果,并继续向中间毕竟,直到找到所有符合结果的组合。

3 . 盛最多水的容器
算法:双指针
1.一个变量保存最大值,初始化为0,两个指针分别指向头和尾
2.当头小于尾时,开始循环,更新最大值
3.每次指向高度较低的那个指针向里移动

4.最接近的三数之和
算法:和三数之和的算法类似,区别在于更新结果的条件是新的和离目标更近

5.缺失的第一个正数:给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。
这道题要求常数级的空间,因此没法用哈希表,我们采用原地哈希的方式来做,将整个数组作为哈希表。
算法:
1.遍历数组,将<=0的数全部替换为n+1,n为数组的长度
2.遍历数组,将符合abs(nums[i])<=n的数对应的hash的位置标记为负数,即nums[nums[i]-1]=-abs(nums[abs(nums[i])-1]).
3.遍历数组,找到第一个不为负数的位置i,结果就为i+1。如果没有找到,则说明从1-n的所有数正好全部出现,因此返回n+1。
注意:需要改变数组的内容时,在简易for循环for(auto &num:nums)中一定要用引用的形式。

哈希表


1.两数之和
算法:用hash表,时间复杂度尾 Θ ( n ) \Theta(n) Θ(n)
1.构建一个hash表,遍历数组元素,把元素放入表中,值为元素的索引
2.一边放一边检查,target-元素是否在表中,在的话返回元素的值,退出,不在的话把元素加进去。

2 . 宝石与石头
算法:用set,先把宝石都放到set中,再循环石头,找到一个计数就加一。

3.无重复字符的最长子串
算法:移动窗口和哈希表
1.用两个指针i,j来表示移动窗口的前后端,用哈希表来存储已经存在过的字符以及其下标,
2.刚开始两个都指向第一个元素,用第二个指针来遍历整个字符串
3.当第二个指针指向的数不在前面的序列中时,把字符及其下标插入哈希表中,继续移动后指针
4.当后指针的元素在前面的子序列中时,i移向重复的字符的下个位置,继续移动j。
5.移动的过程每步都计算j和i的距离,保存最大的值

4.LRU缓存机制
算法:哈希表加双向链表,把哈希表映射到双向链表中
1.hash表存储key和链表节点的迭代器,节点类型尾pair<int,int >
2.put:当key不存在于hash表中时,如果链表满了,则把链表尾的元素删除,并把hash表中的记录也删除,然后把新节点插入链表头以及hash中
3.get:不在hash中,直接返回-1,否则,把对应的节点移到链表头,更新hash的记录,并返回节点的第二个值。

字符串:

1.Z 字形变换
算法:
1.变量字符串,用一个变量来指示当前行,这个变量按Z字行走就行
2.当当前行为第一行或最后一行时,改变走的方向。
3.每遍历一个字符,就把这个字符加到当前这一行的字符串后面

2.罗马数字转整数
算法:把所有情况都放入hash表中,遍历字符串,先判断两个字符的是否在表中,在的输出对应值,索引向前前进两个字符,否则只把当前字符转为整数输出。

3.报数
算法:两层大循环,外层是层数,内层是每次的转换。转换算法为:
1.遍历字符串,一个count变量统计一个字符出现的次数,初始化为1,相同时count+1
2.不同时把count转为string加入结果字符串,把当前字符也加入。然后count归1,继续循环。

4.验证回文串
算法:调用翻转字符串API,翻转后判断两字符是否相同
注意:判断是否为十进制数用isdigit(),判断是否为字母用isalpha(),判断是否为数字或字母用isalnum()。大写字母转小写用tolower(),反之用touppper()。

5.替换空格
算法:
1.遍历一遍确定空格个数,从而确定新字符串的长度new_length
2.用resize(new_length)函数重新规定字符串的长度
3.双指针,一个指向老字符串的尾部,一个指向新的尾部,倒着遍历,把老字符串赋给新的,遇到空格则替换。
注意:resize(n)可以重新规定容器的大小,n大于字符串当前长度时,多余的位置执行默认初始化,如string则初始化为空字符。

6.模式匹配(面试题 16.18.)
算法:这是一道比较偏数学上trick的题,主要需要写出一个等式:cala+cblb=lv,ca,cb为模式中a,b的个数,la,lb为a,b对应字符串的长度,lv为字符串总长度。ca,cb,lv都是已知,我们遍历la[0,lv/ca],对于每一个确定的la,和lb(一定要是整数),再遍历一遍pattern,看是否每一个pattern中的字母都正确的对应字符串即可。
注意:当需要把一个对象的值赋给另一个,而当前对象之后没啥用的时候,最好用a=move(b),减少资源的复制。

二叉树

1.重建二叉树:给出前序和中序遍历,重建二叉树
算法:递归的思想
1.前序遍历的第一个节点为根节点,找到根节点在中序遍历中的位置,将中序遍历分为左右子树两部分
2.根据左右子树的数量,将前序遍历分为左右子树两部分。
3.根据得到的左右子树的前序和中序遍历,递归求解
注意:可以采用map来保存中序遍历的结果,方便查找。

2.二叉树中的最大路径和
算法:迭代
1.确定一个节点的最大贡献值为:该节点的值加上左右子节点的最大贡献值中较大的那个,注意左右子节点的最大贡献值一定要大于0。由此开始递归
2.在递归的同时维护一个参数,表示最大路径和,具体为当前节点的值加上左右子节点的非负最大贡献值。
注意:迭代是计算最大贡献值的过程,只要左右子节点的其中一个即可,而最大路径和是迭代过程顺带计算的,需要把左右子节点非负贡献都加起来。

广度优先算法:

1.二叉树的层次遍历 II
算法:层次遍历用队列,while大循环里嵌套一个小的for循环,for循环的次数为队列的size.

2.网络延迟时间
算法:Dijkstras算法
1.先构建邻接矩阵,再把K邻接的那一行保存到一个vector中,找到里面的最小值
2.更新vector的值,每个值更新为min(原本的值,前面找到的最小值+最小值到原本的节点的距离)
3.每更一次vector,就找里面最小的值保存另一个数组中。
4.最后退出后,从保存的到每个点的最小值中再找个最小的。

3 . 01 矩阵
算法:寻找最短距离就用BFS,这道题有个技巧就是从每个0元素开始往外用BFS。

4.对称二叉树
算法:递归或迭代。
递归判断左右子树是否对称。
迭代:BFS的思想只是每次都弹出前两个数比较是否相等,并按相反的顺序将其左右孩子压入队列

深度优先算法

1.二叉树的最大深度
算法:递归或迭代。
递归返回左右子树的最大深度,加一等于结果
迭代:最大深度用DFS,用栈来实现,栈保存指针和深度,每次更新最大深度。

2.将有序数组转换为二叉搜索树
算法:找到中点,递归即可。

动态规划

1.买卖股票的最佳时机
算法:维持两个变量,一个表示目前为止最低的价格,一个表示最高的利润。

当前价格小于最低价格时,最低价格更新为当前值,大于最低价格时,计算差值,如果大于最高利润则最高利润更新

2.爬楼梯
算法:最终的结果可以由两种情况构成,从n-1跳到n,或从n-2跳到n。
可以递归实现,f(n)=f(n-1)+f(n-2);但容易超时
也可以用循环来实现(建立一个数组,或斐波那契数列)。

3.最长回文子串
算法:
1.遍历整个字符串,对于每个字符,从中心扩展
2.扩展时注意每个字符都有两周扩展方式,一种对应aba这样的子串,一种对应abba
3.维持一个start指针和end指针,每次当子串长度大于最大子串时,按照长度更新这两个指针
4.遍历结束后输出s.substr(start,end-start+1)的子串。

4.戳气球
算法:dp[i][j]表示[i+1,j-1]所有戳破气球得到的最高分,k为其中最后一个被戳破的气球,则
dp[i][j]=max(dp[i][j],nums[i]*nums[k]*nums[j],dp[i][k],dp[k][j]);
最优的k包含在所有的k中,所以遍历k,不断更新dp的[i][j]的值

建立好数组后,先把长度为3的子序列遍历一次,依次增加到整个数组。

5.正则表达式匹配
算法:动态规划时分为两种情况
1.p的尾部位置j是*:则判断p尾部的字符是否和字符串s的尾部字符一致,不一致则dp[i][j]=dp[i][j-2];一致则dp[i][j]=dp[i][j-2] | dp[i-1][j]。
2.p的尾部不是*:判断p和s的尾部是否match,match则dp[i][j]=dp[i-1][j-1],否则dp[i][j]=false。

6.单词拆分
算法:
思路:判断dp[i]是否可拆解,可以根据状态转移方程,转移为判断,dp[j]是否可以拆解,同时判断剩余的字符串s.substr(j,i-j)是否在字典里。
注意:可以先把字典里的字符串都放到哈希表unordered_set里,这样找起来会快些。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值