1.k个一组翻转
java
/**
* 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 reverseKGroup(ListNode head, int k) {
if(head==null||k == 1){
return head;
}
//需要三 + 一个指针,上一组的尾部preTail
//当前组的 头和尾
//下一组的存储 尾部
//最后指针更新
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode curr = head;//只需要上一组和当先组
ListNode preTail = dummy;//只需要上一组和当先组
while(curr!=null){
ListNode kNode = getKNode(curr,k);
if(kNode == null){
break;
}
//2.存储下一组头部 做连接
ListNode nextGroupHead = kNode.next;
//3.断开翻转
ListNode currGroupHead = curr;
ListNode currGroupTail = kNode;
ListNode[] a = reverseList(currGroupHead,currGroupTail);
currGroupHead = a[0];
currGroupTail = a[1];
//4.连接; 更新指针
preTail.next = currGroupHead;
currGroupTail.next = nextGroupHead;
//5. 更新指针
preTail = currGroupTail;
curr = nextGroupHead;
}
return dummy.next;
}
private ListNode getKNode(ListNode head,int k){
//这里相当于head往后数k个得到的节点
ListNode curr = head;
while(curr!=null&&k!=1){
curr= curr.next;
k--;
}
return curr;
}
private ListNode[] reverseList(ListNode head,ListNode tail){
ListNode pre = tail.next;
ListNode curr = head;
while(pre!=tail){
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
//这里实现翻转后,就相当于tail在前面,head在后面
//而且也接上了
return new ListNode[]{tail,head};
}
}
感觉链表的题的话,主要注意下,对于不能链表的切断和连接,对于引用类型,"指针"指向的位置,以及前后关系注意;
2.回溯算法
N皇后
class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> result = new ArrayList<>();
char[][] board = new char[n][n];
for(int i = 0;i<n;i++){
Arrays.fill(board[i],'.');
}
Set<Integer> cols = new HashSet<>();
Set<Integer> diag1 = new HashSet<>();
Set<Integer> diag2 = new HashSet<>();
backtrack(result,board,0,n,cols,diag1,diag2);
return result;
}
private static void backtrack(List<List<String>> result,char[][] board,int row,int n,
Set<Integer> cols,Set<Integer> diag1,Set<Integer> diag2){
if(row ==n){
result.add(construtBoard(board));
return;
}
for(int col = 0;col < n;col++){
if(cols.contains(col) || diag1.contains(row -col) ||
diag2.contains(row + col)){
continue;
}
board[row][col] = 'Q';
cols.add(col);
diag1.add(row -col);
diag2.add(row+col);
backtrack(result,board,row+1,n,cols,diag1,diag2);
board[row][col] = '.';
cols.remove(col);
diag1.remove(row-col);
diag2.remove(row+col);
}
}
private static List<String> construtBoard(char[][] board){
List<String> res = new ArrayList<>();
for(char[] row : board){
res.add(new String(row));
}
return res;
}
}
这里主要复习了下回溯的步骤;里面还有static静态关键字,静态方法里面调用对象、创建对象调用方法;不能直接调用实例方法;
静态方法的限制是:不能隐式的使用this,只要数据是显示提供的,(通过参数、局部变量、new等),就可以操作
3.贪心、动态规划
有的问题适合用贪心、有的问题适合用动态规划,不行就用回溯暴力搜索,
像这种32最长有效括号,这种动态规划、hard题,一般都是注意对边界条件处理,注意状态转移方程;
// 核心状态转移方程:
// 当前有效长度 = 前一个有效长度 + 2(当前匹配对) + pre前一段的有效长度
dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
0-1背包问题,按照变式,注意边界条件和初始化,遍历顺序;
二刷的过程感觉自己理解的快了,但是还不会咋优化,一些基本的算法如二分、排序、双指针等等还不能信手拈来,后面慢慢来吧