文章目录
- [7. 整数反转](https://leetcode-cn.com/problems/reverse-integer/)
- [148. 排序链表](https://leetcode-cn.com/problems/sort-list/)
- [78. 子集](https://leetcode-cn.com/problems/subsets/)
- [46. 全排列](https://leetcode-cn.com/problems/permutations/)
- [94. 二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)
- [70. 爬楼梯](https://leetcode-cn.com/problems/climbing-stairs/)
- [剑指 Offer 59 - I. 滑动窗口的最大值](https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/)
- [面试题 02.08. 环路检测](https://leetcode-cn.com/problems/linked-list-cycle-lcci/)
7. 整数反转
难度简单2120
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
溢出与否
不管是整数还是负数,都可以通过取每一位依次乘以10相加,如果每一次相加后的结果还原回去不相等的话,说明溢出了,直接返回0,否则返回最终结果。
class Solution {
public int reverse(int x) {
int res = 0;
while( x != 0){
int tmp = x % 10;
int ans = res * 10 +tmp;
if((ans -tmp)/10 != res)
//溢出了
return 0;
res = ans;
x /= 10;
}
return res;
}
}
148. 排序链表
难度中等700
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
使用优先队列
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
Queue<ListNode> queue = new PriorityQueue<>((o1,o2)->(o1.val -o2.val));
while(head != null){
queue.offer(head);
head = head.next;
}
ListNode node = new ListNode(0);
ListNode cur = node;
while(!queue.isEmpty()){
cur.next = queue.poll();
cur = cur.next;
}
cur.next = null;
return node.next;
}
}
归并排序解决
class Solution {
public ListNode sortList(ListNode head) {
//非递归归并排序
int len = 0;
for(ListNode x = head;x!=null;x=x.next){
len++;
}
ListNode vNode = new ListNode(0);
vNode.next = head;//增加虚拟节点
for(int i= 1; i < len ;i=i*2){
ListNode vir = vNode;//使用虚拟节点
for(int j = 0; j + i < len; j = j + 2 * i){//这里如果剩余元素小于i说明不用排序了
ListNode first = vir.next,second = first;//归并排序得两个起点
for(int k=0;k<i;k++)
second = second.next;
int f=0,s=0;
//两个归并序列移动得次数
while(f<i && s<i && second != null){
if(first.val < second.val){
vir.next = first;
vir = vir.next;
first = first.next;
f++;
}else{
vir.next = second;
vir = vir.next;
second = second.next;
s++;
}
}
//未排完
while(f < i){
vir.next = first;
vir = vir.next;
first = first.next;
f++;
}
while(s < i && second!=null){
vir.next = second;
vir = vir.next;
second = second.next;
s++;
}
//新的虚拟节点开始
vir.next = second;//注意此处为当前是虚拟节点,由于second已经到达后面得起始点
}
}
return vNode.next;
}
}
78. 子集
难度中等716
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
**说明:**解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
class Solution {
public List<List<Integer>> subsets(int[] nums) {
//使用二进制模拟需要和不需要取得数字
List<List<Integer>> list = new ArrayList<>();
for(int i=0;i<(1 << nums.length);i++){
//一共模拟000...000 - 111...111得情况,符合全排列得关系
List<Integer> sub = new ArrayList<>();
//根据此时每位得结果
for(int j=0;j<nums.length;j++){
//第j位判断是否需要加入
if(((i >> j) & 1) == 1)
sub.add(nums[j]);//第j位为1,说明需要添加
}
list.add(sub);
}
return list;
}
}
46. 全排列
难度中等852
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
使用回溯
添加一个数组判断是否使用过,记录已有长度以及总长度,相等添加(需要转化为新的列表添加),
没有得话添加没添加过得,递归添加,添加后重置当前位置得状态(状态以及路径)。
class Solution {
public List<List<Integer>> permute(int[] nums) {
//回溯法解决
List<List<Integer>> res = new ArrayList<>();
int len = nums.length;//长度
if(len == 0) return res;
boolean[] isUsed = new boolean[len];//标记数字是否使用过
List<Integer> path = new ArrayList<>();//走过的路径
dfs(nums,len,0,path,isUsed,res);//数组、总长、当前路径长度、路径、标记、结果
return res;
}
public void dfs(int[] nums,int len,int depth,List<Integer> path,boolean[] isUsed, List<List<Integer>> res){
if(depth == len){
res.add(new ArrayList<>(path)); //添加后重新new一个 不能改变path
return;
}//已经遍历完毕
for(int i=0;i<len;i++){
if(!isUsed[i]){
//当前数字未被使用
path.add(nums[i]);
isUsed[i] = true;
dfs(nums,len,depth+1,path,isUsed,res);
//搜索后取消此状态
isUsed[i] = false;
path.remove(path.size()-1);
}
}
}
}
94. 二叉树的中序遍历
难度中等637
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
栈辅助处理
需要先操作左边节点,然后中间,最后右边,因此每次先把所有左节点放入,然后每次取栈顶后再访问右边。
直到栈空且右边也无节点了。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
//中序:左中右
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
//有左子树就放进去,没有得话就弹出
TreeNode cur = root;//用于标记当前打印的节点
while(cur!=null || !stack.isEmpty()){//说明还有数据未操作
while(cur!=null){//左子树未遍历完成
//先打印左边得节点
stack.push(cur);
cur = cur.left;
}
//先打印栈顶也就是本身,然后打印右子树
cur = stack.pop();
res.add(cur.val);
cur = cur.right;
}
return res;
}
}
70. 爬楼梯
难度简单1197
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
**注意:**给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
class Solution {
public int climbStairs(int n) {
//0 1 2 3 4
// 1 2 3 5
if(n == 0) return 1;
if(n <= 3) return n;
int dp0 = 2;
int dp2 = 3;
for(int i=4;i<=n;i++){
int tmp = dp0+dp2;
dp0 = dp2;
dp2 =tmp;
}
return dp2;
}
}
剑指 Offer 59 - I. 滑动窗口的最大值
难度简单89
给定一个数组 nums
和滑动窗口的大小 k
,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
暴力解决
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0) return new int[0];
int[] ans = new int[nums.length - k +1];
for(int i=-1;i<nums.length-k;i++){
int v = nums[i+1];//左边
for(int j=2;j<=k;j++) v = Math.max(v,nums[i+j]);
ans[i+1] = v;
}
return ans;
}
}
存储最大索引
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0) return new int[0];
int maxIndex = -1;
int[] ans = new int[nums.length - k +1];
for(int i=-1;i<nums.length-k;i++){
if(maxIndex >= i+1)
maxIndex = nums[i+k] > nums[maxIndex] ? i+k : maxIndex;
else{
maxIndex = i+1;
for(int j=2;j<=k;j++){
maxIndex = nums[i+j] > nums[maxIndex] ? i+j : maxIndex;
}
}
ans[i+1] = nums[maxIndex];
}
return ans;
}
}
面试题 02.08. 环路检测
难度中等28
给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。
有环链表的定义:在链表中某个节点的next元素指向在它前面出现过的节点,则表明该链表存在环路。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。
进阶:
你是否可以不用额外空间解决此题?
快慢指针
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null) return null;
ListNode low = head;
ListNode quick = head;//一次走两格
while(low != null && quick!=null){
low = low.next;
if(quick.next ==null){
quick = quick.next;//需要判断是否可以走两格
break;
}
quick = quick.next.next;
if(quick == low) break;
}
if(quick == null || low == null) return null;//不存在环
low = head;//从头追赶
while(low != quick){
low = low.next;
quick = quick.next;
}
return low;
}
}
//一次走两格
while(low != null && quick!=null){
low = low.next;
if(quick.next ==null){
quick = quick.next;//需要判断是否可以走两格
break;
}
quick = quick.next.next;
if(quick == low) break;
}
if(quick == null || low == null) return null;//不存在环
low = head;//从头追赶
while(low != quick){
low = low.next;
quick = quick.next;
}
return low;
}
}