7-图文打造LeeCode算法宝典-链表和树算法题解

本文深入探讨了链表的两种常见操作:反转链表和合并链表。提供了递归和非递归两种方法实现链表反转,并详细解释了每种方法的思路和步骤。此外,还介绍了如何合并多个已排序的链表,通过分治策略实现高效合并。这些算法在理解和实现上都具有挑战性,对于提升编程技巧非常有帮助。
摘要由CSDN通过智能技术生成

链表和树

反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

思路一:递归地反转链表

递归的终止条件

(1)head为null,直接返回head。

(2)head.next为null,直接返回head。

递归的过程

我们新的头节点记为newHead,其值应该是翻转以head.next为头节点的链表的结果。同时把head放在head.next的后面,并令head.next为null,这样我们就把head元素放在了新链表的末尾。

由于涉及到递归,而每一次递归的时间复杂度都是O(1)级别的,因此时间复杂度和空间复杂度都是O(n)级别的,其中n为链表中的节点个数。

代码实现1

public class Solution {
	
	public ListNode reverseList(ListNode head) {
		if(head == null || head.next == null) {
			return head;
		}
		ListNode newHead = reverseList(head.next);
		head.next.next = head;
		head.next = null;
		return newHead;
	}
}

思路二:非递归地反转链表

链表相关的题,多设指针,在草稿纸上多演练几次,一定能够轻松解决。

设立虚拟头节点dummyHead和三个指针cur1、cur2、cur3反转链表。

令cur1指向虚拟头节点dummyHead。

如果cur1.next是一个空节点或者说cur1.next.next是一个空节点,即链表中没有节点或链表中只有一个节点,无需翻转,直接返回head即可。

令cur2指向cur1.next,cur3指向cur2.next。在我的定义中,cur2指向的是待处理节点的前一个节点,cur3指向的是待处理节点。只要cur3不为null,就进行以下循环。

(1)令cur2.next指向cur3.next。

(2)设立临时变量temp存储cur1.next结点。令cur1.next节点指向cur3,即将待处理节点放在第一个节点的位置,而令cur3.next为temp。

(3)更新cur3的值为cur2.next。

最后我们返回翻转后的结果dummyHead.next即可。

时间复杂度是O(n)级别的,其中n为链表中的节点数。空间复杂度是O(1)级别的。

代码实现2

public class Solution {
	
	public ListNode reverseList(ListNode head) {
		ListNode dummyHead = new ListNode(-1);
		dummyHead.next = head;
		ListNode cur1 = dummyHead;
		if(cur1.next == null || cur1.next.next == null) {
			return head;
		}
		ListNode cur2 = cur1.next;
		ListNode cur3 = cur2.next;
		while(cur3 != null) {
			cur2.next = cur3.next;
			ListNode temp = cur1.next;
			cur1.next = cur3;
			cur3.next = temp;
			cur3 = cur2.next;
		}
		return dummyHead.next;
	}
}
两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
代码实现
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode root = new ListNode(0);
        ListNode cursor = root;
        int carry = 0;
        while(l1 != null || l2 != null || carry != 0) {
            int l1Val = l1 != null ? l1.val : 0;
            int l2Val = l2 != null ? l2.val : 0;
            int sumVal = l1Val + l2Val + carry;
            carry = sumVal / 10;
            
            ListNode sumNode = new ListNode(sumVal % 10);
            cursor.next = sumNode;
            cursor = sumNode;
            
            if(l1 != null) l1 = l1.next;
            if(l2 != null) l2 = l2.next;
        }
        
        return root.next;
    }
}
合并K个排序链表

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6
解题思路

使用分治思想;
1、将过程分为:分治,合并两个链表。

代码代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
    	int L = 0, R = lists.length-1;
		if(R == 0) return lists[R];
        return soluted(lists,L,R);
    }
	public ListNode soluted(ListNode[] list, int L, int R) {
		if(R < L) return null;
		if(L == R) return list[L];
		if(L == R - 1) return dbListMerge(list[L], list[R]);
		return dbListMerge(soluted(list, L, (L+R)/2) , soluted(list, (L+R)/2+1, R));
	}
	public ListNode dbListMerge(ListNode l1,ListNode l2) {
		ListNode ans = new ListNode(0);
		if(l1 == null) return l2;
		if(l2 == null) return l1;
		ListNode cursor = ans;
		while(l1 != null && l2 != null) {
			int v1 = l1.val;
			int v2 = l2.val;
			if(v1 > v2) {
				cursor.next = l2;
				l2 = l2.next;
			}else {
				cursor.next = l1;
				l1 = l1.next;
			}
			cursor = cursor.next;
		}
		if(l1 == null)cursor.next = l2;
		if(l2 == null)cursor.next = l1;
		return ans.next;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xianghan收藏册

极简精品作,一分也是一份鼓励哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值