@[TOP](leetcode 链表)
leetcode160 链表相交(easy)
题目:
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在节点 c1 开始相交。
坑:找相同内存地址的交点(同时相遇)而不是相同数字的节点
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
/**
定义两个指针, 第一轮让两个到达末尾的节点指向另一个链表的头部, 最后如果相遇则为交点(在第一轮移动中恰好抹除了长度差)
两个指针等于移动了相同的距离, 有交点就返回, 无交点就是各走了两条指针的长度
**/
if(headA == null || headB == null) return null;
ListNode pA = headA, pB = headB;
// 在这里第一轮体现在pA和pB第一次到达尾部会移向另一链表的表头, 而第二轮体现在如果pA或pB相交就返回交点, 不相交最后就是null==null
while(pA != pB) {
pA = (pA == null) ? headB : pA.next;
pB = (pB == null) ? headA : pB.next;
}
return pA;
}
}
leetcode206 反转链表(easy)
题目:反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
方法一:迭代
第一次循环:
第二次循环:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null; //指向当前节点的前一个节点
ListNode cur = head; //当前节点
ListNode temp = null; //指向当前节点的下一个节点
while(cur != null){
temp = cur.next; //保存当前节点的下一个节点
cur.next = pre; //将当前节点指向pre
pre = cur; //将pre和cur都后移一位
cur = temp;
}
return pre; //最后的一个节点就是pre
}
}
方法二:头插法
class Solution{
public ListNode reverseList(ListNode head){
ListNode newHead = new ListNode(-1);
ListNode next = null;
while(head!=null){
next = head.next; //保存后面的节点
head.next = newHead.next;
newHead.next = head;//插入到新头节点后面
head = next;//节点后移一位
}
return newHead.next;
}
}
方法三:递归
// 类似第一种迭代的方法
class Solution {
ListNode pre = null, tmp = null;
public ListNode reverseList(ListNode head) {
if (head == null)
return pre;
tmp = head.next;
head.next = pre;
pre = head; //pre和head后移一位
head = tmp;
return reverseList(head);
}
}
leetcode21 合并两个有序列表
方法一:递归
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
if (l2 == null) {
return l1;
}
if (l1.val <= l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
方法二:迭代(尾插法)
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode newHead = new ListNode(-1);
ListNode tail = newHead; //由于头节点不能动,所以加一个指示指针
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
tail.next = l1;
l1 = l1.next;
} else {
tail.next = l2;
l2 = l2.next;
}
tail = tail.next;
tail.next = l1 == null? l2: l1; //加入最后一个节点
}
return newHead.next;
}
}
leetcode83 删除排序链表中的重复元素
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.
方法一:迭代
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode temp = head;
while( temp.next!= null){
if(temp.next.val == temp.val){//需值相同,若不加val还有内存地址是不同的
temp.next = temp.next.next;
}else{
temp = temp.next;
}
}
return head;
}
}
方法二:递归
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null)
return head;
head.next = deleteDuplicates(head.next);
return head.val == head.next.val ? head.next : head;
}
leetcode19 删除链表的倒数第N个节点
Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
双指针:快慢指针
- fast指针先移动n步
- slow和fast同时移动,二者间隔n,当fast移到最后一个节点时,slow即倒数第n个节点
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fast = head;
while (n-- > 0) { //相当于for循环,循环n次for(;n>0;n--)
fast = fast.next;
}
if (fast == null) return head.next; //删除头节点
ListNode slow = head;
while (fast.next != null) { //不包括删除第二个节点(head.next)
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next; //删除slow后面的节点
return head;
}
leetcode24 两两交换链表中的节点(Medium)
Given 1->2->3->4, you should return the list as 2->1->4->3.
题目要求:不能修改结点的 val 值,O(1) 空间复杂度。
方法一:递归
- 找终止条件:当递归到链表为空或者链表只剩一个元素的时候,没得交换了,终止。
- 找返回值:返回给上一层递归的值应该是已经交换完成后的子链表。
- 单次的过程:因为递归是重复做一样的事情,所以只用考虑某一步是怎么完成的。假设待交换的俩节点分别为head和next,就相当于是一个含三个节点的链表交换前两个节点。
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode next = head.next; //保存头节点的下一个节点,使两两相连
head.next = swapPairs(next.next); //递归内层的头节点放到外层的头节点后面
next.next = head; //把next放到head前面
return next; //返回每个子链的头节点
}
}
方法二:迭代
//此处使用3个指针 pre,l1,l2
public ListNode swapPairs(ListNode head) {
ListNode newHead = new ListNode(-1);
newHead.next = head;
ListNode pre = newHead;
while (pre.next != null && pre.next.next != null) {
ListNode l1 = pre.next;
ListNode l2 = pre.next.next;
l1.next = l2.next;
l2.next = l1;
pre.next = l2;
pre = l1;
}
return newHead.next;
}
leetcode445 链表求和(Medium)
Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7
7243+564 = 7807
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
while (l1 != null) {
stack1.push(l1.val);
l1 = l1.next;
}
while (l2 != null) {
stack2.push(l2.val);
l2 = l2.next;
}
int carry = 0; //进位标志
ListNode head = null;
while (!stack1.isEmpty() || !stack2.isEmpty() || carry > 0) {
//carry>0很重要,防止例如5+5=10时输出为0
int sum = carry;
sum += stack1.isEmpty()? 0: stack1.pop();
sum += stack2.isEmpty()? 0: stack2.pop();
ListNode node = new ListNode(sum % 10);
node.next = head; //头插法
head = node;
carry = sum / 10;
}
return head;
}
}
leetcode234 回文链表(easy)
Example 1: Input: 1->2 Output: false
Example 2: Input: 1->2->2->1 Output: true
以 O(1) 的空间复杂度来求解
切成两半,把后半段反转,然后比较两半是否相等。
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null){
return true;
}
ListNode fast = head.next; //快慢指针
ListNode slow = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
ListNode cutHead = cut(head,slow.next);
return isEqual(head,reverse(cutHead));
}
private ListNode cut(ListNode head,ListNode cutHead){
while(head.next != cutHead){
head = head.next;
}
head.next = null;
return cutHead;
}
private ListNode reverse(ListNode head){ //头插法
while(head == null || head.next == null){
return head;
}
ListNode newHead = null;
ListNode nextNode = null;
while(head!=null){
nextNode = head.next;
head.next = newHead;
newHead = head;
head = nextNode;
}
return newHead;
}
private boolean isEqual(ListNode l1,ListNode l2){
while(l1 != null && l2 != null){
if(l1.val != l2.val){
return false;
}else{
l1 = l1.next;
l2 = l2.next;
}
}
return true;
}
}
leetcode725 分隔链表(Medium)
Input: root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
输入被分成了k个连续的部分,并且每部分的长度相差不超过1.前面部分的长度大于等于后面部分的长度。
Example 1:
Input: root = [1, 2, 3], k = 5 Output: [[1],[2],[3],[],[]]
输入输出各部分都应该是链表,而不是数组。
Example 2: 输入的结点 root 的 val= 1, root.next.val = 2, \root.next.next.val = 3, 且 root.next.next.next = null。
第一个输出 output[0] 是 output[0].val = 1, output[0].next = null。
最后一个元素 output[4] 为 null, 它代表了最后一个部分为空链表。
思路:
- 遍历链表获取长度 length
- length 除以 k 得到每段链表的平均长度 aveLength 和 余数 remainder,remainder 的值就是有多少个长度为 (aveLength + 1) 的子链表排在前面。
举个例子帮助理解一下 11 / 3 = 3 余 2: 一共有3段,每段平均3个节点,但是剩下了2个节点,剩下的2个节点不能丢啊,得全部塞到子链表里面去。根据题意长的链表排前面,短的链表排后面,所以只有前面的两个子链表一人分担一个多余的节点,如此一来便形成了 4 4 3 的结构。 - 接下来按照每个子链表应该的长度[4, 4, 3]去截断给定的链表。
public ListNode[] splitListToParts(ListNode root, int k) {
int length = 0;
ListNode cur = root;
while(cur != null){
length++;
cur = cur.next;
}
int avgSize = length / k; //平均长度
int mod = length % k; //余数
ListNode[] res = new ListNode[k];
cur = root;
for(int i = 0 ; cur != null && i < k ; i++){
res[i] = cur;
int size = avgSize + (mod-- >0 ? 1:0);
for(int j = 0 ; j < size-1;j++){
cur = cur.next;
}
ListNode next = cur.next; //保存每个子链表的头节点
cur.next = null; //分隔
cur = next;
}
return res;
}
leetcode328 链表元素按奇偶聚集(Medium)
Input: 2->1->3->5->6->4->7->NULL
Output: 2->3->6->7->1->5->4->NULL
应当保持奇数节点和偶数节点的相对顺序。
链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。
思路:将奇节点放在一个链表里,偶链表放在另一个链表里。然后把偶链表接在奇链表的尾部
public class Solution {
public ListNode oddEvenList(ListNode head) {
if (head == null) return null;
ListNode odd = head, even = head.next, evenHead = even;
while (even != null && even.next != null) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = evenHead;
return head;
}
}