本文只是记录一下刷过的题目,记录当下刷题的思考等,方便后续回顾。
高频重点
LRU 算法
思路:LRU算法,最近最少使用,访问后,该元素,会放到最前面。
class LRUCache {
class DlinkNode{
int key;
int val;
DlinkNode prev;
DlinkNode next;
public DlinkNode(){};
public DlinkNode(int key,int val){
this.key = key;
this.val = val;
}
}
DlinkNode tail;
DlinkNode head;
int size = 0;
int capacity=0;
Map<Integer,DlinkNode> cache = new HashMap();
public LRUCache(int capacity) {
tail = new DlinkNode();
head= new DlinkNode();
this.capacity=capacity;
this.size=0;
tail.prev = head;
head.next = tail;
}
public int get(int key) {
DlinkNode node = cache.get(key);
if(node==null){
return -1;
}else{
deleteNode(node);
addNodeToHead(node);
return node.val;
}
}
public void put(int key, int value) {
DlinkNode node = cache.get(key);
if(node!=null){
node.val= value;
deleteNode(node);
addNodeToHead(node);
}else{
if(size==capacity){
DlinkNode removeNode = tail.prev;
deleteNode(removeNode);
cache.remove(removeNode.key);
size--;
}
DlinkNode newNode = new DlinkNode(key,value);
addNodeToHead(newNode);
size++;
cache.put(key,newNode);
}
}
void deleteNode(DlinkNode node){
node.prev.next = node.next;
node.next.prev = node.prev;
}
void addNodeToHead(DlinkNode node){
node.prev = head;
node.next = head.next;
head.next.prev =node;
head.next = node;
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
215. 数组中的第K个最大元素
class Solution {
public int findKthLargest(int[] nums, int k) {
return partion(nums,0,nums.length-1,nums.length-k);
}
int partion(int[]nums,int l,int r,int k){
if(l==r) return nums[l];
int pivot = nums[l];int i=l-1;int j=r+1;
while(i<j){
do i++; while(nums[i]<pivot);
do j--;while(nums[j]>pivot);
if(i<j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
if(k<=j){
return partion(nums,l,j,k);
}else{
return partion(nums,j+1,r,k);
}
}
}
3. 无重复字符的最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.length()==1){
return 1;
}
Map<Character,Integer> map = new HashMap();
int max=0;
int left=0;
for(int i =0;i<s.length();i++){
if(map.containsKey(s.charAt(i))){
left= Math.max(map.get(s.charAt(i))+1,left);
System.out.println(left);
}
map.put(s.charAt(i),i);
max=Math.max(max,i-left+1);
}
return max;
}
}
class Solution {
public int lengthOfLongestSubstring(String s) {
//滑动窗口
char[] ss = s.toCharArray();
Set<Character> set = new HashSet<>();//去重
int res = 0;//结果
for(int left = 0, right = 0; right < s.length(); right++) {//每一轮右端点都扩一个。
char ch = ss[right];//right指向的元素,也是当前要考虑的元素
while(set.contains(ch)) {//set中有ch,则缩短左边界,同时从set集合出元素
set.remove(ss[left]);
left++;
}
set.add(ss[right]);//别忘。将当前元素加入。
res = Math.max(res, right - left + 1);//计算当前不重复子串的长度。
}
return res;
}
}
leetocde 15三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组
思路:联想到二数之和,我们可以先固定一个数,然后剩下的列表使用二数之和的写法去解决。但是这个题目有一个特殊的地方是存在重复值,比如类似[-2,0,0,2,2]这样的数据,我们固定-2。然后求解二数之和,如果我们找到一个满足的,那么我们需要跳过这种重复的值。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res= new ArrayList();
for(int i =0;i< nums.length;i++){
if(nums[i]>0) continue;
if(i>0&&nums[i]==nums[i-1]) continue; //为什么要有这个,为了避免重复的数值,比如-2,-2我们只需要寻找一次就习惯,不应该重复寻找。
int taerget = -nums[i];
int left = i+1;
int right = nums.length-1;
while(left<right){
if(nums[left]+nums[right] == taerget){
res.add(Arrays.asList(nums[left],nums[i],nums[right]));
int templeft= nums[left];
int tempRight = nums[right];
System.out.println(left+"_a");
while(left<right&&templeft==nums[++left]);
while(right>=left && tempRight==nums[--right]);
}else if(nums[left]+nums[right]<taerget){
left++;
}else{
right--;
}
}
}
return res;
}
}
树的题目
leetcode 236 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3 。
示例 2:
- 树的问题优先考虑递归。
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root ==null|| root==p||root==q) return root;
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
if(left==null){
return right;
}
if(right==null){
return left;
}
return root;
}
230二叉搜索树中第 K 小的元素
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k ****小的元素(从 1 开始计数)。
二叉搜索树的中序遍历是有序的,也就是意味着我们只需要中序遍历一下,就可以找到k小的元素,我们用一个变量count代表当前中序遍历的返回值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private int count;
private int ans;
public int kthSmallest(TreeNode root, int k) {
count = 0;
inorder(root,k);
return ans;
}
private void inorder(TreeNode root, int k){
if(root == null) return;
inorder(root.left,k);
count++;
if(count == k) ans = root.val;
else inorder(root.right,k);
return;
}
}
226. 翻转二叉树
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点
示例 1:
- 思考:是树的题目,树的题目,无非就是三种遍历方式,我们考虑使用哪一种方式去做这道题,这个题目没有特殊的要求,前序,后序都可以。比如我们后序遍历的时候,拿到一个节点的左,右,我们交换一下就可以。后序遍历会自动帮我们完成所有重复的过程。
输入: root = [4,2,7,1,3,6,9]
输出: [4,7,2,9,6,3,1]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
//inorder
public TreeNode invertTree(TreeNode root) {
if(root==null) return null;
TreeNode temp = root.right;
root.right = root.left;
root.left =temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
//后序遍历
public TreeNode invertTree(TreeNode root) {
if(root==null) return root;
invertTree(root.left);
invertTree(root.right);
TreeNode temp =root.left;
root.left= root.right;
root.right=temp;
return root;
}
}
110. 平衡二叉树
给定一个二叉树,判断它是否是
平衡二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if(root==null) return true;
int left = getTreeDepth(root.left);
int right = getTreeDepth(root.right);
boolean res= Math.abs(left-right)<=1?true :false;
return res&& isBalanced(root.left)&& isBalanced(root.right);
}
int getTreeDepth(TreeNode root){
if(root==null) return 0;
int leftDepth = getTreeDepth(root.left);
int rightTreeDepth = getTreeDepth(root.right);
return Math.max(leftDepth,rightTreeDepth)+1;
}
}
105. 从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
- 思路:没有重复元素,用一个map存储元素的值对应的位置,前序遍历和中序遍历的特点,来重新buid tree,前序遍历找到根节点,然后中序遍历中找到对应的位置,然后我们就知道左子树,和右字树,前序遍历我们根据根节点,和左子树的个数,就可以递归走下去了。
class Solution {
private Map<Integer, Integer> indexMap;
public TreeNode myBuildTree(int[] preorder, int[] inorder, int preLeft, int preRight, int inLeft, int inRight) {
if (preLeft >preRight ) {
return null;
}
int inRoot = indexMap.get(preorder[preLeft]);
TreeNode root = new TreeNode(preorder[preLeft]);
int leftTreeSize = inRoot - inLeft;
root.left= myBuildTree(preorder,inorder,preLeft+1,preLeft+leftTreeSize,inLeft,inRoot-1);
root.right= myBuildTree(preorder,inorder,preLeft+1+leftTreeSize,preRight,inRoot+1,inRight);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
// 构造哈希映射,帮助我们快速定位根节点
indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < n; i++) {
indexMap.put(inorder[i], i);
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
}
543. 二叉树的直径
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。
两节点之间路径的 长度 由它们之间边数表示。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxd=0;
public int diameterOfBinaryTree(TreeNode root) {
depth(root);
return maxd;
}
public int depth(TreeNode node){
if(node==null){
return 0;
}
int Left = depth(node.left);
int Right = depth(node.right);
maxd=Math.max(Left+Right,maxd);//将每个节点最大直径(左子树深度+右子树深度)当前最大值比较并取大者
return Math.max(Left,Right)+1;//返回节点深度
}
}
124. 二叉树中的最大路径和
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int max=Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
dfs(root);
return max;
}
int dfs(TreeNode root){
if(root==null) return 0;
int left = dfs(root.left);
int right = dfs(root.right);
max = Math.max(max,left+root.val);
max = Math.max(max,right+root.val);
max = Math.max(max,left+right+root.val);
max = Math.max(max,root.val);
// return left+right+root.val;
return Math.max(Math.max(left,right)+root.val,root.val);
}
}
662. 二叉树最大宽度
//
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int max = 0;
Map<Integer,Integer> map =new HashMap();
public int widthOfBinaryTree(TreeNode root) {
if(root==null) return 0;
dfs(root,1,1);
return max;
}
void dfs(TreeNode root,int level,int index){
if(root ==null) return ;
if(!map.containsKey(level)){
map.put(level,index);
}
max = Math.max(max,index- map.get(level)+1);
dfs(root.left,level+1,2*index);
dfs(root.right,level+1,2*index+1);
}
}
94. 二叉树的中序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res =new ArrayList();
Stack<TreeNode> stack = new Stack();
while(!stack.isEmpty()||root!=null){
if(root!=null){
stack.push(root);
root=root.left;
}else{
TreeNode node = stack.pop();
res.add(node.val);
root = node.right;
}
}
return res;
}
}
145. 二叉树的后序遍历
/**
* 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> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode prev = null;
TreeNode cur = root;
while(cur!=null||!stack.isEmpty()){
while(cur!=null){
stack.push(cur);
cur=cur.left;
}
cur= stack.peek();
if(cur.right==null||cur.right==prev){
res.add(cur.val);
stack.pop();
prev =cur;
cur = null;
}else{
cur = cur.right;
}
}
return res;
}
}
双指针
11. 盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明: 你不能倾斜容器。
class Solution {
public int maxArea(int[] height) {
int maxArea = 0;
int left=0;
int right = height.length-1;
while(left<right){
maxArea = Math.max(maxArea,Math.min(height[left],height[right])*(right-left));
if(height[left]<height[right]){
left++;
}else{
right--;
}
}
return maxArea;
}
}
76. 最小覆盖子串
class Solution {
public String minWindow(String s, String t) {
Map<Character,Integer> need = new HashMap();
Map<Character,Integer> window = new HashMap();
for(char s1:t.toCharArray()){
need.put(s1,need.getOrDefault(s1,0)+1);
}
int valid = 0;
int left = 0;
int right = 0;
int len = Integer.MAX_VALUE;
int leftIndex = 0;
while(right<s.length()){
char s1= s.charAt(right++);
window.put(s1,window.getOrDefault(s1,0)+1);
if(need.containsKey(s1)){
if(window.get(s1).equals(need.get(s1))){
valid++;
}
}
while(valid==need.size()){
if(right-left<=len){
len = right-left;
leftIndex = left;
}
char d= s.charAt(left++);
if(need.containsKey(d)){
if(window.get(d).equals(need.get(d))){
valid--;
}
window.put(d,window.get(d)-1);
}
}
}
return len ==Integer.MAX_VALUE?"":s.substring(leftIndex,leftIndex+len);
}
}
209. 长度最小的子数组
数学
50. Pow(x, n)
实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。
class Solution {
// 负数我们如何求解,转为正数来。正数如何求解,我们递归,每一次每一次缩小一半的,4=f(2)*f(2) 我们小于判读一下 如果是5 就是f(2)*f(2)*x;
public double myPow(double x, int n) {
if(n==0) return 1.0;
if(n<0){
return 1/fn(x,-n);
}else{
return fn(x,n);
}
}
public double fn(double x,int n){
if(n==0) return 1.0;
double half = fn(x,n/2);
if(n%2!=0){
return half*half*x;
}else{
return half*half;
}
}
}
链表题目
19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点。
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fast =head;
ListNode dummy = new ListNode(0);
dummy.next= head;
ListNode slow = dummy;
for(int i =0;i<n;i++){
fast = fast.next;
}
while(fast!=null){
fast=fast.next;
slow= slow.next;
}
slow.next= slow.next.next;
return dummy.next;
}
23. 合并 K 个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists.length == 0 ) return null;
return mergeListNodes(lists,0,lists.length-1);
}
public ListNode mergeListNodes(ListNode[]lists,int left,int right){
if(left==right){
return lists[left];
}
int mid = left+(right-left)/2;
return mergeTwo(mergeListNodes(lists,left,mid),mergeListNodes(lists,mid+1,right));
}
public ListNode mergeTwo(ListNode l1,ListNode l2){
if(l1==null){
return l2;
}else if(l2==null){
return l1;
}else if(l1.val<l2.val){
l1.next= mergeTwo(l1.next,l2);
return l1;
}else{
l2.next= mergeTwo(l2.next,l1);
return l2;
}
}
}
//时间复杂度:
- 总的来说,上述代码的时间复杂度是 ,其中 `k` 是链表的数量,`n` 是所有链表的平均节点数。O(nklogk)
24. 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
- 思路:链表题目。就是模拟写出来。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode prev =dummy;
while(prev.next!=null&&prev.next.next!=null){
ListNode first = prev.next;
ListNode second =prev.next.next;
prev.next = second;
first.next = second.next;
second.next = first;
prev= first;
}
return dummy.next;
}
}
113. 路径总和 II
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List<List<Integer>> res =new ArrayList();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
dfs(root,new ArrayList(),targetSum);
return res;
}
void dfs(TreeNode root,List<Integer> temp,int targetSum ){
if(root==null) return;
temp.add(root.val);
if(root.left==null&&root.right==null&&targetSum==root.val){
res.add(new ArrayList(temp));
temp.remove(temp.size()-1);
return;
}
dfs(root.left,temp,targetSum-root.val);
dfs(root.right,temp,targetSum-root.val);
temp.remove(temp.size()-1);
}
}
. 路径总和 III
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int pathSum(TreeNode root, int targetSum) {
Map<Long,Integer> map =new HashMap();
map.put(0L,1);
return dfs(root,targetSum,0,map);
}
int dfs(TreeNode root,int targetSum,long current,Map<Long,Integer> map){
if(root ==null) return 0;
current+=root.val;
int res=0;
res+= map.getOrDefault(current-targetSum,0);
map.put(current,map.getOrDefault(current,0)+1);
res+=dfs(root.left,targetSum,current,map);
res+=dfs(root.right,targetSum,current,map);
map.put(current,map.get(current)-1);
return res;
}
}
25. K 个一组翻转链表
给你链表的头节点 head ,每 k **个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k **的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换
public ListNode reverseKGroup(ListNode head, int k) {
int len = 0;
ListNode cur =head;
ListNode dummy = new ListNode(0);
dummy.next = head;
while(cur!=null){
cur = cur.next;
len++;
}
ListNode prev = dummy;
cur = dummy.next;
ListNode next = null;
for(int i =0;i<len/k;i++){
for(int j =0;j<k-1;j++){
next = cur.next;
cur.next = next.next;
next.next = prev.next;
prev.next = next;
}
prev = cur;
cur = prev.next;
}
return dummy.next;
}
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode currentNode = head;
int len = 0;
// 计算链表长度
while (currentNode != null) {
len++;
currentNode = currentNode.next;
}
// 剩余节点不足k个 直接返回头节点
if (len < k) {
return head;
}
ListNode pre = head;
ListNode cur = head.next;
for (int i = 0;i < k - 1;i++) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
head.next = reverseKGroup(cur, k);
return pre;
}
}
206. 反转链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode newHead = null;
ListNode cur = head;
while(cur!=null){
ListNode next = cur.next;
cur.next=newHead;
newHead=cur;
cur=next;
}
return newHead;
}
}
92. 反转链表 II
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(head==null) return null;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre=dummy;
for(int i=1;i<left;i++){
pre = pre.next;
}
int count = right -left;
ListNode cur = pre.next;
for(int i =0;i<count;i++){
ListNode next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next= next;
}
return dummy.next;
}
}
143. 重排链表(top)
给定一个单链表 L **的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换
class Solution {
public void reorderList(ListNode head) {
if (head == null) {
return;
}
ListNode mid = middleNode(head);
ListNode l1 = head;
ListNode l2 = mid.next;
mid.next = null;
l2 = reverseList(l2);
mergeList(l1, l2);
}
public ListNode middleNode(ListNode head) {
ListNode fast =head;
ListNode slow = head;
while(fast.next!=null&&fast.next.next!=null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
public void mergeList(ListNode l1, ListNode l2) {
ListNode l1Node;
ListNode l2Node;
while(l1!=null&&l2!=null){
l1Node= l1.next;
l2Node = l2.next;
l1.next = l2;
l1 = l1Node;
l2.next = l1;
l2 = l2Node;
}
}
}
61. 旋转链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if (head == null || head.next == null) return head;
ListNode cur =head;
int len =1;
while(cur.next!=null){
cur= cur.next;
len++;
}
cur.next = head;
ListNode cur1= head;
for(int i=0;i<len-k%len-1;i++){
cur1 = cur1.next;
}
ListNode newNode = cur1.next;
cur1.next = null;
return newNode;
}
}
109. 有序链表转换二叉搜索树
给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为 平衡 二叉搜索树。
示例 1:
输入: head = [-10,-3,0,5,9]
输出: [0,-3,9,-10,null,5]
解释: 一个可能的答案是[0,-3,9,-10,null,5],它表示所示的高度平衡的二叉搜索树。
示例 2:
输入: head = []
输出: []
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode sortedListToBST(ListNode head) {
if(head==null) return null;
ListNode cur = head;
int len= 0 ;
while(cur!=null){
cur= cur.next;
len++;
}
return build(head,0,len-1);
}
TreeNode build(ListNode head,int l,int r){
if(l>r) return null;
ListNode cur = head;
int mid = l+(r-l)/2;
int size = mid-l;
while(size>0){
cur = cur.next;
size--;
}
TreeNode root = new TreeNode(cur.val);
root.left = build(head,l,mid-1);
root.right = build(cur.next,mid+1,r);
return root;
}
}
## [113. 路径总和 II](https://leetcode.cn/problems/path-sum-ii/)
数学
41. 缺失的第一个正数
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组
- 思路:空间复杂度为 O(1)。那么就只能原地置换元素。题目是求最小的正整数。数组,我们可以交换,存在的正整数交换到它正确的位置。这样我们去遍历,第一个不符合 i+1 的元素,就是缺少的值。
比如我们有 3,4,-1,1 交换后,应该是 1,-1,3,4 1,3,4 都在其对应的位置,这样自然缺的就是 2
class Solution {
public int firstMissingPositive(int[] nums) {
for(int i =0;i<nums.length;i++){
while( nums[i]>0&&nums[i]<=nums.length&& nums[i]!=i+1&& nums[nums[i]-1]!=nums[i]){
int temp = nums[nums[i]-1];
nums[nums[i]-1]=nums[i];
nums[i]=temp;
}
}
for(int i=0;i<nums.length;i++){
if(nums[i]!=i+1){
return i+1;
}
}
return nums.length+1;
}
}
42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
class Solution {
public int trap(int[] height) {
int n = height.length;
int lmax[] = new int[height.length];
int rmax[]=new int[height.length];
lmax[0]=height[0];
rmax[n-1]=height[n-1];
for(int i=1;i<n;i++){
lmax[i] = Math.max(lmax[i-1],height[i]);
}
for(int i =n-2;i>=0;i--){
rmax[i] = Math.max(rmax[i+1],height[i]);
}
int res = 0;
for(int i=1;i<n-1;i++){
res+=Math.min(lmax[i],rmax[i])-height[i];
}
return res;
}
}
堆的题目
347 前K个高频元素
*给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//返回频率前K高,第一直觉就是堆,我们维护一个小根堆,前k高的元素放到对头,每次去取就可以了
//
PriorityQueue<int[]> queue = new PriorityQueue<int[]>( (int[]a,int[]b)->{
return a[1]-b[1];
});
Map<Integer,Integer> map =new HashMap();
for(int num:nums){
map.put(num,map.getOrDefault(num,0)+1);
}
int[]res = new int [k];
map.forEach((k1,v1)->{
int key = k1;
int count= v1;
if(queue.size()< k){
queue.offer(new int[]{key,count});
}else{
if(queue.peek()[1]<v1){
queue.poll();
queue.offer(new int[]{key,count});
}
}
});
for(int i=0;i<res.length;i++){
res[i] = queue.poll()[0];
}
return res;
}
}
二分题目
33. 搜索旋转排序数组 整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
class Solution {
public int search(int[] nums, int target) {
int i=0;
if(nums.length==0) return -1;
int j=nums.length-1;
while(i<=j){
int mid = i+(j-i)/2;
if(nums[mid]==target){
return mid;
}
if(nums[i]<=nums[mid]){
if(target>=nums[i]&&target<=nums[mid]){
j=mid-1;
}else{
i=mid+1;
}
}else{
if(target>nums[mid]&&target<=nums[j]){
i=mid+1;
}else{
j=mid-1;
}
}
}
return -1;
}
}
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[2];
if(nums.length<1){
return res;
}
int l=0;
int r=nums.length-1;
while (l<r){
int mid =(l+r)/2;
if(nums[mid]<target) l = mid+1;
else r=mid;
}
if(nums[r]==target){
res[0]=r;
} else{
res[0]=-1;
}
l=0;
r=nums.length-1;
while (l<r){
int mid =(l+r+1)/2;
if(nums[mid]>target) r= mid-1;
else l=mid;
}
if(nums[l]==target){
res[1] = l;
}else{
res[1] = -1;
}
return res;
}
}
Stack
32. 最长有效括号
给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号
子串的长度。
class Solution {
public int longestValidParentheses(String s) {
int max = 0;
Deque<Integer> stack =new LinkedList();
stack.push(-1);
for(int i =0;i<s.length();i++){
if(s.charAt(i)=='('){
stack.push(i);
}else{
stack.pop();
if(stack.isEmpty()){
stack.push(i);
}else{
max= Math.max(max,i-stack.peek());
}
}
}
return max;
}
}
71. 简化路径
给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 ****(以 '/' 开头),请你将其转化为 更加简洁的规范路径。
在 Unix 风格的文件系统中规则如下:
- 一个点
'.'表示当前目录本身。 - 此外,两个点
'..'表示将目录切换到上一级(指向父目录)。 - 任意多个连续的斜杠(即,
'//'或'///')都被视为单个斜杠'/'。 - 任何其他格式的点(例如,
'...'或'....')均被视为有效的文件/目录名称。
返回的 简化路径 必须遵循下述格式:
- 始终以斜杠
'/'开头。 - 两个目录名之间必须只有一个斜杠
'/'。 - 最后一个目录名(如果存在)不能 ****以
'/'结尾。 - 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含
'.'或'..')。
返回简化后得到的 规范路径 。
class Solution {
public String simplifyPath(String path) {
Stack<String> stack = new Stack();
String[]paths = path.split("/+");
for(String temp:paths){
if(temp.equals("..")){
if(!stack.isEmpty()){
stack.pop();
}
}
else{
if(!temp.equals(".")&&!temp.equals("")){
stack.push(temp);
}
}
}
String res= "";
while(!stack.isEmpty()){
res = "/"+stack.pop()+res;
}
if(res.length()==0){
return "/";
}
return res;
}
}
动态规划
53. 最大子数组和
class Solution {
public int maxSubArray(int[] nums) {
int[]dp=new int[nums.length];
dp[0]=nums[0];
int max= dp[0];
for(int i=1;i<nums.length;i++){
dp[i]=dp[i-1]>0?dp[i-1]+nums[i]:nums[i];
max= Math.max(dp[i],max);
}
return max;
}
}
class Solution {
public int maxSubArray(int[] nums) {
int pre= nums[0];
int max = nums[0];
for(int i=1;i<nums.length;i++){
int cur = pre>0?pre+nums[i]:nums[i];
max = Math.max(cur,max);
pre = cur;
}
return max;
}
}
72. 编辑距离
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 **所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
class Solution {
public int minDistance(String word1, String word2) {
int n1= word1.length();
int n2= word2.length();
int[][]dp=new int[n1+1][n2+1];
for(int i =1;i<=n1;i++){
dp[i][0]=dp[i-1][0]+1;
}
for(int j=1;j<=n2;j++){
dp[0][j]=j;
}
for(int i =1;i<=n1;i++){
for(int j =1;j<=n2;j++){
if(word1.charAt(i-1)!=word2.charAt(j-1)){
dp[i][j]= Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
}else{
dp[i][j]= dp[i-1][j-1];
}
}
}
return dp[n1][n2];
}
}
class Solution {
public int minDistance(String word1, String word2) {
int n1= word1.length();
int n2= word2.length();
int[][]dp=new int[n1+1][n2+1];
for(int i =1;i<=n1;i++){
dp[i][0]=dp[i-1][0]+1;
}
for(int j=1;j<=n2;j++){
dp[0][j]=j;
}
for(int i =1;i<=n1;i++){
for(int j =1;j<=n2;j++){
int left = dp[i-1][j]+1;
int down = dp[i][j-1]+1;
int left_down = dp[i-1][j-1];
if(word1.charAt(i-1)!=word2.charAt(j-1)){
left_down +=1;
}
dp[i][j] = Math.min(left, Math.min(down, left_down));
}
}
return dp[n1][n2];
}
}
5. 最长回文子串
给你一个字符串 s,找到 s 中最长的 回文 子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
int begin = 0;
int maxLen=1;
boolean[][]dp=new boolean[s.length()][s.length()];
for(int i=0;i<s.length();i++){
dp[i][i]=true;
}
for(int i=1;i<len;i++){
for(int j=0;j<i;j++){
if(s.charAt(i)!=s.charAt(j)){
dp[j][i]=false;
}else{
if(i-j<3){
dp[j][i]=true;
}else{
dp[j][i]=dp[j+1][i-1];
}
}
if(dp[j][i]&&i-j+1>maxLen){
maxLen = i-j+1;
begin = j;
}
}
}
return s.substring(begin,begin+maxLen);
}
}
贪心题目
55. 跳跃游戏
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
class Solution {
public boolean canJump(int[] nums) {
int maxStep= nums[0];
if(nums.length==1) return true;
for(int i=1;i<nums.length;i++){
if(i<= maxStep){
if(i+nums[i]>=maxStep){
maxStep=i+nums[i];
}
}
if(maxStep>=nums.length-1){
return true;
}
}
return false;
}
}
45. 跳跃游戏 II
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
class Solution {
public int jump(int[] nums) {
int step = 0;
int maxStep=0;
int end = 0;
for(int i=0;i<nums.length-1;i++){
maxStep = Math.max(maxStep,i+nums[i]);
if(i==end){
step++;
end= maxStep;
}
}
return step;
}
}
回溯算法
46. 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
class Solution {
List<List<Integer>> res = new ArrayList();
public List<List<Integer>> permute(int[] nums) {
back(new ArrayList(),0,nums);
return res;
}
void back(List<Integer> arr,int start,int[]nums){
if(arr.size()==nums.length){
res.add(new ArrayList(arr));
return;
}
for(int i=start;i<nums.length;i++){
if(!arr.contains(nums[i])){
arr.add(nums[i]);
back(arr,start,nums);
arr.remove(arr.size()-1);
}
}
}
}
47. 全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [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>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
Arrays.sort(nums);
helper(res, new ArrayList<>(), nums, new boolean[nums.length]);
return res;
}
public void helper(List<List<Integer>> res, List<Integer> list, int[] nums, boolean[] used) {
if (list.size() == nums.length) {
res.add(new ArrayList<>(list));
}
for (int i = 0; i < nums.length; i++) {
if (used[i] || i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;
used[i] = true;
list.add(nums[i]);
helper(res, list, nums, used);
used[i] = false;
list.remove(list.size() - 1);
}
}
}
39 组合总数
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 **不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
输入: candidates = [2,3,6,7], target = 7
输出: [[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
class Solution {
List<List<Integer>> res =new ArrayList();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
back(candidates,0,target,new ArrayList());
return res;
}
void back(int[]nums,int start, int target,List<Integer> temp){
if(target==0){
res.add(new ArrayList(temp));
return;
}
if(target<0){
return;
}
for(int i=start;i<nums.length;i++){
temp.add(nums[i]);
back(nums,i,target-nums[i],temp); //为什么这里面还是i 因为可以重新选,所以继续选,递归终止条件,==0 就返回,如果小于0代表超过了,也应该返回。
temp.remove(temp.size()-1);
}
}
}
40组合总和II
每个数字只能用1次。
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
class Solution {
List<List<Integer>> res = new ArrayList();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
dfs(candidates,target,0,new ArrayList());
return res;
}
public void dfs(int[] nums,int target,int start,List<Integer> temp){
if(target<0){
return;
}
if(target==0){
res.add(new ArrayList(temp));
return;
}
for(int i =start;i<nums.length;i++){
if (i>start && nums[i]==nums[i-1]){
continue;
}
temp.add(nums[i]);
dfs(nums,target-nums[i],i+1,temp);
temp.remove(temp.size()-1);
}
}
}
下一个排序(无聊的题目)
public class Solution {
public void nextPermutation(int[] nums) {
// 考察数学,比较无聊
// 从倒数第二个元素开始向左遍历,寻找第一个满足 nums[i] < nums[i + 1] 的位置 i
// 从数组的末尾开始向左遍历,找到第一个比 nums[i] 大的元素 nums[j]
//交互 nums[i]和nums[j];
//对i+1位置之后的进行反转
int i =nums.length-2;
while(i>=0&&nums[i]>=nums[i+1]){
i--;
}
if(i>=0){
int j =nums.length-1;
while(j>=0&&nums[j]<=nums[i]){
j--;
}
swap(nums,i,j);
}
// 对 i + 1 位置之后的元素进行反转
reverse(nums, i + 1);
}
// 反转数组中从 start 位置到末尾的元素
private void reverse(int[] nums, int start) {
int i = start, j = nums.length - 1;
// 使用双指针法,交换元素,将降序排列变为升序排列
while (i < j) {
swap(nums, i, j);
i++;
j--;
}
}
// 交换 nums 数组中 i 和 j 位置的元素
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
集II 可能有重复
public static List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
Arrays.sort(nums);
helper(res, new ArrayList<>(), nums, 0);
return res;
}
public static void helper(List<List<Integer>> res, List<Integer> list, int[] nums, int index) {
res.add(new ArrayList<>(list));
for (int i = index; i < nums.length; i++) {
if (i != index && nums[i] == nums[i - 1]) continue;
list.add(nums[i]);
helper(res, list, nums, i + 1);
list.remove(list.size() - 1);
}
}
132
class Solution {
List<List<String>> res= new ArrayList();
public List<List<String>> partition(String s) {
back(s,0,new ArrayList());
return res;
}
void back(String s,int start,List<String>temp){
if(start==s.length()){
res.add(new ArrayList(temp));
return;
}
for(int i=start;i<s.length();i++){
String s1= s.substring(start,i+1);
if(!check(s1)){
continue;
}
temp.add(s1);
back(s,i+1,temp);
temp.remove(temp.size()-1);
}
}
private boolean check(String s) {
if (s == null || s.length() <= 1) {
return true;
}
int left = 0;
int right = s.length() - 1;
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
}
数组
56. 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间
class Solution {
public int[][] merge(int[][] arr) {
Arrays.sort(arr,new Comparator<int[]>(){
public int compare(int[]a,int[]b){
return a[0]-b[0];
}
});
int i =0;
int n = arr.length;
List<int[]> res = new ArrayList();
while(i<n){
int left = arr[i][0];
int right =arr[i][1];
while(i<n-1&& right>=arr[i+1][0]){
right = Math.max(right,arr[i+1][1]);
i++;
}
res.add(new int[]{left,right});
i++;
}
return res.toArray(new int[res.size()][2]);
}
}
单调栈
int[] nextGreaterElement(int[] nums) {
int n = nums.length;
// 存放答案的数组
int[] res = new int[n];
Stack<Integer> s = new Stack<>();
// 倒着往栈里放
for (int i = n - 1; i >= 0; i--) {
// 判定个子高矮
while (!s.isEmpty() && s.peek() <= nums[i]) {
// 矮个起开,反正也被挡着了
s.pop();
}
// nums[i] 身后的更大元素
res[i] = s.isEmpty() ? -1 : s.peek();
s.push(nums[i]);
}
return res;
}
739. 每日温度
class Solution {
public int[] dailyTemperatures(int[] nums) {
int n = nums.length;
Stack<Integer> stack = new Stack();
int res[] = new int[n];
for(int i =n-1;i>=0;i--){
while(!stack.isEmpty()&&nums[stack.peek()]<=nums[i]){
stack.pop();
}
res[i]=stack.isEmpty()?0:stack.peek()-i;
stack.push(i);
}
return res;
}
}
84. 柱状图中最大的矩形
class Solution {
public int largestRectangleArea(int[] heights) {
// 这里为了代码简便,在柱体数组的头和尾加了两个高度为 0 的柱体。
int[] tmp = new int[heights.length + 2];
System.arraycopy(heights, 0, tmp, 1, heights.length);
Deque<Integer> stack = new ArrayDeque<>();
int area = 0;
for (int i = 0; i < tmp.length; i++) {
// 对栈中柱体来说,栈中的下一个柱体就是其「左边第一个小于自身的柱体」;
// 若当前柱体 i 的高度小于栈顶柱体的高度,说明 i 是栈顶柱体的「右边第一个小于栈顶柱体的柱体」。
// 因此以栈顶柱体为高的矩形的左右宽度边界就确定了,可以计算面积\U0001f336️ ~
while (!stack.isEmpty() && tmp[i] < tmp[stack.peek()]) {
int h = tmp[stack.pop()];
area = Math.max(area, (i - stack.peek() - 1) * h);
}
stack.push(i);
}
return area;
}
}
BFS
127. 单词接龙
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
int step= 0;
Queue<String> queue = new LinkedList();
if(!wordList.contains(endWord)){
return 0;
}
queue.add(beginWord);
Set<String> set = new HashSet();
for(String s:wordList){
set.add(s);
}
while(!queue.isEmpty()){
step++;
int levelSize = queue.size();
for(int i=0;i<levelSize;i++){
String word = queue.poll();
if(endWord.equals(word)){
return step;
}
StringBuilder sb = new StringBuilder(word);
for(int j=0;j<word.length();j++){
for(char c='a';c<='z';c++){
if(word.charAt(j)==c){
continue;
}
sb.setCharAt(j,c);
if(set.contains(sb.toString())){
queue.add(sb.toString());
set.remove(sb.toString());
}
sb.setCharAt(j,word.charAt(j));
}
}
}
}
return 0;
}
}
排序
归并排序
class Solution {
public int[] sortArray(int[] nums) {
mergeSort(nums,0,nums.length-1);
return nums;
}
void mergeSort(int[]nums,int l,int r){
if(l>=r) return;
int mid = l+(r-l)/2;
mergeSort(nums,l,mid);
mergeSort(nums,mid+1,r);
merge(nums,l,mid,r);
}
void merge(int[]nums,int l,int mid,int r){
int[]temp = new int[r-l+1];
int i=l;
int j=mid+1;
int k=0;
while(i<=mid&&j<=r){
if(nums[i]>=nums[j]){
temp[k++]=nums[j++];
}else{
temp[k++]=nums[i++];
}
}
while(i<=mid){
temp[k++]=nums[i++];
}
while(j<=r){
temp[k++]=nums[j++];
}
for(int a=0;a<temp.length;a++){
nums[a+l]=temp[a];
}
}
}
快速排序
class Solution {
public int[] sortArray(int[] nums) {
quick(nums,0,nums.length-1);
return nums;
}
void quick(int[]nums,int l,int r){
if(l<r){
int pivot = partion(nums,l,r);
quick(nums,l,pivot-1);
quick(nums,pivot+1,r);
}
}
int partion(int[]nums,int left,int right){
int random= new Random().nextInt(right-left+1)+left;
int pivot = nums[random];
swap(nums,left,random);
int j=left;
for(int i =left+1;i<=right;i++){
if(nums[i]<pivot){
j++;
swap(nums,i,j);
}
}
swap(nums,j,left);
return j;
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}}
模拟,实现题目
6. Z 字形变换 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
请你实现这个将字符串进行指定行数变换的函数:
- 思路:规律总结:遍历顺序是 0 到 n-1,然后 n-1 到 0
class Solution {
public String convert(String s, int numRows) {
if(numRows < 2) return s;
List<StringBuilder> rows = new ArrayList<StringBuilder>();
for(int i = 0; i < numRows; i++) rows.add(new StringBuilder());
int i = 0, flag = -1;
for(char c : s.toCharArray()) {
rows.get(i).append(c);
if(i == 0 || i == numRows -1) flag = - flag;
i += flag;
}
StringBuilder res = new StringBuilder();
for(StringBuilder row : rows) res.append(row);
return res.toString();
}
}
7. 整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
class Solution {
public int reverse(int x) {
long result = 0;
while(x!=0){
result = result*10+(x%10);
x= x/10;
}
return result > Integer.MAX_VALUE || result < Integer.MIN_VALUE ? 0 : (int) result;
}
}
8. 字符串转换整数 (atoi)
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数。
函数 myAtoi(string s) 的算法如下:
- 空格: 读入字符串并丢弃无用的前导空格(
" ") - 符号: 检查下一个字符(假设还未到字符末尾)为
'-'还是'+'。如果两者都不存在,则假定结果为正。 - 转换: 通过跳过前置零来读取该整数,直到遇到非数字字符或到达字符串的结尾。如果没有读取数字,则结果为0。
- 舍入: 如果整数数超过 32 位有符号整数范围
[−231, 231 − 1],需要截断这个整数,使其保持在这个范围内。具体来说,小于−231的整数应该被舍入为−231,大于231 − 1的整数应该被舍入为231 − 1。
返回整数作为最终结果。
- 要点:去除空格。刚开始+ -判断。
还有就是溢出判断,我们当前的结果+我们要添加的元素>max,根据公式反推,去校验当前元素要不要添加
public class Solution {
public int myAtoi(String str) {
str = str.trim();
int result = 0;
boolean isPos = true;
for(int i = 0; i < str.length(); i++){
char c = str.charAt(i);
if(i==0 && (c=='-'||c=='+')){
isPos = c=='+'?true:false;
} else if (c>='0' && c<='9'){
// 检查溢出情况 原因 比如 reusl*10+ digit>max 公式反推即可以得到这个。
if(result>(Integer.MAX_VALUE - (c - '0'))/10){
return isPos? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
result *= 10;
result += c - '0';
} else {
return isPos?result:-result;
}
}
return isPos?result:-result;
}
}
36. 有效的数独
43. 字符串相乘
class Solution {
public String multiply(String num1, String num2) {
/**
num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
例: 123 * 45, 123的第1位 2 和45的第0位 4 乘积 08 存放在结果的第[1, 2]位中
index: 0 1 2 3 4
1 2 3
* 4 5
---------
1 5
1 0
0 5
---------
0 6 1 5
1 2
0 8
0 4
---------
0 5 5 3 5
这样我们就可以单独都对每一位进行相乘计算把结果存入相应的index中
**/
int n1 = num1.length()-1;
int n2 = num2.length()-1;
if(n1 < 0 || n2 < 0) return "";
int[] mul = new int[n1+n2+2];
for(int i = n1; i >= 0; i--) {
for(int j = n2; j >= 0; j--) {
int bitmul = (num1.charAt(i)-'0') * (num2.charAt(j)-'0');
bitmul += mul[i+j+1]; // 先加低位判断是否有新的进位
mul[i+j] += bitmul / 10;
mul[i+j+1] = bitmul % 10;
}
}
StringBuilder sb = new StringBuilder();
int i = 0;
// 去掉前导0
while(i < mul.length-1 && mul[i] == 0)
i++;
for(; i < mul.length; ++i)
sb.append(mul[i]);
return sb.toString();
}
}
数学题目
31. 下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,
arr = [1,2,3],以下这些都可以视作arr的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1]。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,
arr = [1,2,3]的下一个排列是[1,3,2]。 - 类似地,
arr = [2,3,1]的下一个排列是[3,1,2]。 - 而
arr = [3,2,1]的下一个排列是[1,2,3],因为[3,2,1]不存在一个字典序更大的排列。 - 先找出最大的索引 k 满足 nums[k] < nums[k+1],如果不存在,就翻转整个数组;
再找出另一个最大索引 l 满足 nums[l] > nums[k];
交换 nums[l] 和 nums[k];
最后翻转 nums[k+1:]。
public class Solution {
public void nextPermutation(int[] nums) {
// 从倒数第二个元素开始向左遍历,寻找第一个满足 nums[i] < nums[i + 1] 的位置 i
int i = nums.length - 2;
while (i >= 0 && nums[i + 1] <= nums[i]) {
i--;
}
// 如果找到了这样的位置 i
if (i >= 0) {
// 从数组的末尾开始向左遍历,找到第一个比 nums[i] 大的元素 nums[j]
int j = nums.length - 1;
while (j >= 0 && nums[j] <= nums[i]) {
j--;
}
// 交换 nums[i] 和 nums[j]
swap(nums, i, j);
}
// 对 i + 1 位置之后的元素进行反转
reverse(nums, i + 1);
}
// 反转数组中从 start 位置到末尾的元素
private void reverse(int[] nums, int start) {
int i = start, j = nums.length - 1;
// 使用双指针法,交换元素,将降序排列变为升序排列
while (i < j) {
swap(nums, i, j);
i++;
j--;
}
}
// 交换 nums 数组中 i 和 j 位置的元素
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
小众题目
75. 颜色分类
给定一个包含红色、白色和蓝色、共 n **个元素的数组 nums ,原地 ****对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
class Solution {
//没有太多意思
public void sortColors(int[] nums) {
int ptr= 0 ;
int n = nums.length;
for(int i =0;i<n;i++){
if(nums[i]==0){
int temp = nums[i];
nums[i] = nums[ptr];
nums[ptr]= temp;
ptr++;
}
}
for(int i =ptr;i<n;i++){
if(nums[i]==1){
int temp = nums[i];
nums[i] = nums[ptr];
nums[ptr]= temp;
ptr++;
}
}
}
}