1.两数相加 力扣2
因为此题他们存储数字是链表逆序的,所以直接进行每一位相加即可。从链表头开始,每一位的值相加组成新链表的头,注意进位问题。然后l1与l2进行遍历后,再次相加组成新链表的下一位。重复过程最后返回新链表即可。
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null;
ListNode cur = null;
int carry = 0;
while (l1 != null || l2 != null) {
int n1 = l1 == null ? 0 : l1.val;
int n2 = l2 == null ? 0 : l2.val;
int sum = n1 + n2 + carry;
if (head == null) {
head = cur = new ListNode(sum % 10);
} else {
cur.next = new ListNode(sum % 10);
cur = cur.next;
}
carry = sum / 10;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
if (carry > 0) {
cur.next = new ListNode(carry);
}
return head;
}
}
2.删除链表的倒数第n个节点
此题。第一步我们要求链表长度,用一个while循环。第二,我们要正向的找到要删除的节点的上一个节点,然后删除他的下一个节点。最后返回链表即可。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
int a = 0;
ListNode dummy = new ListNode(0,head);
ListNode cur = dummy;
dummy.next = head;
while (head != null) {
a++;
head = head.next;
}
for (int i = 0;i < a - n;i++) {
cur = cur.next;
}
cur.next = cur.next.next;
return dummy.next;
}
}
3.合并两个有序链表 21
此题用递归即可。确定头节点递归后返回即可。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null || l2 == null) {
return l1 == null ? l2 : l1;
} else if (l1 == null && l2 == null) {
return null;
} else {
if (l1.val >= l2.val) {
l2.next = mergeTwoLists(l1,l2.next);
return l2;
} else {
l1.next = mergeTwoLists(l1.next,l2);
return l1;
}
}
}
}
4.两两交换链表中的节点 24
首先,我们要设新节点。用来表示交换的两个节点和他们之后的节点。然后第二个节点->第一个节点->他们之后的节点。然后再将指针节点往后移两位即可。重复步骤最后返回头节点。
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(0,head);
dummy.next = head;
ListNode cur = dummy;
while (cur.next != null && cur.next.next != null) {
ListNode temp1 = cur.next;
ListNode temp2 = cur.next.next;
ListNode temp = temp2.next;
cur.next = temp2;
temp2.next = temp1;
temp1.next = temp;
cur = cur.next.next;
}
return dummy.next;
}
}
5.旋转链表 61
旋转链表的题,要把链表成环,这样简单。首先遍历找到最后一个节点,把它和头节点相连成环。然后根据k找到新头节点,断开,返回新头节点即可。
此题两个优化,首先,头节点为空,或者只有头节点或者k为0时,不能旋转,直接返回即可。
第二,当这个给定的k值为倍数时,也等于没旋转,返回头节点即可。
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(head == null || head.next == null || k == 0) {
return head;
}
int n = 1;
ListNode cur = head;
while (cur.next != null) {
cur = cur.next;
n++;
}
int a = n - k % n;
if (a == n) return head;
cur.next = head;
while (a > 0) {
a = a - 1;
cur = cur.next;
}
ListNode node = cur.next;
cur.next = null;
return node;
}
}
6.删除排序链表中的重复元素二。82
此题要求是删除所有重复的元素,删光,所以我们找出了要删的元素,直接利用while循环一直删除即可。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null) return head;
ListNode dummy = new ListNode(0,head);
dummy.next = head;
ListNode cur = dummy;
while (cur.next != null && cur.next.next != null) {
if (cur.next.val != cur.next.next.val) {
cur = cur.next;
} else {
int k = cur.next.val;
//一直删前面的,删光
while (cur.next != null && cur.next.val == k) {
cur.next = cur.next.next;
}
}
}
return dummy.next;
}
}
7.删除排序链表中的元素一 83
此题不用全部删除,所以不用第二层while循环了,因为if的判断条件时判断cur的后两个节点,所以直接删除即可。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null) return head;
ListNode dummy = new ListNode(0,head);
dummy.next = head;
ListNode cur = dummy;
while (cur.next != null && cur.next.next != null) {
if (cur.next.val != cur.next.next.val) {
cur = cur.next;
} else {
cur.next = cur.next.next;
}
}
return dummy.next;
}
}
8.分割链表 86
创建两个新的链表,一个存小的,一个存大的,最后连上就行
class Solution {
public ListNode partition(ListNode head, int x) {
ListNode dummy1 = new ListNode(0,head);
ListNode dummy2 = new ListNode(0,head);
ListNode cur1 = dummy1;
ListNode cur2 = dummy2;
while (head != null) {
if (head.val < x) {
cur1.next = head;
cur1 = cur1.next;
} else {
cur2.next = head;
cur2 = cur2.next;
}
head = head.next;
}
cur2.next = null;
cur1.next = dummy2.next;
return dummy1.next;
}
}
9.反转链表一 206
首先,设置一个新pre头节点,把它设为null,然后让头指点指向空,让pre赋值到头节点。然后while循环继续往下遍历重复步骤,最后pre应该到了最后一个节点,此时链表已经反转,并且pre是头节点。
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode cur = head;
ListNode pre = null;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
10.反转链表二 92
根据上一道题的反转列表做法,先设置一个返回新链表的类,然后我们需要找到四个点,分别是,要翻转列表的左右端点,和他们端点的旁边两个节点。通过遍历我们找到了这四个点,然后翻转之后,再把她们重新连接,就实现了反转一部分链表。
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummy = new ListNode(0,head);
dummy.next = head;
ListNode cur = dummy;
for (int i = 0;i < left - 1;i++) {
cur = cur.next;
}
ListNode small = cur.next;
ListNode l = cur;
cur = cur.next;
l.next = null;
for (int i = 0;i < right - left;i++) {
cur = cur.next;
}
ListNode large = cur;
ListNode r = cur.next;
large.next = null;
reverse(small);
l.next = large;
small.next = r;
return dummy.next;
}
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;
}
}
11.相交链表 160
这道题要找出重合的,首先想到的是找重合set集合。于是第一次写了set的方法,只超过25%,估计是用了辅助空间,用空间换时间。
于是换了种想法,用双指针来实现。一个指针从a链表开始走,一个从b链表开始走,两个指针都让他们走完a+b的全程链表。如果他们相交,那么一定会返回。因为,他们走的全程一样,而且他们最后相交的部分全程也一样,换句话说,如果她们重合,他们在重合之前走的路一定是长度相等的。所以,一定能相交返回那个值。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode p1 = headA,p2 = headB;
while (p1 != p2) {
p1 = p1 == null ? headB : p1.next;
p2 = p2 == null ? headA : p2.next;
}
return p1;
}
}
12 移除链表元素 203
就是删除节点,遍历删除即可。
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy = new ListNode(0,head);
dummy.next = head;
ListNode cur = dummy;
while (cur.next != null) {
if (cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return dummy.next;
}
}
13.回文链表 234
此题要考虑复杂度的问题,我觉得用快慢指针较好,用快慢指针来判断链表的中点,然后断开分成两个链表,把后面的反转。反转之后,再利用遍历两个链表来判断回文。
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null || head.next == null) {
return true;
}
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode head1 = slow.next;
slow.next = null;
ListNode pre = reverse(head1);
while (pre != null) {
if (head.val != pre.val) {
return false;
}
head = head.next;
pre = pre.next;
}
return true;
}
public ListNode reverse(ListNode head) {
ListNode cur = head;
ListNode pre = null;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
14.删除链表中的节点 237
不能访问头节点,那就把他下一个节点值赋给他,删他下一个节点就ok了
class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
15.环形链表1 141
此题看到排重,第一想到的肯定是set集合。但是用了辅助数组,第二想的是否能用快慢指针降低空间复杂度,只要快的节点能赶上慢的,就一定环。为什么他们一定能在环中相遇呢,因为快的走两步,慢的走一步,我的想法是,快的-慢的=1,可以理解为快的以一步靠近慢的。所以一定能遇到。
注意,这种龟兔赛跑问题,快指针一定要放在慢指针的前面。因为如果她们都放在head,那么while循环是不会运行的。直接就会返回true。
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head.next;
while (fast != slow) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
16.环形链表二 142
相交一定是环与链表的焦点,设相交之前长度为a,在环内走了b相交,环剩下的为c,得出:
a+(n+1)b+nc=2(a+b)⟹a=c+(n−1)(b+c)
证明 走了a就等于走了无论绕多少圈,最后加c也就是链表与环的交点
17.有序链表转换为二叉搜索树 109
先设置一个类来寻找链表的重点,因为链表是有序的,天然的二叉搜索树。然后找到中点建立根节点递归左右子树即可。
class Solution {
public TreeNode sortedListToBST(ListNode head) {
return build(head,null);
}
public TreeNode build(ListNode left,ListNode right) {
if (left == right) return null;
ListNode mid = findmid(left,right);
TreeNode root = new TreeNode(mid.val);
root.left = build(left,mid);
root.right = build(mid.next,right);
return root;
}
public ListNode findmid(ListNode left,ListNode right) {
ListNode slow = left;
ListNode fast = left;
while (fast != right && fast.next != right) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
18 将二叉树展开为链表 114
morrirs遍历,将一个节点,他的右子树安装到左子树的最右边节点,然后把新的左子树安装到右子树的位置上,左子树设置为空,然后节点向右遍历,重复以上步骤,最后得到了单支只有右子树的二叉树,就是展开的链表。
class Solution {
public void flatten(TreeNode root) {
while (root != null) {
if (root.left != null) {
TreeNode pre = root.left;
while (pre.right != null) {
pre = pre.right;
}
pre.right = root.right;
root.right = root.left;
root.left = null;
}
root = root.right;
}
}
}
19.约瑟夫问题
41个人围城一个环,从一开始往后,3次报数死一回,直到死到最后只剩一个人,这个人的初始位置应该是31,代码如下
public class josephquestion { private static class Node<T> { T item; Node next; public Node(T item, Node next) { this.item = item; this.next = next; } } public static void main(String[] args) { //解决约瑟夫问题 Node<Integer> head = null; Node<Integer> pre = null; //1。构造循环链表 for (int i = 1; i <= 41; i++) { //设置首节点 if (i == 1) { head = new Node<>(i, null); pre = head; continue;//立即终止后面的语句进入下一层循环 } Node<Integer> newnode = new Node<>(i, null); pre.next = newnode; pre = newnode; if (i == 41) { pre.next = head; } } //2 计数器count,模拟报数 int count = 0; Node<Integer> n = head; Node<Integer> before = null; while (n.next != n) { count++; if (count == 3) { before.next = n.next; System.out.println(n.item + ","); count = 0; n = n.next; } else { before = n; n = n.next; } } System.out.println(n.item); } }
测试后最后三位输出