思路:
代码
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
ListNode dummy = new ListNode();
ListNode p = dummy;
int sum = 0, carry = 0;
while(l1 != null && l2 != null){
sum = l1.val + l2.val + carry;
if(sum > 9){
carry = sum / 10;
sum -= 10;
}else {
carry = 0;
}
ListNode s = new ListNode(sum);
p.next = s;
p = p.next;
l1 = l1.next;
l2 = l2.next;
}
while(l1 != null){
sum = l1.val + carry;
if(sum >= 10){
carry = sum / 10;
sum -= 10;
}else {
carry = 0;
}
ListNode s = new ListNode(sum);
p.next = s;
p = p.next;
l1 = l1.next;
}
while(l2 != null){
sum = l2.val + carry;
if(sum >= 10){
carry = sum / 10;
sum -= 10;
}else {
carry = 0;
}
ListNode s = new ListNode(sum);
p.next = s;
p = p.next;
l2 = l2.next;
}
if(carry != 0){
ListNode s = new ListNode(carry);
p.next = s;
p = p.next;
}
return dummy.next;
}
思路:
代码
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// return reverse(head);
ListNode newHead = reverse(head);
ListNode dummy = new ListNode();
dummy.next = newHead;
ListNode p = dummy, pre = null;
while(n != 0){
pre = p;
p = p.next;
n--;
}
pre.next = p.next;
return reverse(dummy.next);
}
public ListNode reverse(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode p = head, q = head.next, h;
while(q != null){
h = q.next;
q.next = p;
p = q;
q = h;
}
head.next = null;
return p;
}
}
思路:
代码
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
ListNode dummy = new ListNode();
ListNode p = dummy;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
p.next = l1;
l1 = l1.next;
}else {
p.next = l2;
l2 = l2.next;
}
p = p.next;
}
while(l1 != null){
p.next = l1;
p = p.next;
l1 = l1.next;
}
while(l2 != null){
p.next = l2;
p = p.next;
l2 = l2.next;
}
return dummy.next;
}
}
思路:
代码
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode res = null;
for(int i = 0; i < lists.length; i++){
res = mergerTwo(res, lists[i]);
}
return res;
}
public ListNode mergerTwo(ListNode l1, ListNode l2){
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
ListNode dummy = new ListNode();
ListNode p = dummy;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
p.next = l1;
l1 = l1.next;
}else {
p.next = l2;
l2 = l2.next;
}
p = p.next;
}
while(l1 != null){
p.next = l1;
p = p.next;
l1 = l1.next;
}
while(l2 != null){
p.next = l2;
p = p.next;
l2 = l2.next;
}
return dummy.next;
}
}
方法2:使用二分将问题的规模进一步的缩小,因为题目说了每个链表都已经按照升序进行了排列。
代码:
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
int n = lists.length;
if(n == 0){
return null;
}
return recursion(lists, 0, n - 1);
}
public ListNode recursion(ListNode[] lists, int left, int right){
//注意要有递归的跳出边界
if(left == right){
return lists[left];
}
int mid = left + ((right - left) >> 1);
ListNode leftList = recursion(lists, left, mid);
ListNode rightList = recursion(lists, mid + 1, right);
return mergeTwoLists(leftList, rightList);
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2){
ListNode dummy = new ListNode();
ListNode p = dummy;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
p.next = l1;
l1 = l1.next;
}else {
p.next = l2;
l2 = l2.next;
}
p = p.next;
}
if(l1 != null){
p.next = l1;
}
if(l2 != null){
p.next = l2;
}
return dummy.next;
}
}
代码
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
return true;
}
}
return false;
}
}
思路:与上一题一样,只是在最后的时候新增判断,不让指针为空,为空了就返回null;
代码
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null){
return null;
}
if(head.next == null){
if(head == head.next){
return head;
}else {
return null;
}
}
ListNode slow = head, fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
break;
}
}
fast = head;
while(slow != fast && slow != null && fast != null){
slow = slow.next;
fast = fast.next;
}
if(slow == null || fast == null){
return null;
}
return fast;
}
}
注意点:
简单阐述LRU(Least Recently Used
),在内存中,当内存满的时候,有新的记录要添加,删除的就是最长时间没有被访问的。如果某个记录被访问了,那么,将这个记录重新置顶。其余的记录依次后移。
思路:使用map+双链表
。
写一个双链表的类。
class LinkedNode{
int key;
int value;
LinkedNode pre;
LinkedNode next;
public LinkedNode(){
}
public LinkedNode(int key, int value){
this.key = key;
this.value = value;
}
}
在LRUCache
类中,要初始化一个map
,这个map
就是用来记录每次put
进去的值,并初始化一个大小size
,用来记录每次操作完成之后cache
的大小,同时初始化头尾节点,并使头尾相连。
get(int key)
的方法作用就是获取在缓存中的记录。如果缓存中存在这个记录,那么就让这个记录到第一位,并返回它的值。
put(int key, int value)
方法作用就是向缓存中放入数据。每次先从缓存中确认是否有记录。
value
值重新设置即可,并将此条记录移动到头部。cache
大小要记录。如果此时添加之后的大小比初始给定cache
的大小大的话,那么需要移除最靠近靠近尾部的记录,因为此记录已经太久没有被访问了。然后再记录此时的cache
大小。四个函数的意思:因为LRU机制,所以需要两个函数, 一个是移动至头的moveToHead(LinkedNode node)函数、 一个是删除尾结点removeTail()函数。 第一个函数需要做的是将传入的节点,先删除该节点(假设该节点存在双链表中),然后再移动添加至头结点中。 第二个函数首先需要获取尾指针前面的节点,然后删除该节点,并返回该节点。 分析就需要四个函数:1、添加至头2、删除结点3、移动至头4、删除尾结点
class LinkedNode{
int key;
int value;
LinkedNode pre;
LinkedNode next;
public LinkedNode(){
}
public LinkedNode(int key, int value){
this.key = key;
this.value = value;
}
}
class LRUCache {
private Map<Integer, LinkedNode> cache = new HashMap<>();
private int size;
private int capacity;
private LinkedNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new LinkedNode();
tail = new LinkedNode();
head.next = tail;
tail.pre = head;
}
public int get(int key) {
LinkedNode node = cache.get(key);
if(node == null){
return -1;
}
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
LinkedNode node = cache.get(key);
if(node == null){
LinkedNode newNode = new LinkedNode(key,value);
//新节点首先要放入cache中,然后再加入到链表的头
cache.put(key,newNode);
addTohead(newNode);
++size;
if(size > capacity){
LinkedNode tail = removeTail();
//从缓存中获取待删除的key,因为是用map记录的,所以使用map自带的方法remove()删除
cache.remove(tail.key);
--size;
}
}else {
node.value = value;
moveToHead(node);
}
}
private void addTohead(LinkedNode node){
node.pre = head;
node.next = head.next;
head.next.pre = node;
head.next = node;
}
private void removeNode(LinkedNode node){
node.pre.next = node.next;
node.next.pre = node.pre;
}
private void moveToHead(LinkedNode node){
removeNode(node);
addTohead(node);
}
private LinkedNode removeTail(){
LinkedNode res = tail.pre;
removeNode(res);
return res;
}
}
/**
* 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);
*/
额外四个方法:
添加到头(新节点使用)
删除结点(老结点移动的时候使用)
移动到头(老结点被重新访问,先添加到头,再删除本结点)
删除尾部(新添加的节点导致容量爆出,删除尾部,就是最久未访问的节点)
除了最后一个方法,其他的都要传入结点node。
初始化cache的时候,要有size,要有capacity,要有head,要有tail,head、tail连起来
(head-mid)
进行一次排序,(mid-tail)
进行一次排序,目的就是将整个链表不断拆分,即:递归,递归完成之后再还原,这样就能将整个链表进行合并了。本题的快慢指针是,快指针两次判断的条件都是不等于尾。
代码:
class Solution {
public ListNode sortList(ListNode head) {
return sort(head, null);
}
public ListNode sort(ListNode head, ListNode tail){
if(head == null){
return head;
}
if(head.next == tail){
head.next = null;
return head;
}
ListNode slow = head, fast = head;
while(fast != tail){
slow = slow.next;
fast = fast.next;
if(fast != tail){
fast = fast.next;
}
}
ListNode mid = slow;
ListNode list1 = sort(head,mid);
ListNode list2 = sort(mid, tail);
ListNode res = megerList(list1,list2);
return res;
}
public ListNode megerList(ListNode l1, ListNode l2){
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
ListNode dummy = new ListNode();
ListNode p = dummy;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
p.next = l1;
l1 = l1.next;
}else {
p.next = l2;
l2 = l2.next;
}
p = p.next;
}
if(l1 != null){
p.next = l1;
}
if(l2 != null){
p.next = l2;
}
return dummy.next;
}
}
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null){
return null;
}
int len1 = 0, len2 = 0;
ListNode p = headA, q = headB;
while(p != null){
len1++;
p = p.next;
}
while(q != null){
len2++;
q = q.next;
}
int dif = len1 > len2 ? len1 - len2 : len2 - len1;
ListNode longer = null, shorter = null;
if(len1 > len2){
longer = headA;
shorter = headB;
}else {
longer = headB;
shorter = headA;
}
while(dif > 0){
longer = longer.next;
dif--;
}
while(longer != null && shorter != null){
if(longer == shorter){
return longer;
}else {
longer = longer.next;
shorter =shorter.next;
}
}
return null;
}
}
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode p = head, q = head.next, h = null;
while(q != null){
h = q.next;
q.next = p;
p = q;
q = h;
}
head.next = null;
return p;
}
}
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null){
return true;
}
ListNode fast = head.next, slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
ListNode reverseHead = reverse(slow.next);
slow.next = null;
while(head != null && reverseHead != null){
if(head.val != reverseHead.val){
return false;
}else {
head = head.next;
reverseHead = reverseHead.next;
}
}
return true;
}
public ListNode reverse(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode p = head, q = head.next, h = null;
while(q != null){
h = q.next;
q.next = p;
p = q;
q = h;
}
head.next = null;
return p;
}
}
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode();
dummy.next = head;
ListNode pre = dummy, p = dummy;
while(p.next != null){
//这个for循环就是找到待翻转断开的位置。
for(int i = 0; i < k && p != null; i++){
p = p.next;
}
//如果为空,那么就说明已经工作完成
if(p == null){
break;
}
//start永远指向待翻转的头
ListNode start = pre.next;
//next永远指向翻转结束的下一个节点,保存结点信息。
ListNode next = p.next;
//从p位置断开。
p.next = null;
//进行连接
pre.next = reverse(start);
start.next = next;
//翻转完毕之后,start到了尾部,那么pre还是指向下一次开始的头部,即start
pre = start;
//p和pre永远在一起
p = pre;
}
return dummy.next;
}
public ListNode reverse(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode p = head, q = head.next, h = null;
while(q != null){
h = q.next;
q.next = p;
p = q;
q = h;
}
head.next = null;
head = p;
return head;
}
}
class Solution {
public Node copyRandomList(Node head) {
if(head == null){
return head;
}
Node p = head;
Node nextNode = null;
while(p != null){
nextNode = p.next;
Node s = new Node(p.val);
s.next = p.next;
p.next = s;
p = nextNode;
}
p = head;
Node copy = null;
while(p != null){
nextNode = p.next.next;
copy = p.next;
if(p.random != null){
copy.random = p.random.next;
}else {
copy.random = null;
}
p = nextNode;
}
//分割
Node res = head.next;
p = head;
while(p != null){
nextNode = p.next.next;
copy = p.next;
p.next = nextNode;
if(nextNode != null){
copy.next = nextNode.next;
}else {
copy.next = null;
}
p = nextNode;
}
return res;
}
}
第一步:复制水平方向的节点,保存下一个节点的信息。
第二步:水平方向复制了之后,原链表的下一个节点就是两次next,先报该节点信息进行保存。然后进行random方向的连接。
第三步:水平方向的分割。在分割时候同样要记录下一个节点以及下下个节点的位置信息。
代码的细节:
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummy = new ListNode();
dummy.next = head;
ListNode p = dummy;
ListNode start = null, end = null, startPre = null, endNext = null;
while(left - 1 > 0){
p = p.next;
left--;
}
startPre = p;
start = p.next;
p = head;
while(right - 1 > 0){
p = p.next;
right--;
}
end = p;
endNext = p.next;
startPre.next = null;
end.next = null;
ListNode reverseNode = reverse(start);
startPre.next = reverseNode;
start.next = endNext;
return dummy.next;
}
public ListNode reverse(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode p = head, q = head.next, h = null;
while(q != null){
h = q.next;
q.next = p;
p = q;
q = h;
}
head.next = null;
head = p;
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 void reorderList(ListNode head) {
if(head == null){
return;
}
ListNode mid = fingMid(head);
ListNode l2 = mid.next;
mid.next = null;
l2 = reverse(l2);
ListNode l1 = head;
mergeTwo(l1, l2);
}
public ListNode fingMid(ListNode head){
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
public ListNode reverse(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode p = head, q = head.next, h = null;
while(q != null){
h = q.next;
q.next = p;
p = q;
q = h;
}
head.next = null;
head = p;
return head;
}
public void mergeTwo(ListNode head1, ListNode head2) {
ListNode next1 = null;
ListNode next2 = null;
while (head1 != null && head2 != null) {
next1 = head1.next;
next2 = head2.next;
head1.next = head2;
head1 = next1;
head2.next = head1;
head2 = next2;
}
}
}
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode dummy = new ListNode(Integer.MIN_VALUE);
dummy.next = head;
ListNode p = dummy.next;
ListNode q = null;
while(p.next != null){
if(p.val == p.next.val){
q = p.next;
while(q != null && q.val == p.next.val){
q = q.next;
}
p.next = q;
}else {
p = p.next;
}
}
return dummy.next;
}
}
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode dummy = new ListNode();
dummy.next = head;
ListNode p = dummy;
ListNode q = null;
while(p.next != null && p.next.next != null){
if(p.next.val == p.next.next.val){
q = p.next;
while(q != null && q.val == p.next.val){
q = q.next;
}
p.next = q;
}else {
p = p.next;
}
}
return dummy.next;
}
}
两题的区别:
- 工作指针的区别,p指针的位置的区别,删除所有重复的元素的话,工作指针要指向虚拟头结点,保留一个元素的话,工作指针就指向首节点。
- 值的比较的区别:删除所有重复的元素
if(p.val == p.next.val){
q = p.next;
while(q != null && q.val == p.next.val){
q = q.next;
}
p.next = q;
}
保留一个重复元素
if(p.next.val == p.next.next.val){
q = p.next;
while(q != null && q.val == p.next.val){
q = q.next;
}
p.next = q;
}