作者简介:大家好,我是未央;
博客首页:未央.303
系列专栏:牛客面试必刷TOP101
每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!!!
前言
一、BM11 链表相加(二)
题目描述
描述:
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
数据范围:0≤n,m≤1000000,链表任意值 0≤val≤9
要求:空间复杂度 O(n),时间复杂度 O(n)
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
示例1:
示例2:
题目解析
解题思路:
方法:反转链表法(推荐使用)
思路:
既然链表每个节点表示数字的每一位,那相加的时候自然可以按照加法法则,从后往前依次相加。但是,链表是没有办法逆序访问的,这是我们要面对第一只拦路虎。解决它也很简单,既然从后往前不行,那从前往后总是可行的吧,将两个链表反转一下:
while(cur != null){ //断开链表,要记录后续一个 ListNode temp = cur.next; //当前的next指向前一个 cur.next = pre; //前一个更新为当前 pre = cur; //当前更新为刚刚记录的后一个 cur = temp; }
即可得到个十百千……各个数字从前往后的排列,相加结果也是个位在前,怎么办?再次反转,结果不就正常了。
解题步骤:
- step 1:特殊情况。任意一个链表为空,返回另一个链表就行了,因为链表为空相当于0,0加任何数为0,包括另一个加数为0的情况。
- step 2:相继反转两个待相加的链表,反转过程可以参考BM1反转链表。
- step 3:设置返回链表的链表头,设置进位carry=0.
- step 4:从头开始遍历两个链表,直到两个链表节点都为空且carry也不为1. 每次取出不为空的链表节点值,为空就设置为0,将两个数字与carry相加,然后查看是否进位,将进位后的结果(对10取模)加入新的链表节点,连接在返回链表后面,并继续往后遍历。
- step 5:返回前将结果链表再反转回来。
图示说明:
代码编写:
import java.util.*; public class Solution { //建立一个ReverseList()方法函数用来反转链表操作。 //反转链表 public ListNode ReverseList(ListNode pHead) { if (pHead == null) return null; ListNode cur = pHead; ListNode pre = null; while (cur != null) { //断开链表,要记录后续一个 ListNode temp = cur.next; //当前的next指向前一个 cur.next = pre; //前一个更新为当前 pre = cur; //当前更新为刚刚记录的后一个 cur = temp; } return pre; } public ListNode addInList (ListNode head1, ListNode head2) { //step 1:特殊情况。任意一个链表为空,返回另一个链表就行了,因为链表为空相当于0,0加任何数为0,包括另一个加数为0的情况。 if (head1 == null) return head2; if (head2 == null) return head1; //step 2:相继反转两个待相加的链表,反转过程可以参考BM1反转链表。 head1 = ReverseList(head1); head2 = ReverseList(head2); //step 3:设置返回链表的链表头,设置进位carry=0. //添加表头 ListNode res = new ListNode(-1); ListNode head = res; //进位符号 int carry = 0; //step 4:从头开始遍历两个链表,直到两个链表节点都为空且carry也不为1. 每次取出不为空的链表节点值,为空就设置为0,将两个数字与carry相加,然后查看是否进位,将进位后的结果(对10取模)加入新的链表节点,连接在返回链表后面,并继续往后遍历。 //只要某个链表还有或者进位还有 while (head1 != null || head2 != null || carry != 0) { //链表不为空则取其值 int val1 = head1 == null ? 0 : head1.val; int val2 = head2 == null ? 0 : head2.val; //相加 int temp = val1 + val2 + carry; //获取进位 carry = temp / 10; temp %= 10; //添加元素 head.next = new ListNode(temp); head = head.next; //移动下一个 if (head1 != null) head1 = head1.next; if (head2 != null) head2 = head2.next; } //step 5:返回前将结果链表再反转回来。 return ReverseList(res.next); } }
二、BM12 单链表的排序
题目描述
描述:
给定一个节点数为n的无序单链表,对其按升序排序。
数据范围:0<n≤100000,保证节点权值在[−109,109]之内。
要求:空间复杂度 O(n),时间复杂度O(nlogn)
示例1:
示例2:
题目解析
解题思路:
方法:转化为数组排序(扩展思路)
思路:
链表最难受的就是不能按照下标访问,只能逐个遍历,那像排序中常规的快速排序、堆排序都不能用了,只能用依次遍历的冒泡排序、选择排序这些。但是这些O(n2)复杂度的排序方法太费时间了,我们可以将其转化成数组后再排序。
解题步骤:
- step 1:遍历链表,将节点值加入数组。
- step 2:使用内置的排序函数对数组进行排序。
- step 3:依次遍历数组和链表,按照位置将链表中的节点值修改为排序后的数组值。
代码编写:
import java.util.*; public class Solution { public ListNode sortInList (ListNode head) { ArrayList<Integer> nums = new ArrayList(); ListNode p = head; //step 1:遍历链表,将节点值加入数组。 while (p != null) { nums.add(p.val); p = p.next; } p = head; //step 2:使用内置的排序函数对数组进行排序。 Collections.sort(nums); //step 3:依次遍历数组和链表,按照位置将链表中的节点值修改为排序后的数组值。 for (int i = 0; i < nums.size(); i++) { //将数组元素依次加入链表 p.val = nums.get(i); p = p.next; } return head; } }