11.BM11 链表相加(二)
拿到第一个想法,就是利用stack。因为要先算后面再算前面,但是链表并不能进行随机读取,所以就利用stack的特性,来反着算。但是这样就要用到数组,java的数组实在太麻烦了相比python中的list;
所以我想使用三次头插法。前两次分别逆转输入的两个链表(其实就是BM1中反转链表即可),第三次用头插法建立目标链表。
public class Solution {
/**
*
* @param head1 ListNode类
* @param head2 ListNode类
* @return ListNode类
*/
public ListNode reverselist(ListNode head) {
ListNode headnode = new ListNode(-1);
headnode.next = head;
ListNode cur = head;
while (cur.next != null) {
ListNode temp = cur.next;
cur.next = temp.next;
temp.next = headnode.next;
headnode.next = temp;
}
return headnode.next;
}
public ListNode addInList(ListNode head1, ListNode head2) {
// write code here
ListNode head3 = reverselist(head1);
ListNode head4 = reverselist(head2);
// System.out.println(head3.val);
ListNode head5 = new ListNode(-1);
int flag = 0;//0代表没有进位
while (head3 != null && head4 != null) {
ListNode new_node = new ListNode((head3.val + head4.val + flag) % 10);
System.out.println("当前节点值:" + (head3.val + head4.val + flag) % 10 );
new_node.next = head5.next;
head5.next = new_node;
flag = (head3.val + head4.val + flag) / 10;
head3 = head3.next;
head4 = head4.next;
}
if (head3 == null) {
while (head4 != null) {
ListNode new_node = new ListNode((head4.val + flag) % 10);
flag = (head4.val + flag) / 10;
new_node.next = head5.next;
head5.next = new_node;
head4 = head4.next;
}
} else {
while (head3 != null) {
ListNode new_node = new ListNode((head3.val + flag) % 10);
flag = (head3.val + flag) / 10;
new_node.next = head5.next;
head5.next = new_node;
head3 = head3.next;
}
}
if (flag != 0) {
ListNode new_node = new ListNode(1);
new_node.next = head5.next;
head5.next = new_node;
}
return head5.next;
}
}
此题存在一个陷阱,就是在两个链表都遍历完之后,仍然可能需要进位。比如例题这种情况。
如果只循环到长的遍历完,那么就会失去第一个节点。
所以在两者遍历完之后,还需要看flag==0?flag是设置的进位标识符,且包含了进位的大小(本题中只要是进位就只能是1)。此题如果用python中的list作为栈来存储数据,然后依据list再来创建新的链表会非常方便,主要是我目前对java中数组的便利操作还不了解。所以使用三次头插法来解决还是挺繁琐。
12.BM12 单链表的排序
第一个想法:这不就是一个冒泡排序吗。。。但是要求时间O(nlogn),所以舍弃。
第二个想法:把链表的存出来,然后Arraylist.sort()。再重新建立链表,这样的时间复杂度是O(n+nlogn) = O(nlogn)。但是不专业,那任何处理链表的值的问题都弄成数组问题了。
第三个就是参考答案给出 采用归并的思路。
归并是1分2,2分4…然后最小比较之后归并----再第二小比较归并—直到和为一个。而每一段(包括只有一个点的时候),其是有序的!所以,问题就变成了两个
1.合并有序链表
2.把一个整个链表,分为两段。其中每一段又是该问题的子问题(直到1个,天然有序)。这样有序的1个和1个归并,就变为了,有序的2个和2个归并…直到整个原来的链表。 而分隔链表可以用,快慢指针:快走2,慢走1,快走到末尾时,慢自然指向中部(因为要涉及断开,可以给慢一个前序指针pre,指向慢的前面结点)
public class Solution {
/**
*
* @param head ListNode类 the head node
* @return ListNode类
*/
public ListNode merge(ListNode head1, ListNode head2) {
if (head1 == null) {
return head2;
}
if(head2==null){
return head1;
}
ListNode p = new ListNode(-1);
ListNode head3 = p;
while (head1 != null & head2 != null) {
if (head1.val < head2.val) {
head3.next = head1;
head1 = head1.next;
head3 = head3.next;
} else {
head3.next = head2;
head2 = head2.next;
head3 = head3.next;
}
}
if (head1 != null) {
head3.next = head1;
} else {
head3.next = head2;
}
return p.next;
}
public ListNode sortInList(ListNode head) {
// write code here
if (head.next == null) {
return head;
}
ListNode left = new ListNode(-1);
left.next = head;
ListNode mid = head;
ListNode right = head;
while (right != null && right.next != null) {
left = left.next;
mid = mid.next;
right = right.next.next;
}
left.next = null;
return merge(sortInList(head), sortInList(mid));
}
}
13.BM13 判断一个链表是否为回文结构
有了前面的铺垫,该题就显得比较简单了。
但是这道题,我犯了一个错误,我直接把head逆置后,和head做比较明显错误,而且还有一个所谓判断回文,是判断值,绝对不能拿结点来比较。
public class Solution {
/**
*
* @param head ListNode类 the head
* @return bool布尔型
*/
public ListNode reverse(ListNode head) {
ListNode headnode = new ListNode(-1);
headnode.next = null;
while (head != null) {
ListNode newnode = new ListNode(head.val);
newnode.next = headnode.next;
headnode.next = newnode;
head = head.next;
}
return headnode.next;
}
public boolean isPail(ListNode head) {
// write code here
ListNode head1 = reverse(head);
while (head != null) {
if (head.val != head1.val) {
return false;
}
head = head.next;
head1 = head1.next;
continue;
}
return true;
}
}
14.BM14 链表的奇偶重排
这个要求空间复杂度O*(n),时间复杂度 O*(n),主要是空间复杂度很宽松。
弄一个计数count来判断奇偶。然后new两个头结点分别接收奇偶,遍历一次即可完成。
public ListNode oddEvenList(ListNode head) {
// write code here
if (head == null) {
return head;
}
int count = 1;
ListNode odd = new ListNode(-1);
ListNode even = new ListNode(-1);
ListNode m = odd;//奇数
ListNode n = even;
while (head != null) {
if (count % 2 == 0) {
n.next = head;
n = n.next;
} else {
m.next = head;
m = m.next;
}
count++;
head = head.next;
}
n.next = null;//!!!!一定要注意
m.next = even.next;
return odd.next;
}
注意该题有一个非常值得注意的细节。就是偶数的最后的结点并没有指向null。而是指向了奇数的最后一个结点,随着奇数的最后一个结点指向偶数的第一个结点,就会形成一个环。所以要在最后,加上代码中的那句话,把偶数的最后结点指向null。
15.BM15 删除有序链表中重复的元素-I
就是记住第一个元素,后面依次比较:
相同则直接前序的.next继承当前的.next(也就是相当于删除当前)
不同则pre应该后移到此处(更新pre)
public ListNode deleteDuplicates (ListNode head) {
// write code here
if (head == null) {
return head;
}
ListNode pre = head;
ListNode cur = head.next;
while (cur != null) {
if (cur.val == pre.val) {
pre.next = cur.next;
} else {
pre = pre.next;
}
cur = cur.next;
}
return head;
}
16.BM16 删除有序链表中重复的元素-II
上题对非空链表的第一个元素不会进行改动,但是本体却会涉及到,所以只要涉及头指针,就要使用使用头结点来便利操作。
当遇到有重复的,就把所有重复的遍历完并且,去掉。
遇到不重复的前面结点next指向该节点。
但是要判断重不重复,必须要判断至少两个结点。
public class Solution {
/**
*
* @param head ListNode类
* @return ListNode类
*/
public ListNode deleteDuplicates(ListNode head) {
// write code here
if (head == null) {
return null;
}
ListNode headnode = new ListNode(1001);//1001 保证头结点不影响后续
headnode.next = head;
ListNode cur = headnode;
while (cur.next != null && cur.next.next != null) {//因为要比较两个相邻结点,为什么不是cur呢?本体cur起的作用相当于前序结点。
// 1001 1 1 1 2 3 3
// cur 然后cur.next一直往后找排除相同,知道找到2,此时cur直接 = cur.next
if (cur.next.val == cur.next.next.val) {
int temp = cur.next.val;
while (cur.next != null && cur.next.val == temp) {
cur.next = cur.next.next;
}
} else {
cur = cur.next;
}
}
return headnode.next;
}
}