String一章节墨迹了六七天。后面AC15 以下的题目不做了,没有什么启发性,都是些就题论题,又很难想,实现繁琐的题目。目前时间紧张,要提升到更重要的方面还有很多,所以果断跳过了。 下周重做String(AC 28 -19) 时候再整理. 现在的计划是八月底过完第二遍(190题左右算一遍 AC 15以下暂避)
Linked List一直是我软肋。 我心不够细致。希望这遍能有质的提升。
这题是第一道有点启发的list 题目。
1.做法肯定是constant space。 就new 了一个 node, 其余的只是把原有的node 接来接去。
2. 要想插入到node之前,必须保存node的前一个node。 其实替换/删除 node也是需要保存前一个的,在不允许改变node.val的情况下。
public class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode prev = new ListNode(0);
prev.next = l1;
ListNode head = prev;
while(l1 != null && l2 != null){
if(l1.val > l2.val){
ListNode temp = l2.next;
l2.next = l1;
prev.next = l2;
l2 = temp;
}else{
l1 = l1.next;
}
prev = prev.next;
}
if(l2 != null)//This is necessary. Notice prev will only match the tail when l2 still has nodes left.
prev.next = l2;
return head.next;
}
}
经典的小题,还记得第一次做做了很久,第二遍就顺利多了。
1。guard node的使用不说多说了
2。 prev cur nxt 三巨头。
public class Solution {
public ListNode swapPairs(ListNode head) {
ListNode guard = new ListNode(0);
guard.next = head;
ListNode prev = guard;
ListNode cur = head;
while(cur != null && cur.next != null){
ListNode nxt = cur.next;
prev.next = nxt;
ListNode temp = nxt.next;
nxt.next = cur;
cur.next = temp;
prev = cur;
cur = cur.next;
}
return guard.next;
}
}
Convert Sorted List to Binary Search Tree : ****
太喜欢这道题了。 这才是我所欣赏的算法艺术。简单梳理几点,这道题要火候到了自然理解,不到的话写才明白也难知其精髓
1. 老生常谈, BST重要性质就是 in-order traversal 保留有序性。
2. recursion 的pattern就是触底反弹(回). 这也是追踪观察recursion过程的一个要领。
3. 这个题要想order(n)内解决,就必须用in-order traversal, 这样linked list只需要扫一遍从头到尾。按照in-order的顺序来构建tree!自然先从left开始。
4. 第四点,返回条件是 if(l > r){ return null} (其实这个和sorted array 返回 BST是一样的,理清思路一步一步来可以想到的) 。
这个题可以启发到 可以根据 in-order post-order pre-order的list 来构建binary tree么?其实如果这个题换个说,说给你一个tree的 in-order traversal的list,让你重建tree,是一样的。
public class Solution {
public TreeNode sortedListToBST(ListNode head) {
int count = 0;
ListNode node = head;
while(head != null){
count ++;
head = head.next;
}
List
list = new ArrayList
();
list.add(node);
return helper(list,1,count);
}
public TreeNode helper(List
list, int l, int r){
if(l > r){
return null;
}
int m = (l+r)/2;
TreeNode left = helper(list,l,m-1);
TreeNode root = new TreeNode(list.get(0).val);
root.left = left;
list.set(0,list.get(0).next);
TreeNode right = helper(list,m+1,r);
root.right = right;
return root;
}
}
Partition List : **
//关于linked list有一个很容易陷入的误区。像下面第一种做法,其实除去指针外是不消耗空间的,就是把node接来接去。猛的一想还以为
//需要额外空间的,是一种来自array的误导。所以第一周方法显然是最优最易于实现的。
class Solution {
public:
ListNode *partition(ListNode *head, int x) {
ListNode *head1,*head2,*p1,*p2;
head1=new ListNode(0);
head2=new ListNode(0);
p1=head1;
p2=head2;
while(head!=NULL)
{
if(head->val
next=new ListNode(head->val);
p1=p1->next;
}
else
{
p2->next=new ListNode(head->val);
p2=p2->next;
}
head=head->next;
}
if(head2->next!=NULL)p1->next=head2->next;
ListNode *temp=head1->next;
delete(head1);
delete(head2);
return temp;
}
};//http://blog.youkuaiyun.com/havenoidea
//这是一种直接的想法,但是实现起来不简单。我做来挺久,最后发现是少了 “cur != end” 的判断。
public class Solution {
public ListNode partition(ListNode head, int x) {
if(head == null || head.next == null)
return head;
ListNode end = head;
int count = 1;
while(end.next != null){
count ++;
end = end.next;
}
ListNode res = new ListNode(0);
res.next = head;
ListNode prev = res;
//ListNode prev = new ListNode(0);
//prev.next = head;
//ListNode res = prev;
//上面俩个写法是等价的,关于java 的传参问题不应该再有疑问。
ListNode cur = head;
while(count > 0 && cur != null){
if(cur.val < x){
prev = cur;
cur = cur.next;
}else {
if(cur != end){
ListNode nxt = cur.next;
prev.next = nxt;
end.next = cur;
cur.next = null;
cur = nxt;
end = end.next;
}
}
count -- ;
}
return res.next;
}
}
Remove Nth Node From End of List : **
挺喜欢这个题目。
一点。 list不扫一遍不知道长度,那么在一遍中如何能知道目前所在node的绝对位置呢?
用俩个node的相对位置决定。
例如经典的找中点,一快一慢,快的一次走俩,慢的一次走一个,快的到终点时候,慢的所在位置就是终点。
这个题非常雷同,快的先走到第n个node,然后一起走,快的到终点了,慢的在的就是从后往前数到第n个。而我们要的是这个的前一个,所以让快的到倒数第二个,慢的正好停在倒数第n个的前一个,就可以慢倒数第n个去掉了。
public class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode f = head;
ListNode s = head;
while(n-1>0){
f = f.next;
n--;
}
while(f.next!= null && f.next.next != null){
s = s.next;
f = f.next;
}
if(f.next == null){
head = head.next;
return head;
}else{
s.next = s.next.next;
}
return head;
}
}
Insertion Sort List : ***
这个题真是。。。做了很久。思路很简单,但是有一个致命点细节问题,表面是细节,其实是关乎实现的大问题。用另一种似乎近似的实现,做了很久也做不对,放弃了。
具体看代码描述
public class Solution {
public ListNode insertionSortList(ListNode head) {
ListNode guard = new ListNode(0);
//注意,guard没有向通常那样直接与head相连。
//这里的意图是实现决定的,在insertion sort里,我们去维护前面元素的一个独立的sorted list。
//我们要这个sorted list的尾元素的next 保持是null
//**********
//当处理第一个node时候,他前面没有node,所有他前面是没有sorted list的。我们把他加入sorted list后,我们要
//保持sorted list的边界,这个list 此时只有这一个node,要node与他本来的next 切断才行。
//**********
//我试图不用这种实现,把guard直接连上了head 并且试图检查backtrack是否为cur 来决定sorted list是否扫完一遍,无果,这其中的联系很复杂,
//会有node自己连上自己进入死循环,在leetcode上会持续报 memory limit exceeded。最后也没有成功做出来这种实现。
ListNode cur = head;
ListNode backtrack = guard;
while(cur != null){
backtrack = guard;
ListNode nxt = cur.next;
while(backtrack.next != null && backtrack.next.val < cur.val ){
backtrack = backtrack.next;
}
cur.next = backtrack.next;
backtrack.next = cur;
cur = nxt;
}
return guard.next;
}
}
这种题没啥好说的,关键是细节上当场能不能保持清醒,一遍做对。也不知道面到这种题目是好事还是坏事。总之这个reverse点操作还是要做熟。
//code ganker的思路,插入法
public ListNode reverseBetween(ListNode head, int m, int n) {
ListNode guard = new ListNode(0);
guard.next = head;
ListNode prev = guard;
int i = 1;
while(i < m){
prev = prev.next;
i++;
}
if(prev.next == null)
return head;
ListNode close = prev.next;
ListNode cur = prev.next.next;
while(i < n){
ListNode temp = cur.next;
cur.next = prev.next;
prev.next = cur;
close.next = temp;
cur = temp;
i++;
}
return guard.next;
}
//自己的实现,调转法
public class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
int diff = n-m;
ListNode guard = new ListNode(0);
guard.next = head;
ListNode node = guard;
if(m == n)
return head;
while(m>1){
node = node.next;
m--;
}
ListNode prev = node;
ListNode cur = node.next;
ListNode nxt = node.next.next;
while(diff > 0){
ListNode temp = nxt.next;
nxt.next = cur;
cur = nxt;
nxt = temp;
diff--;
}
prev.next.next = nxt;
prev.next = cur;
return guard.next;
}
}
Reverse Nodes in k-Group : ****
倒叙,删除,添加是linked list必须熟练但节本操作,核心都是 prev cur nxt 这三个node。 技巧是要求head前面加一个guard node
这个题昨晚做了很久,总算弄出来。其实思路是有的,方法也明确,问题都出在边界问题的上。其实边界问题还是好处理的,把边界情况单独想一下,不要觉得麻烦,
其实很明白的。而且代码本身往往是揭示了需要检查哪些exception的。说白了在外层while循环内部,每次向前移动了node(尤其是在while loop 内),都要考虑检查null exception。
链接是之前单独整理的这个题目。
Sort List : **
public class Solution {
public ListNode sortList(ListNode head) {
return mergeSort(head);
}
public ListNode mergeSort(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode s = head;
ListNode f = head;
while(f.next != null && f.next.next != null){
f = f.next.next;
s = s.next;
}
ListNode head1 = head;
ListNode head2 = s.next;
s.next = null;//Cut off second half!
//This is also required in the question : Insertion Sort List.
head1 = mergeSort(head1);
head2 = mergeSort(head2);
return merge(head1,head2);
}
public ListNode merge(ListNode head1, ListNode head2){
ListNode guard = new ListNode(0);
ListNode node = guard;
while(head1 != null && head2 != null){
if(head1.val > head2.val){
node.next = head2;
head2 = head2.next;
}else{
node.next = head1;
head1 = head1.next;
}
node = node.next;
}
while(head1 != null){
node.next = head1;
head1 = head1.next;
node = node.next;
}
while(head2 != null){
node.next = head2;
head2 = head2.next;
node = node.next;
}
return guard.next;
}
}
还是变着法考reverse
public class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null)
return true;
ListNode f = head;
ListNode s = head;
ListNode node = head;
int count = 0;
while(node != null){
node = node.next;
count ++;
}
while(f.next != null && f.next.next != null){
f = f.next.next;
s = s.next;
}
ListNode head2 = s.next;
s.next = null;
ListNode prev = new ListNode(0);
prev.next = head;
ListNode cur = head;
ListNode nxt = cur.next;
while(nxt != null){
ListNode temp = nxt.next;
nxt.next = cur;
cur = nxt;
nxt = temp;
}
prev.next.next = null;
prev.next = cur;
ListNode head1 = prev.next;
if(count%2 == 1){
head1 = head1.next;
}
while(head1 != null && head2 != null){
if(head1.val != head2.val){
return false;
}
head1 = head1.next;
head2 = head2.next;
}
return true;
}
}
Rotate List : **
public class Solution {
public ListNode rotateRight(ListNode head, int k) {
//always check this
if(head == null || head.next == null)
return head;
ListNode prev = new ListNode(0);
prev.next = head;
ListNode node = head;
int count = 0;
while(node != null){
node = node.next;
count ++ ;
}
//Trap here
k = k%count;
if(k == 0)
return head;
//***
node = prev;
for(int i=1; i<=count-k; i++){
node = node.next;
}
//same as:
//node = head;
//for(int i=2; i<=count-k; i++){
// node = node.next;
//}
//***
ListNode temp = node.next;
ListNode node2 = temp;
node.next = null;
while(node2.next != null){
node2 = node2.next;
}
node2.next = prev.next;
prev.next = temp;
return prev.next;
}
}
这个题给四星。我一直觉得recursion是我的算法里第一个掌握精髓的技巧。但是由于我60%的recursion的练习都是关于Tree的,导致我的思维上习惯性的在非遍历多Tree的问题上
recursion的神经不敏感。这是需要突破的思维限制。这个题的recursion用的很简单,但是我总觉很巧妙,很难想。这也正是我recursion这一关最后的一个突破点,自由联想。
这个其实无需解释多了,一看就明白。和每俩个,再把每俩个的和的结果继续和。典型的recursion 模式。 时间复杂度时 T(n) = T(n/2) + k*n
另外附priority queue的做法,但是个人觉得这更多依赖数据结构内部的算法了。但是还是很经典的思路。
public class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists == null || lists.length == 0)
return null;
return helper(lists,0,lists.length-1);
}
public ListNode helper(ListNode[] lists, int l, int r){
if(l >= r){
return lists[l];
}
int m = (l+r)/2;
return merge( helper(lists, l, m), helper(lists,m+1,r));
}
public ListNode merge(ListNode head1, ListNode head2){
ListNode guard = new ListNode(0);
ListNode node = guard;
while(head1 != null && head2 != null){
if(head1.val < head2.val){
node.next = head1;
head1 = head1.next;
}else{
node.next = head2;
head2 = head2.next;
}
node = node.next;
}
if(head1 == null){
node.next = head2;
}else{
node.next = head1;
}
return guard.next;
}
}
//另附priority queue 做法。
public class Solution {
public ListNode mergeKLists(List
lists) {
if (lists.size() == 0)
return null;
PriorityQueue
queue = new PriorityQueue
(lists.size(), new Comparator
() {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});
for (int i = 0; i < lists.size(); i++) {
if (lists.get(i) != null)
queue.add(lists.get(i));
}
ListNode head = new ListNode(0);
ListNode p = head;
while (!queue.isEmpty()) {
ListNode node = queue.poll();
p.next = node;
if (node.next != null)
queue.add(node.next);
p = p.next;
}
return head.next;
}
}
public class Solution {
public void reorderList(ListNode head) {
//whenever you would reverse a linked list, check this at very beginning
if(head == null || head.next == null)
return;
ListNode f = head;
ListNode s = head;
while(f.next != null && f.next.next != null){
f = f.next.next;
s = s.next;
}
ListNode cur = s.next;
s.next = null;
ListNode prev = new ListNode(0);
prev.next = cur;
ListNode nxt = cur.next;
ListNode temp;
while(nxt != null){
temp = nxt.next;
nxt.next = cur;
cur = nxt;
nxt = temp;
}
prev.next.next = nxt;
prev.next = cur;
ListNode head2 = prev.next;
while(head != null && head2 != null){
temp = head.next;
head.next = head2;
head = temp;
temp = head2.next;
head2.next = head;
head2 = temp;
}
}
}