这周挑战了一下周赛,结果卡在第三题了,果然还是太菜了.....,最后一题二叉树等刷到二叉树专题再加进来!
这周主要把链表的基本题目全刷完了。
#6072 转角路径的乘积中最多能有几个尾随零 |
周赛时想到了其实就是找5和2的个数和前缀和,但是最后转角时处理不好,最后也没写出来,参考了大佬的方法写了一个Java版的。
主要是要前缀和找5和2的个数,然后遍历时,要考虑四边情况(从左出发到上,从左出发到下,从右出发到上,从右出发到下)取最大值。
class Solution {
public int getCntFactor(int x,int factor){
int ret=0;
while(x%factor==0){
ret++;
x/=factor;
}
return ret;
}
public int maxTrailingZeros(int[][] grid) {
int n=grid.length,m=grid[0].length;
int[][]row2=new int[n+1][m+1];
int[][]col2=new int[n+1][m+1];
int[][]row5=new int[n+1][m+1];
int[][]col5=new int[n+1][m+1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int cnt2=getCntFactor(grid[i-1][j-1],2);
int cnt5=getCntFactor(grid[i-1][j-1],5);
row2[i][j]=row2[i][j-1]+cnt2;
col2[i][j]=col2[i-1][j]+cnt2;
row5[i][j]=row5[i][j-1]+cnt5;
col5[i][j]=col5[i-1][j]+cnt5;
}
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
//从左边出发,到上边结束
ans=Math.max(ans,Math.min(row2[i][j]+col2[i-1][j],row5[i][j]+col5[i-1][j]));
//从左边出发,到下边结束
ans=Math.max(ans,Math.min(row2[i][j]+col2[n][j]-col2[i][j],row5[i][j]+col5[n][j]-col5[i][j]));
//从右边出发,到上边结束
ans=Math.max(ans,Math.min(row2[i][m] - row2[i][j] + col2[i][j], row5[i][m] - row5[i][j] + col5[i][j]));
//从右边出发,到下边结束
ans= Math.max(ans, Math.min(row2[i][m] - row2[i][j] + col2[n][j] - col2[i-1][j], row5[i][m] - row5[i][j] + col5[n][j] - col5[i-1][j]));
}
}
return ans;
}
}
#567 字符串的排列 |
利用ASCII码,用滑动窗口来判断两个字符串关系。
class Solution {
public boolean checkInclusion(String s1, String s2) {
if(s1.length()>s2.length()) return false;
int left=0,right=s1.length();
int[]a1=new int[26];
int[]a2=new int[26];
for(int i=0;i<s1.length();i++){
a1[s1.charAt(i)-'a']++;
a2[s2.charAt(i)-'a']++;
}
if(Arrays.equals(a1,a2)) return true;
while(right<s2.length()){
a2[s2.charAt(right)-'a']++;
a2[s2.charAt(left)-'a']--;
if(Arrays.equals(a1,a2)) return true;
left++;
right++;
}
return false;
}
}
双指针,要注意是从右到左统计,避免重复(滑动就完事了)
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int left=0,answer=0,product=1;
for(int right=0;right<nums.length;right++){
product*=nums[right];
while(product>=k&&left<=right){
product/=nums[left++];
}
answer+=right-left+1;
}
return answer;
}
}
#283 移动零 |
直接忽视零,最后再补上
class Solution {
public void moveZeroes(int[] nums) {
int i=0,j=0;
for(i=0;i<nums.length;i++){
if(nums[i]!=0)
{
nums[j++]=nums[i];
}
}
while(j<nums.length)
{
nums[j++]=0;
}
}
}
双指针解决
class Solution {
public int[] sortedSquares(int[] nums) {
int l=0;
int r=nums.length-1;
int[]res=new int[nums.length];
int j=nums.length-1;
while(l<=r){
if(nums[l]*nums[l]>nums[r]*nums[r]){
res[j--]=nums[l]*nums[l++];
}else{
res[j--]=nums[r]*nums[r--];
}
}
return res;
}
}
#3 无重复字符的最长子串 |
滑动窗口解决,用哈希表统计出现的字符
class Solution {
public int lengthOfLongestSubstring(String s) {
// 哈希集合,记录每个字符是否出现过
Set<Character> occ = new HashSet<Character>();
int n = s.length();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.remove(s.charAt(i - 1));
}
while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
// 不断地移动右指针
occ.add(s.charAt(rk + 1));
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
}
#611 有效三角形的个数 |
妙用最长边简化
class Solution {
public int triangleNumber(int[] nums) {
int ans=0;
if(nums==null||nums.length==0){
return ans;
}
Arrays.sort(nums);
for(int i=nums.length-1;i>1;i--){
int start=0;
int end=i-1;
while(start<end){
if(nums[start]+nums[end]>nums[i]){
ans+=(end-start);//start、start+1.....end-1;
end--;
}else{
start++;
}
}
}
return ans;
}
}
#844 比较含退格的字符串 |
1.栈的运用
class Solution {
public boolean backspaceCompare(String s, String t) {
StringBuilder stack_s=new StringBuilder();
StringBuilder stack_t=new StringBuilder();
for(char c:s.toCharArray()){
if(c!='#'){
stack_s.append(c);
}else if(stack_s.length()>0){
stack_s.deleteCharAt(stack_s.length()-1);
}
}
for(char c:t.toCharArray()){
if(c!='#'){
stack_t.append(c);
}else if(stack_t.length()>0){
stack_t.deleteCharAt(stack_t.length()-1);
}
}
return stack_s.toString().equals(stack_t.toString());
}
}
2.双指针模拟
class Solution {
public boolean backspaceCompare(String s, String t) {
int i=s.length()-1,j=t.length()-1;
int skips=0,skipt=0;
char[]S=s.toCharArray();
char[]T=t.toCharArray();
while(true){
while(i>=0){
if(S[i]=='#') skips++;
else{
if(skips>0) skips--;
else break;
}
i--;
}
while(j>=0){
if(T[j]=='#') skipt++;
else{
if(skipt>0) skipt--;
else break;
}
j--;
}
if(i<0||j<0) break;
if(S[i]!=T[j]) return false;
i--;
j--;
}
if(i==-1&&j==-1) return true;
return false;
}
}
#34 在排序数组中查找元素的第一个和最后一个元素的位置
二分查找
class Solution {
public int[] searchRange(int[] nums, int target) {
int[]res={-1,-1};
int index=binarySearch(nums,target);
if(index==-1){
return res;
}
int left=index,right=index;
while(left-1>=0&&nums[left-1]==nums[index]){
left--;
}
while(right+1<nums.length&&nums[right+1]==nums[index]){
right++;
}
return new int[]{left,right};
}
public int binarySearch(int[]nums,int target){
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]==target){
return mid;
}else if(nums[mid]<target){
left=mid+1;
}else{
right=mid-1;
}
}
return -1;
}
}
好题!
class Solution {
public String longestPalindrome(String s) {
if(s==null||s.length()==0){
return"";
}
int[]range=new int[2];
char[]str=s.toCharArray();
for(int i=0;i<s.length();i++){
i=findLongest(str,i,range);
}
return s.substring(range[0],range[1]+1);
}
public static int findLongest(char[]str,int low,int[]range){
int high=low;
while(high<str.length-1&&str[high+1]==str[low]){
high++;
}
int ans=high;
while(low>0&&high<str.length-1&&str[low-1]==str[high+1]){
low--;
high++;
}
if(high-low>range[1]-range[0]){
range[0]=low;
range[1]=high;
}
return ans;
}
}
链表的反转是很常用的操作,可以通过画图理解一下next,pre的次序(其实很好理解,pre就是当前节点的前一个,next就是当前节点的后一个,时刻保证这两个是保持这样的变化就可以)
/*
public class ListNode {//define listNode
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
ListNode pre=null;//pre ListNode
ListNode next=null;//next ListNode
while(head!=null){
next=head.next;
head.next=pre;//reverse ListNode
pre=head;
head=next;
}
return pre;
}
}
指定区间反转和整体反转略有不同。这个需要理解一下,
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类
* @param m int整型
* @param n int整型
* @return ListNode类
*/
public ListNode reverseBetween (ListNode head, int m, int n) {
ListNode res=new ListNode(-1);
res.next=head;
ListNode cur=head;
ListNode pre=res;
for(int i=1;i<m;i++){//the pre m nodes remain the same
pre=cur;
cur=cur.next;
}
for(int i=m;i<n;i++){
ListNode nextNode=cur.next;
cur.next=nextNode.next;
nextNode.next=pre.next;
pre.next=nextNode;
}
return res.next;
}
}
#25 K 个一组翻转链表 |
递归解决每k个一组
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) {
ListNode tail=head;
for(int i=0;i<k;i++){//find k tail Node
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);//recursion
return pre;
}
}
java 快排模板:
public class QuickSort {
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public void quickSort(int[] arr, int start, int end) {
if (start >= end)
return;
int k = arr[start];
int i = start, j = end;
while (i != j) {
while (i < j && arr[j] >= k)
--j;
swap(arr, i, j);
while (i < j && arr[i] <= k)
++i;
swap(arr, i, j);
}
quickSort(arr, start, i - 1);
quickSort(arr, i + 1, end);
}
public static void main(String[] args) {
int[] arr = {5, 2, 6, 9, 1, 3, 4, 8, 7, 10};
new QuickSort().quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
}
java归并排序模板:
public class MergeSort {
public static void merge_sort(int q[], int left, int right)
{
if (left >= right) return;
int []tmp=new int[q.length];
int mid = left + right >> 1;
merge_sort(q, left, mid);
merge_sort(q, mid + 1, right);
int k = 0, i = left, j = mid + 1;
while (i <= mid && j <= right)
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= right) tmp[k ++ ] = q[j ++ ];
for (i = left, j = 0; i <= right; i ++, j ++ ) q[i] = tmp[j];
}
public static void main(String[]args) {
int[]arr= {5,4,2,1,3};
System.out.println("The original list is:");
for(int i=0;i<arr.length;i++) System.out.print(arr[i]+" ");
System.out.println(" ");
merge_sort(arr,0,arr.length-1);
System.out.println("The mergesorted list is:");
for(int i=0;i<arr.length;i++) System.out.print(arr[i]+" ");
}
}
分治合并,两个两个合并,递归合成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 merge(ListNode list1,ListNode list2){//TwoMerge
if(list1==null) return list2;
if(list2==null) return list1;
ListNode head=new ListNode(0);
ListNode cur=head;
while(list1!=null&list2!=null){
if(list1.val>list2.val){
cur.next=list2;
list2=list2.next;
}else{
cur.next=list1;
list1=list1.next;
}
cur=cur.next;
}
if(list1!=null){
cur.next=list1;
}
if(list2!=null){
cur.next=list2;
}
return head.next;
}
ListNode divideMerge(ListNode[]lists,int left,int right){//divide space
if(left>right){
return null;
}else if(left==right){
return lists[left];
}
int mid=left+right>>1;
return merge(divideMerge(lists,left,mid),divideMerge(lists,mid+1,right));
}
public ListNode mergeKLists(ListNode[] lists) {
return divideMerge(lists,0,lists.length-1);
}
}
#141 环形链表 |
快慢指针,easy;如果有循环的话,那快慢指针必定相逢!
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head==null){
return false;
}
ListNode fast=head;
ListNode slow =head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return true;
}
}
return false;
}
}
2.抖机灵做法;
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
int count=10000;//leetcode 测试用例最多10000个
while(head!=null&&count>0){
head=head.next;
count--;
}
if(head==null)
return false;
return true;
}
}
#剑指 Offer II 022 链表中环的入口节点 |
1.HashMap<ListNode,Boolean>map 容易理解并且解决,但是效率好像不是很高
/**
* 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) {
ListNode node=head;
HashMap<ListNode,Boolean>map=new HashMap<>();
while(node!=null){
if(map.containsKey(node)){
return node;
}else{
map.put(node,true);
node=node.next;
}
}
return null;
}
}
2.数学找规律求解,快慢指针有点难理解,但是效率好像快一点
n为任意数
fast的路程是:a+b+n(b+c),fast=2*slow;slow=a+b;
则可得a+n(b+c)+b=2(a+b)⟹a=c+(n−1)(b+c)
所以再用一个指针从头走,slow也走,再次相遇就是入口处。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode hasCycle(ListNode head){
if(head==null) return null;
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return slow;
}
}
return null;
}
public ListNode detectCycle(ListNode head) {
ListNode slow=hasCycle(head);
ListNode fast=head;
if(slow==null) return null;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return slow;
}
}
1.用栈的性质解决
两个栈压入,再弹出相加,记下进位
/**
* 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 addTwoNumbers(ListNode head1, ListNode head2) {
Stack<ListNode>stack1=new Stack<>();
Stack<ListNode>stack2=new Stack<>();
ListNode p1=head1;
ListNode p2=head2;
while(p1!=null){
stack1.push(p1);
p1=p1.next;
}
while(p2!=null){
stack2.push(p2);
p2=p2.next;
}
int carry=0;
ListNode ans=new ListNode(0);
ListNode cur=ans;
while(!stack1.isEmpty()||!stack2.isEmpty()){
int pp1=!stack1.isEmpty()?stack1.pop().val:0;
int pp2=!stack2.isEmpty()?stack2.pop().val:0;
int plus=carry+pp1+pp2;
carry=plus/10;
ListNode newNode=new ListNode(plus%10);
newNode.next=cur.next;
cur.next=newNode;
}
if(carry>0){
ans.val=1;
return ans;
}else{
return ans.next;
}
}
}
2.两次链表翻转
/**
* 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 reverseListNode(ListNode head){
if(head==null){
return head;
}
ListNode pre=null;
ListNode cur=head;
while(cur!=null){
ListNode temp=cur.next;
cur.next=pre;
pre=cur;
cur=temp;
}
return pre;
}
public ListNode addTwoNumbers(ListNode head1, ListNode head2) {
if(head1==null){
return head2;
}
if(head2==null){
return head1;
}
head1=reverseListNode(head1);
head2=reverseListNode(head2);
int carry=0;
ListNode ans=new ListNode(-1);
ListNode head=ans;
while(head1!=null||head2!=null||carry!=0){
int val1=head1==null?0:head1.val;
int val2=head2==null?0:head2.val;
int temp=val1+val2+carry;
carry=temp/10;
temp=temp%10;
head.next=new ListNode(temp);
head=head.next;
if(head1!=null){
head1=head1.next;
}
if(head2!=null){
head2=head2.next;
}
}
return reverseListNode(ans.next);
}
}
#剑指 Offer II 077 链表排序 |
1.链表的冒泡排序(牛客能过,但leetcode超时,的确效率低,但容易想容易写)
/**
* 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 sortList(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode move=head;
while(move.next!=null){
ListNode temp=move.next;
while(temp!=null){
if(temp.val<move.val){
int val=move.val;
move.val=temp.val;
temp.val=val;
}
temp=temp.next;
}
move=move.next;
}
return head;
}
}
2.链表的归并排序(效率高)
/**
* 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 sortList(ListNode head) {
if(head==null||head.next==null){
return head;
}
return mergesort(head);
}
public ListNode mergesort(ListNode head){
if(head.next==null){
return head;
}
ListNode fast=head,slow=head,pre=null;
while(fast!=null&&fast.next!=null){
pre=slow;
fast=fast.next.next;
slow=slow.next;
}
pre.next=null;
ListNode left=mergesort(head);
ListNode right=mergesort(slow);
return merge(left,right);
}
public ListNode merge(ListNode left,ListNode right){
ListNode dummy=new ListNode(-1);
ListNode cur=dummy;
while(left!=null&&right!=null){
if(left.val<right.val){
cur.next=left;
left=left.next;
}else{
cur.next=right;
right=right.next;
}
cur=cur.next;
}
if(left!=null){
cur.next=left;
}
if(right!=null){
cur.next=right;
}
return dummy.next;
}
}
#234 回文链表 |
快慢指针,一半翻转比较是否回文
/**
* 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 boolean isPalindrome(ListNode head) {
ListNode slow=head,fast=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
ListNode reverseList=reverse(slow);
ListNode head1=reverseList;
ListNode head2=head;
while(head1!=null){
if(head1.val!=head2.val){
return false;
}else{
head1=head1.next;
head2=head2.next;
}
}
return true;
}
public ListNode reverse(ListNode head){
ListNode pre=null;
ListNode cur=head;
while(cur!=null){
ListNode next=cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
return pre;
}
}
#328 奇偶链表 |
奇偶指针,遍历
/**
* 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 oddEvenList(ListNode head) {
if(head==null){
return null;
}
ListNode odd=head;
ListNode even=head.next;
ListNode evenHead=even;
while(even!=null&&even.next!=null){
odd.next=even.next;
odd=odd.next;
even.next=odd.next;
even=even.next;
}
odd.next=evenHead;
return head;
}
}
#83 删除排序链表中的重复元素 |
简单模拟
/**
* 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 deleteDuplicates(ListNode head) {
if(head==null) return null;
ListNode cur=head;
while(cur!=null&&cur.next!=null){
if(cur.val==cur.next.val){
cur.next=cur.next.next;
}else{
cur=cur.next;
}
}
return 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; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head==null||head.next==null){
return head;
}
head.next=deleteDuplicates(head.next);
if(head.val==head.next.val) head=head.next;
return 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; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head==null){
return null;
}
ListNode ans=new ListNode(0);
ans.next=head;
ListNode cur=ans;
while(cur.next!=null&&cur.next.next!=null){
if(cur.next.val==cur.next.next.val){
int temp=cur.next.val;
while(cur.next!=null&&cur.next.val==temp){
cur.next=cur.next.next;
}
}else{
cur=cur.next;
}
}
return ans.next;
}
}
从左下角看,往上是减小,往右是变大。
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix.length==0){
return false;
}
int row=matrix.length;
if(matrix[0].length==0){
return false;
}
int col=matrix[0].length;
for(int i=row-1,j=0;i>=0&&j<col;){
if(matrix[i][j]>target){
i--;
}else if(matrix[i][j]<target){
j++;
}else{
return true;
}
}
return false;
}
}
#162 寻找峰值 |
上坡必有坡顶,所以上坡的那一段一定有峰值(二分法原理)
class Solution {
public int findPeakElement(int[] nums) {
int left=0,right=nums.length-1;
while(left<right){
int mid=left+((right-left)>>1);
if(nums[mid]>nums[mid+1]){
right=mid;
}else{
left=mid+1;
}
}
return right;
}
}