13.1 数据结构的介绍
链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一个节点的指针,因此很多链表问题可以用递归来处理。不同于数组,链表并不能直接获取任意节点的值,必须要通过指针找到该节点后才能获取其值。同理,在未遍历到链表结尾时,我们也无法知道链表的长度,除非依赖其他数据结构储存长度。
由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或指针出现问题。有两个小技巧可以解决这个问题:**一是尽量处理当前节点的下一个节点而非当前节点本身,二是建立一个虚拟节点 (dummy node),使其指向当前链表的头节点,**这样即使原链表所有节点全被删除,也会有一个 dummy 存在,返回 dummy->next 即可。
13.2 链表的基本操作
题目代号: 206 反转链表
题目描述:
翻转一个链表
测试用例:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
我的分析:
1、把当前指针的后面扔给中转站
2、当前指针指向头节点的下一个节点
3、头节点指向当前指针节点
4、指针后移
代码:
public ListNode reverseList(ListNode head) {
ListNode HummyHead = new ListNode(0);
ListNode temp = head;//指针指向头节点
while (temp != null) {
ListNode zhongzhuan = temp.next;//把当前指针的下一个节点给中转站
temp.next = HummyHead.next;//当前节点指向头节点的下一个节点
HummyHead.next = temp;//哑节点指向当前节点
temp = zhongzhuan;//指针指向中转站
}
return HummyHead.next;
}
题目代号: 21 合并两个有序链表
题目描述:
给定两个增序的链表,试将其合并成一个增序的链表
测试用例:
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4
我的分析:
这思路很容易理解,就新建一个带哑节点的链表,并让temp指针指向它
两个指针依次向后移动,看谁小,就把谁挂到temp上,temp向后移动一位
出了这个while循环,那就只剩一个链表了,那就把这个链表挂到temp上即可
代码:
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode zong = new ListNode(0),temp = zong;//指向总链表的指针
while (l1 != null && l2 != null){//出了这个while循环,那就肯定有个链表结束了,还剩另一条链表
if(l1.val <= l2.val){
temp.next = l1;
l1 = l1.next;
}else {
temp.next = l2;
l2 = l2.next;
}
temp = temp.next;//总链表的指针向后移动
}
// if(l1 != null){
// temp.next = l1;
// }else{
// temp.next = l2;
// }
temp.next = l1 != null? l1:l2;//temp指针指向剩下的链表
return zong.next;
}
题目代号: 24 两两交换链表中的节点
题目描述:
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
测试用例:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
我的分析:
temp相当于总节点
1、2两个步骤相当于把这个链表扯开了
真的很有讲究,记住这个指向的顺序
代码:
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0, head);
ListNode temp = dummyHead;//搞个哑节点,然后指向它
while (temp.next != null && temp.next.next != null) {
ListNode node1 = temp.next;
ListNode node2 = temp.next.next;//其实是可以把指针里的数据放到新定义的节点里的
temp.next = node2;//准确的说只有当等号前的的代号有next的时候,就是指向,而如果没有next的话,就是赋值
node1.next = node2.next;
node2.next = node1;
temp = temp.next.next;
}
return dummyHead.next;
}
题目代号: 160 相交链表
题目描述:
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
测试用例:
我的分析:
假设链表 A 的头节点到相交点的距离是 a,链表 B 的头节点到相交点的距离是 b,相交点到链表终点的距离为 c。我们使用两个指针,分别指向两个链表的头节点,并以相同的速度前进,若到达链表结尾,则移动到另一条链表的头节点继续前进。按照这种前进方法,两个指针会在a + b + c 次前进后同时到达相交节点。
指针temp1移动走向:a+c+b+c
指针temp2移动走向:b+c+a+c
代码:
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode temp1 = headA;
ListNode temp2 = headB;
while (temp1 != temp2){//当不相等的时候,就一直向后移动
temp1 = temp1 == null ? headB:temp1.next;
temp2 = temp2 == null ? headA:temp2.next;
// if(temp1 == null){
// temp1 = headB;//temp1移动完了headA,那就去headB开始移动
// }else{
// temp1 = temp1.next;//没移动完就接着移动
// }
// if(temp2 == null){
// temp2 = headA;
// }else{
// temp2 = temp2.next;
// }
}
return temp2;//你就说说这个方法有多牛逼,temp1遍历完headA就去遍历headB
//temp2遍历完headB就去遍历headA,所以最后几位数肯定会相遇的,相遇就跳出循环,return那位数,不相遇就一直到最后,return null
}
题目代号: 234 回文链表
题目描述:
请判断一个链表是否为回文链表。
测试用例:
Input: 1->2->3->2->1
Output: true
我的分析:
这方法真的绝了,直接放进一个数组中,然后左右遍历,看不相同就直接返回false,全部相同就返回true
代码:
public boolean isPalindrome(ListNode head) {
List<Integer> list = new ArrayList<Integer>();
// 将链表的值复制到数组中
ListNode temp = head;
while (temp != null) {
list.add(temp.val);
temp = temp.next;
}
// 使用双指针判断是否回文
int left = 0;
int right = list.size() - 1;
while (front < right) {
if (!list.get(left).equals(list.get(right))) {
return false;
}
left++;
right--;
}
return true;
}