1、最长无重复子数组--返回数组长度
public int maxLength (int[] arr)
解法1:set
import java.util.*;
public class Solution {
/**
*
* @param arr int整型一维数组 the array
* @return int整型
*/
public int maxLength (int[] arr) {
// write code here
int n = arr.length;
int start = 0, end = 0;
int max = 0;
while (start <= end && end < n) {
Set<Integer> set = new HashSet<>();
while (end < n && set.add(arr[end])) {
if (end == n - 1) break;
end++;
}
max = Math.max(max, set.size());
if (end == n - 1) break;
start = end;
}
return max;
}
}
解法2:滑动窗口(双指针)
经典滑动窗口
import java.util.*;
public class Solution {
/**
*
* @param arr int整型一维数组 the array
* @return int整型
*/
public int maxLength (int[] arr) {
// write code here
// 滑动窗口
if (arr == null || arr.length == 0) return 0;
HashMap<Integer, Integer> window = new HashMap<>();
int left = 0, right = 0;
int n = arr.length;
int res = -1;
while (right < n) {
int in = arr[right];
right++;
window.put(in, window.getOrDefault(in, 0)+1);
while (window.get(in) > 1) {
int out = arr[left];
left++;
window.put(out, window.getOrDefault(out, 0)-1);
}
res = Math.max(res, right - left);
}
return res;
}
}
5、合并两个有序的数组 NC22
public void merge(int A[], int m, int B[], int n)
解法1:从后往前比较&使用函数
public class Solution {
public void merge(int A[], int m, int B[], int n) {
int i=m, j=n;
while(m>0 && n>0){
if(A[m-1] >= B[n-1]){
A[m+n-1] = A[m-1];
m--;
}else{
A[m+n-1] = B[n-1];
n--;
}
}
if(m==0) System.arraycopy(B, 0, A, 0, n);
}
}
或
public class Solution {
public void merge(int A[], int m, int B[], int n) {
int p1 = m - 1;
int p2 = n - 1;
int index = m + n - 1;
while (p1 >= 0 && p2 >= 0) {
A[index--] = A[p1] > B[p2] ? A[p1--] : B[p2--];
}
while (p2 >= 0) {
A[index--] = B[p2--];
}
}
}
6、删除链表的倒数第n个节点
public ListNode removeNthFromEnd (ListNode head, int n)
方案:先走n步,然后快慢一起走,直到最后
public class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode first = head;
ListNode second = head;
for(int i = 0; i < n; i++)
first = first.next;
//如果n的值等于链表的长度,直接返回去掉头结点的链表
if(first == null)
return head.next;
while(first.next != null) //同时移动两个指针
{
first = first.next;
second = second.next;
}
second.next = second.next.next;
return head;
}
}
7、反转字符串
public String solve (String str)
import java.util.*;
public class Solution {
public static void main(String[] args){
Solution s = new Solution();
String str = "abcd";
String str1 = s.solve(str);
System.out.println(str1);
}
//思路:将字符串里的字符对称相互替换
// 将字符串转为字符数组
public String solve (String str) {
char[] ch = str.toCharArray();
for(int i = 0;i < ch.length/2;i++){
char temp = ch[i];
ch[i] = ch[ch.length-1-i];
ch[ch.length-1-i] = temp;
}
String s = new String(ch);
return s;
}
}
或StringBuilder对象的reverse方法
8、NC54 数组中相加和为0的三元组
public ArrayList<ArrayList<Integer>> threeSum(int[] num)
i循环,left和right从i右侧开始移动,判断三数之和并进行移动
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
//双指针
Arrays.sort(num);//排序
ArrayList<ArrayList<Integer>> ans=new ArrayList<ArrayList<Integer>>();
for(int i=0;i<num.length-2;i++)
{
int left=i+1;
int right=num.length-1;
if(i>0&&num[i]==num[i-1])continue;//若相同,则跳过
while(left<right)
{
if(num[i]+num[left]+num[right]==0)
{ //三数相加为0,则输出
ArrayList<Integer> list=new ArrayList<>();
list.add(num[i]);
list.add(num[left]);
list.add(num[right]);
left++;right--;
ans.add(list);
//若相同,则跳过
while(left<right&&num[left]==num[left-1])left++;
while(left<right&&num[right]==num[right+1])right--;
}
if(num[i]+num[left]+num[right]<0)left++;//三数相加小于0,left右移
if(num[i]+num[left]+num[right]>0)right--;//三数和大于0,right左移
}
}
return ans;
}
}
9、NC128 接雨水问题
给定一个整形数组arr,已知其中所有的值都是非负的,将这个数组看作一个柱子高度图,计算按此排列的柱子,下雨之后能接多少雨水。
public long maxWater (int[] arr)
方案:左右双指针找最大的作为对应的数组值,遍历原数组,结果为左右低的减去当前
public long maxWater (int[] arr) {
// write code here
if(arr.length <= 2){
return 0;
}
int[] left = new int[arr.length];
left[0] = 0;
int leftMax = arr[0];
for(int i = 1 ; i < arr.length; i++){
leftMax = Math.max(leftMax,arr[i-1]);
left[i] = leftMax;
}
int[]right = new int[arr.length];
right[arr.length-1] = 0;
int rightMax = arr[arr.length-1];
for(int j = arr.length -2;j>=0;j--){
rightMax = Math.max(rightMax,arr[j+1]);
right[j] = rightMax;
}
long result = 0;
for(int k = 1; k < arr.length-1;k++){
result += Math.max(Math.min(left[k],right[k]) - arr[k],0);
}
return result;
}
10、NC96 判断一个链表是否为回文结构
public boolean isPail (ListNode head)
方法1:反转后比较
import java.util.*;
public class Solution {
public boolean isPail (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 cur1 = reverseList, cur2 = head;
while (cur1 != null) {
if (cur1.val != cur2.val) {
return false;
}
cur1 = cur1.next;
cur2 = cur2.next;
}
return true;
}
public ListNode reverse(ListNode head) {
ListNode cur = head, pre = null;
while (cur != null) {
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
方法2:入栈再出栈比较
方法3:存入数组,判断是否相等
public boolean isPail (ListNode head) {
// write code here
ArrayList<Integer> arr=new ArrayList<>();
while (head!=null){
arr.add(head.val);
head=head.next;
}
for (int i=0;i<arr.size()-i-1;i++){
//注意,此处最初写的是arr.get(i)!=arr.get(arr.size()-i-1),但是一直有测试例子无法通过
//还是equal牛逼
if (!arr.get(i).equals(arr.get(arr.size()-i-1))){
return false;
}
}
return true;
}
方法4:双端队列(入队后两端同时出队)
public boolean isPail (ListNode head) {
if (head == null || head.next == null) {
return false;
}
Deque<Integer> deque = new ArrayDeque<>();
while (head != null) {
deque.addLast(head.val);
head = head.next;
}
while (deque.size() > 1) {
if (!deque.pollFirst().equals(deque.pollLast())) {
return false;
}
}
return true;
}
11、NC82 滑动窗口的最大值
public ArrayList<Integer> maxInWindows(int [] num, int size)
解法1:双指针
//经典双指针问题
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList list = new ArrayList();
if(num.length == 0 || size < 1 || size > num.length) return list;
int left = 0, right = size - 1;
//在窗口中遍历求最大值并插入list,然后左右指针同时右移
while(right < num.length){
int max = num[left];
for(int i = left; i <= right; i++){
max = Math.max(max,num[i]);
}
left += 1;
right += 1;
list.add(max);
}
return list;
}
}
解法2:双端队列
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList<Integer> res = new ArrayList();
if(size<=0||num.length == 0) return res;
/**
维护一个双端队列,里面从first到last存储的是滑动窗口里的值的降序排列的下标
*/
Deque<Integer> queue = new LinkedList();
for(int i=0;i<num.length;i++){
/**
如果当前加入的这个数大于了队列末尾的数,则一直弹出来,最后再加入末尾
*/
while(!queue.isEmpty()&&num[queue.getLast()]<num[i]){
queue.pollLast();
}
queue.addLast(i);
/**
根据当前下标以及滑动窗口的大小看看first是否过期,过期了就删除
*/
if(queue.getFirst() == i-size){
queue.pollFirst();
}
/**
前几个元素不满足滑动窗口的大小,不加入,后面再加入
*/
if(i>=size-1){
res.add(num[queue.getFirst()]);
}
}
return res;
}
}
解法3:优先队列
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList<Integer> list = new ArrayList<>();
if(size>num.length || size==0)
return list;
PriorityQueue<Integer> qu = new PriorityQueue<>(new Comparator<Integer>(){
public int compare(Integer o1 ,Integer o2){
return o2-o1;
}
});
for(int i=0;i<size;i++){
qu.add(num[i]);
}
list.add(qu.peek());
qu.remove(num[0]);
for(int i=size;i<num.length;i++){
qu.add(num[i]);
list.add(qu.peek());
qu.remove(num[i-size+1]);
}
return list;
}
}
12、NC28 最小覆盖子串
public String minWindow (String S, String T)
解法1:滑动窗口
这道题的思路是:
1) begin开始指向0, end一直后移,直到begin - end区间包含T中所有字符。
记录窗口长度d
2) 然后begin开始后移移除元素,直到移除的字符是T中的字符则停止,此时T中有一个字符没被
包含在窗口,
3) 继续后移end,直到T中的所有字符被包含在窗口,重新记录最小的窗口d。
4) 如此循环知道end到S中的最后一个字符。
时间复杂度为O(n)
public class Solution {
public String minWindow(String S, String T) {
int[] map = new int[128];
//init map, 记录T中每个元素出现的次数
for(int i = 0; i < T.length(); i++) {
map[T.charAt(i)]++;
}
// begin end两个指针指向窗口的首位,d记录窗口的长度, counter记录T中还有几个字符没被窗口包含
int begin = 0, end = 0, d = Integer.MAX_VALUE, counter = T.length(), head = 0;
// end指针一直向后遍历
while(end < S.length()) {
// map[] > 0 说明该字符在T中出现,counter-- 表示对应的字符被包含在了窗口,counter--, 如果s中的字符没有在T中出现,则map[]中对应的字符-1后变为负值
if(map[S.charAt(end++)]-- > 0) {
counter--;
}
// 当counter==0时,说明窗口已经包含了T中的所有字符
while (counter == 0) {
if(end - begin < d) {
d = end - (head = begin);
}
if(map[S.charAt(begin++)]++ == 0) { // begin开始后移,继续向后寻找。如果begin后移后指向的字符在map中==0,表示是在T中出现的,如果没有出现,map[]中的值会是负值。
counter++; // 在T中的某个字符从窗口中移除,所以counter++。
}
}
}
return d==Integer.MAX_VALUE ? "" :S.substring(head, head+d);
}
}
13、划分链表
public ListNode partition (ListNode head, int x)
方法:两个链表相连
public ListNode partition (ListNode head, int x) {
// write code here
ListNode xiao=new ListNode(0);
ListNode xiaotmp=xiao;
ListNode da=new ListNode(0);
ListNode datmp=da;
while (head!=null){
if (head.val<x){
xiaotmp.next=new ListNode(head.val);
xiaotmp=xiaotmp.next;
}
else {
datmp.next=new ListNode(head.val);
datmp=datmp.next;
}
head=head.next;
}
xiaotmp.next=da.next;
return xiao.next;
}